Wednesday, 14 November 2012

PowerShell - Refresh content on all DP's assigned for a package.

Whilst debugging a large SCCM software updates maintenance script I needed to determine how to refresh the content on all assigned distribution points after an update had been removed from a package. The below code will trigger a distribution point refresh on all assigned DP's immediately. When I get time I will look to turn this command in to a module for easier reuse and piping.

$(Get-WmiObject -ComputerName SCCMServer -Namespace root\sms\site_abc -query "Select * from SMS_SoftwareUpdatesPackage where PackageID = 'xxx00088'").RefreshPkgSource()

Thursday, 8 November 2012

PowerShell - Get-SCCMSoftwareUpdateCompliance 2007 Module

Within my role I am forever having to troubleshoot software updates compliance issues on remote hosts and so needed an easier means to getting a quick summary of the remote workstation or server state. I cannot take credit for all the code and if I later find the source I will add credit for them. The original code was in script format so I turned it into an advanced function\module and then added more of my own secret sauce to display further useful information about the machine and missing non-compliant updates to help with diagnosing simple issues. 

I have become a big advocate of advanced functions and modules, especially when custom modules are saved in the location below to enable use by anyone logged on locally and when paired with PowerShell V3  module auto loading life gets much much easier.  


function Get-SCCMSoftwareUpdateCompliance {

Checks a ConfigMgr client for required Software Update compliance.

Checks a ConfigMgr client for required Software Update compliance, if it is found non-compliant for any assigned security updates the list of updates is returned.

.PARAMETER  Computer
The server name to target.

PS C:\> Get-SCCMSoftwareUpdateCompliance -ComputerName "Foo"


[Parameter(Position=1, Mandatory=$true, HelpMessage="Computer Name", ValueFromPipelineByPropertyName=$True, ValueFromPipeline=$true)]
        [STRING[]] $ComputerName


    foreach ($computer in $ComputerName) {

        if(Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet){
                Write-Host "Checking Software Updates Compliance on [$Computer]" -BackgroundColor Blue
                        $SiteCode = $([WmiClass]"\\$computer\ROOT\ccm:SMS_Client").getassignedsite()
                        $SiteCode = "No Site Code Assigned"
                $ScanHistory = Get-WmiObject -Computer $Computer -Namespace root\ccm\scanagent -Class CCM_scantoolhistory -ErrorAction Stop
                $AgentGUID = Get-WmiObject -Computer $Computer -Namespace root\ccm -Class CCM_Client -ErrorAction Stop
                $ScanSource = Get-WmiObject -Computer $Computer -Namespace root\ccm\SoftwareUpdates\WUAhandler -Class CCM_UpdateSource -ErrorAction Stop
                $OSVer = Get-WmiObject -Computer $Computer -Namespace root\cimv2 -Class Win32_OperatingSystem -ErrorAction Stop
                $InstalledComponents = Get-WmiObject -Computer $Computer -Namespace root\ccm -Class CCM_InstalledComponent -ErrorAction SilentlyContinue
                $HighestComponentVer = $InstalledComponents | Sort version | Select version -last 1
                $Explorer = Get-WmiObject -Class win32_process -Computer $Computer -Filter "Name = 'explorer.exe'" | Select -first 1 -ErrorAction SilentlyContinue
                $LastBootUpTime = $osver.ConvertToDateTime($osver.LastBootUpTime)
                $SCCMservice = Get-Service -Computer $Computer ccmexec
                $WUservice = Get-Service -Computer $Computer wuauserv
                $BITSservice = Get-Service -Computer $Computer bits
                $PowerComponent = $InstalledComponents | Where {$_.Name -like '*Power*'}
                $WUVer = (Get-Command \\$Computer\c$\windows\system32\wuapi.dll).fileversioninfo
                if($PowerComponent.Version -like '*2157*'){$R3 = "YES"}else{$R3 ="NO"}

                        $User = $Explorer.getowner()
                    $User = "No User Logged On"

                $ScanHistory | Select @{Name="SCCM Assigned Site Code";Expression={$SiteCode.sSiteCode}},
                @{Name="Highest Agent Component Version";Expression={$HighestComponentVer.Version}},
                @{Name="Windows Update Agent Version";Expression={$WUVer.ProductVersion}},
                @{Name="Is R3 Installed";Expression={$R3}},
                @{Name="Agent GUID";Expression={$AgentGUID.ClientId}},
                @{Name="Logged On User";Expression={$User.Domain,$User.User}},
                @{Name="Last Boot Time";Expression={$LastBootUpTime}},
                @{Name="Last Scan Time";Expression={$_.ConvertToDateTime($_.LastCompletionTime)}},
                @{Name="WSUS CAB Version";Expression={$ScanHistory.ToolContentVersion}},
                @{Name="WSUS Scan CAB Source";Expression={$scanSource.Contentlocation}},
                @{Name="Windows Update Service";Expression={$WUservice.Status}},
                @{Name="SMS Agent Host Service";Expression={$SCCMservice.Status}},
                @{Name="BITS Transfer Service";Expression={$BITSservice.Status}},
                @{Name="Operating System";Expression={$OSver.Caption}}

   #check if the machine has an update assignment targeted at it
   $UpdateAssigment = Get-WmiObject -Query "Select * from CCM_AssignmentCompliance" -Namespace root\ccm\SoftwareUpdates\DeploymentAgent -Computer $Computer -ErrorAction Stop
                $statusHash = [hashtable]@{
                    "0" = 'No Content Sources'
                    "1" = 'Available'
                    "2" = 'Submitted'
                    "3" = 'Detecting'
                    "4" = 'Downloading CIDef'
                    "5" = 'Downloading SdmPkg'
                    "6" = 'PreDownload'
                    "7" = 'Downloading'
                    "8" = 'Wait Install'
                    "9" = 'Installing'
                    "10" = 'Pending Soft Reboot'
                    "11" = 'Pending Hard Reboot'
                    "12" = 'Wait Reboot'
                    "13" = 'Verifying'
                    "14" = 'Install Complete'
                    "15" = 'State Error'
                    "16" = 'Wait Service Window'

            $DPLocalityHash = [hashtable]@{
                    "10" = 'UNPROTECTED DP'
                    "74" = 'PROTECTED DP'

   #if update assignments were returned check to see if any are non-compliant
   $IsCompliant = $true
   $UpdateAssigment | ForEach-Object{
   Write-Host "Update Assignment: $($_.AssignmentId) : " -NoNewline
   if($_.IsCompliant -eq $true){Write-Host "Compliant" -ForegroundColor Green}else{Write-Host "Non-Compliant" -ForegroundColor Red}
   #mark the compliance as false
   if($_.IsCompliant -eq $false -and $IsCompliant -eq $true){$IsCompliant = $false}
   Write-Host "No Software Update Assignment targeted!" -ForegroundColor Yellow
   #the machine is not compliant; search for the updates
   if($UpdateAssigment -and $IsCompliant -eq $false)
   #check if the machine has any targeted updates that are missing
   $TargetedUpdates = Get-WmiObject -Query "Select * from CCM_TargetedUpdateEX1 where UpdateState = 0" -Namespace root\ccm\SoftwareUpdates\DeploymentAgent -Computer $Computer -ErrorAction Stop

   #loop through updates and get the details.
   $TargetedUpdates | ForEach-Object {

   #get the GUID
   $uID=$_.UpdateID | Select-String -Pattern "SUM_[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}" | Select -Expand Matches | Select -Expand Value
   #strip out the SUM_
                            $uDPLocality = $DPLocalityHash[$_.DPLocality.tostring()]
                            $uPercentComplete = $_.PercentComplete
                            #[decimal]$StatusNo = $_.UpdateStatus
   #query the update status from WMI
   Get-WmiObject -Query "Select * from CCM_UpdateStatus where UniqueID = '$($uID)'" -Namespace root\ccm\SoftwareUpdates\UpdatesStore -Computer $Computer | ForEach-Object {
   $uBulletinID = $_.Bulletin
   #if there is no MS00-000 ID swap it for the KB article number
   if($uBulletinID -eq ""){$uBulletinID="KB$($_.Article)"}
   #Write-Host "[$uBulletinID] :: [$uTitle]"
   $UpdatesMissing+= "[$uBulletinID] :: [$uTitle]::[$uDPLocality]::[%$uPercentComplete]::[$uStatus]"
   Write-Host "[$iMissing] required security updates are missing:"-ForegroundColor Red
   #resort the array of missing updates and return it
   $UpdatesMissing=$UpdatesMissing | Sort-Object -Descending
   return $UpdatesMissing
   #machine is targeted and compliant
   Write-Host "No Missing Software Updates found." -ForegroundColor Green


             Write-Host "" 
             Write-Host $($Computer).ToUpper()"is NOT Online." -ForegroundColor DarkYellow
             Write-Host ""




Wednesday, 7 November 2012

PowerShell - Error when attempting to enable PS remoting.

Whilst enabling PowerShell remoting on our SCCM site servers I encountered this error.

Set-WSManQuickConfig : The WinRM client cannot process the request. It cannot determine the content type of the HTTP re
sponse from the destination computer. The content type is absent or invalid.
At line:50 char:33
+             Set-WSManQuickConfig <<<<  -force
    + CategoryInfo          : InvalidOperation: (:) [Set-WSManQuickConfig], InvalidOperationException
    + FullyQualifiedErrorId : WsManError,Microsoft.WSMan.Management.SetWSManQuickConfigCommand

1 \ Set the following two registry keys.
MaxFieldLength - 65534
MaxRequestBytes - 16777216
2\ Restart the server.