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 ""





  1. Ben is there a source that explains the Update status codes better.

    Mostly interested in "0" = 'No Content Sources' and "15"='State Error'

    Thank you very much!

  2. ccm_scantoolhistory error, class not valid. But I can see it in wbemtest.exe

  3. Hi all, if anyone is still interested I have posted another blog post with a version of this module which works against an SCCM 2012 agent. Apologies for the lack of response.