There are numerous of ways to measure update compliance in an enterprise. Some prefer to just compare the compliance of a Software Update Group against a collection. The issue I’ve found with this is that when you release updates the compliance falls down to 0, and gradually increases over time.

This might be fine if you measure compliance once a month (or when appropriate), but not if you need to report compliance at any time. A report with 10% compliance could be quite shocking to upper management…

Another way is to rely on Reports, but I’ve found most of them to be unreliable, accounting for updates which have not been targeted for the devices/collections which we deploy/manage.

We prefer to leverage DCM and baselines to verify each clients full compliance status (not only including update compliance, but also safety features like BitLocker, Windows Defender, and Credential Guard). However in this article we will focus on update compliance.

To get high update compliance you need to have healthy devices. They need to have a working CM agent, enough space on the system drive to handle updates and no pending reboots to name some common issues.

Update Compliance Configuration Item

This configuration item is set up in an unnamed customers environment with about 2000 devices, spanning over approximately 150 offices with varying bandwidth.
We have set up a “grace period” which allows devices to not be fully updated for 20 days. This is to account for laptops being offline, and also for Software Group release dates and deadlines to not affect the compliance report. If you have more than 7 days delay between availability and deadline on the Software Group this period should probably be increased slightly.

Update Compliance
Discovery Script

$ErrorActionPreference = "SilentlyContinue"
$RegPath = "HKLM:\SOFTWARE\Microsoft"
$Date = Get-Date
$LastCompliance = (Get-ItemProperty -Path $RegPath -ErrorAction SilentlyContinue).LastCompliance
$MissingUpdates = get-wmiobject -query "SELECT * FROM CCM_SoftwareUpdate" -namespace "ROOT\ccm\ClientSDK" | Where {$_.ExclusiveUpdate -eq $false} | select name
[int]$MissingCount = $MissingUpdates.Name.Count

# If we are not missing any updates. Set LastCompliance to todays date.
If ($MissingCount -eq 0) {
    # Set LastCompliance to todays date.
    (New-ItemProperty -Path $RegPath -Name "LastCompliance" -Value $Date -PropertyType String -Force | Out-Null)
    Return $MissingCount
}
# The device has one or more updates pending.
Else {
    # Device has never been compliant.
    If ($LastCompliance -eq "Never" -or $LastCompliance -eq $null) {
        # Set LastCompliance to Never.
        (New-ItemProperty -Path $RegPath -Name "LastCompliance" -Value "Never" -PropertyType String -Force | Out-Null)
        # Refresh/reevaluate updates.
        (New-Object -ComObject Microsoft.CCM.UpdatesStore).RefreshServerComplianceState()
        ([wmiclass]'ROOT\ccm:SMS_Client').TriggerSchedule('{00000000-0000-0000-0000-000000000111}') | out-null
        # Return number of missing updates to baseline report.
        Return $MissingCount
    }
    # Device has not been compliant the last 20 days.
    ElseIf ($LastCompliance -lt ($Date).AddDays(-20)) {
            # Return amount of missing updates.
            Return $MissingCount
    }
    # Device has been compliant the last 20 days.
    Else {
            # Returning 0 to verify compliance (grace period).
            Return 0
    }
}

Compliance Rule

The value returned by the specified script: Equals 0
Report noncompliance if this setting instance is not found: Checked
Noncompliance severity for reports: Information