Enhance Update Compliance Reporting with Microsoft Endpoint Manager

If you’re using Windows Update for Business in Microsoft Endpoint Manager you’re probably also using Microsoft’s Update Compliance solution for reporting. Update Compliance contains some useful data and I know the team are working on additional improvements.

A while back I created my own “Update Compliance” solution in part because I wasn’t happy with the data latency inherent in Microsoft Update Compliance, and in part because there was some additional data I wanted to report on, such as updates other than FUs and CUs like drivers as well as WU/WUfB policies and settings.

I wanted to publish some of the additional data that I gather with my solution so that, if you want to, you can add this data to same Log Analytics workspace where your Microsoft Update Compliance solution is installed and enhance your own reports.

To gather this data, deploy the PowerShell script below using Proactive remediations in Intune and run it on a schedule. The script supports full and delta inventories. The script will gather the data and send it directly to your Log Analytics workspace for ingestion into custom logs. This does require adding the workspace key to the script. Each log entry contains the Azure AD device Id, the Intune device Id and the computer name, so you can easily join the data with existing tables in Update Compliance.

Below is a summary of the data gathered. Some of this is now available natively in Update Compliance.

LA custom log nameData
SU_DeviceInfo_CLInventory dates, OS info, manufacturer/model, current user, current patch level
SU_AvailableUpdates_CLDetails on any updates available to a device on Windows update. This is gathered by scanning WU online. Updates are categorised.
SU_UpdateLog_CLEntries from the System event log and the Microsoft-Windows-WindowsUpdateClient provider. Includes only the most recent event per update. Updates are categorised. This is a handy source for quickly identifying which updates have attempted to install by WU, when, and whether they succeeded.
SU_MDMUpdatePolicy_CLPolicy settings deployed for Windows Update for Business by Microsoft Endpoint Manager. Harvested from the registry at HKLM:\SOFTWARE\Microsoft\PolicyManager\current\device\Update
SU_WUPolicyState_CLThe current WU policy state. Harvested from the registry at HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UpdatePolicy\PolicyState
SU_WUPolicySettings_CLIndicates whether FUs or QUs are paused and the start and end times if they are. Harvested from the registry at HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UpdatePolicy\Setting and HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings
SU_WUClientInfo_CLSome general WU client info I find useful such as whether a reboot is pending from SUs, the WU service start-up type, whether AutoUpdate is enabled, if a reminder notification has been displayed and when, when a reboot may be scheduled for WUfB.
SU_CompatMarkers_CLCompatibility data for feature updates, eg whether an update is blocked, the “RedReason” (what’s blocking the update) and any safeguard hold Ids.

To deploy the script, add your workspace ID and key, as well as a unique name, like your company name for example (this is used to create registry keys and a file directory). You can also set the minimum full and delta inventory schedules – these are a safeguard to prevent the script executing more frequently than needed because PR scripts can run multiple times in SYSTEM and user contexts.

The script will log to the registry at HKLM:\Software\<company name>\SoftwareUpdateReporting:

It will also create a local directory under C:\ProgramData\<company name>\SoftwareUpdateReporting containing the delta and full inventories in json format.

Just deploy the PR script as a detection script – no remediation script is required. You set the schedule, so you have as little or as much data latency as you want.

The script is available here.

Here are some example KQL queries on the data:

Query the current MDM Policy settings for a device:

| summarize arg_max(TimeGenerated,*) by IntuneDeviceID_g
| where ComputerName_s == "PC001"

Query all devices with failed driver updates:

| where KeyWord2_s in ("Failure","Revert") and UpdateType_s == "Driver update"
| top-nested of IntuneDeviceID_g by temp=max(1),
    top-nested 1 of InventoryDate_t by temp99=max(InventoryDate_t),
    top-nested of ComputerName=ComputerName_s by temp1=max(1),
    top-nested of UpdateName=UpdateName_s by temp2=max(1),
    top-nested of KeyWord1=KeyWord1_s by temp3=max(1),
    top-nested of KeyWord2=KeyWord2_s by temp4=max(1),
    top-nested of RebootRequired=RebootRequired_s by temp5=max(1),
    top-nested of UpdateType=UpdateType_s by temp6=max(1),
    top-nested of WindowsVersion=WindowsVersion_s by temp7=max(1),
    top-nested of WindowsDisplayVersion=WindowsDisplayVersion_s by temp8=max(1),
    top-nested of KB=KB_s by temp9=max(1),
    top-nested of ErrorCode=ErrorCode_s by temp10=max(1),
    top-nested of ErrorDescription=ErrorDescription_s by temp11=max(1),
    top-nested of EventTime=TimeCreated_t by temp12=max(1)
| project-away temp*

Query all Windows 10 devices that are blocked from upgrading to Windows 11:

| where CO21H2_BlockedFromUpgrade_s == "Yes" or CO21H2Setup_BlockedFromUpgrade_s == "Yes"
| summarize arg_max(TimeGenerated,*) by IntuneDeviceID_g
| project 

My own UC solution has even more data than this as I also gather support info and update history from MS Docs and calculate compliance on a schedule using an Azure automation runbook, but that part is somewhat more complicated and not within scope of this post 🙂

Hopefully you will find this additional data useful, and you can also customise the script to add whatever other data you want to gather or inventory from your devices.