The patch compliance report is wrong. Some machines say "No assessment", some show the last install as Failed, and some were in a maintenance window last night but nothing ran. This guide sorts those three symptoms into causes and fixes, in the order that finds the problem fastest.
Symptom to cause
| Symptom | Most likely cause | Fix |
|---|---|---|
| "No updates data" or assessment is days old | Periodic assessment off, VM agent not ready, machine deallocated | Set assessmentMode to AutomaticByPlatform, fix the agent |
| Assessment fails immediately | Windows Update service disabled, WSUS policy pointing to a dead server, Linux repo unreachable | Fix the update source on the guest |
| Install shows Failed or partial | Maintenance window too short, reboot blocked, package manager locked | Raise --maximum-duration, check guest logs |
| Schedule ran but machine skipped | Patch orchestration not set to AutomaticByPlatform with the safety check bypass | Set both properties |
| Schedule never touched the machine | No maintenance configuration assignment, or the dynamic scope filter doesn't match | Check assignments and scope tags |
| Arc server shows nothing | Arc agent disconnected or resource provider not registered | Reconnect agent, register providers |
Check 1: find the affected machines with Resource Graph
Start from the fleet view, not the portal blade for one VM. Resource Graph holds the latest assessment and installation results.
patchassessmentresources
| where type =~ "microsoft.compute/virtualmachines/patchassessmentresults"
or type =~ "microsoft.hybridcompute/machines/patchassessmentresults"
| extend vmId = tolower(tostring(split(id, "/patchAssessmentResults/")[0]))
| extend status = tostring(properties.status),
lastRun = todatetime(properties.lastModifiedDateTime),
osType = tostring(properties.osType)
| where status != "Succeeded" or lastRun < ago(2d)
| project vmId, osType, status, lastRun
| order by lastRun asc
Run it from the CLI so you can save the output for the ticket:
az graph query -q "patchassessmentresources | where type =~ 'microsoft.compute/virtualmachines/patchassessmentresults' | extend status=tostring(properties.status), lastRun=todatetime(properties.lastModifiedDateTime) | where status != 'Succeeded' or lastRun < ago(2d) | project id, status, lastRun" --first 500 -o table
For failed installs, query patchinstallationresources:
patchinstallationresources
| where type endswith "/patchinstallationresults"
| extend status = tostring(properties.status),
started = todatetime(properties.startDateTime),
failed = toint(properties.failedPatchCount),
installed = toint(properties.installedPatchCount)
| where started > ago(7d) and status != "Succeeded"
| project id, status, started, installed, failed
| order by started desc
Tip
Property names inside
propertieshave changed between API versions. If a column comes back empty, runpatchinstallationresources | take 1and read the raw JSON to get the names your tenant returns.
Check 2: patch orchestration and assessment settings
Scheduled patching only touches Azure VMs whose patch mode is AutomaticByPlatform and that have bypassPlatformSafetyChecksOnUserSchedule set to true. If the bypass is true but no schedule is assigned, the machine is never patched at all. That combination quietly causes a lot of these reports.
Read the current settings:
az vm show -g rg-prod -n vm-app01 \
--query "{win:osProfile.windowsConfiguration.patchSettings, linux:osProfile.linuxConfiguration.patchSettings}" -o json
Set them on a Windows VM:
az vm update -g rg-prod -n vm-app01 \
--set osProfile.windowsConfiguration.patchSettings.patchMode=AutomaticByPlatform \
osProfile.windowsConfiguration.patchSettings.assessmentMode=AutomaticByPlatform \
osProfile.windowsConfiguration.patchSettings.automaticByPlatformSettings.bypassPlatformSafetyChecksOnUserSchedule=true
On Linux, use the same keys under osProfile.linuxConfiguration.patchSettings.
Warning
Some Azure CLI releases after 2.78 have reported problems setting the bypass flag with
az vm update. If the command errors or the value doesn't stick, set it in the portal (Update Manager > Settings > Update settings) or through the built-in Azure Policy that configures periodic assessment and patch orchestration. Then confirm withaz vm show.
assessmentMode=AutomaticByPlatform turns on periodic assessment, which runs about every 24 hours. Without it, you only see data when someone clicks "Check for updates".
Check 3: the VM agent and the patch extension
Update Manager runs through the Azure VM agent and an extension it installs on demand (WindowsPatchExtension or LinuxPatchExtension, publisher Microsoft.CPlat.Core).
az vm get-instance-view -g rg-prod -n vm-app01 \
--query "{power:instanceView.statuses[1].displayStatus, agent:instanceView.vmAgent.statuses[0].displayStatus, ext:instanceView.extensions[].{name:name, status:statuses[0].displayStatus}}" -o json
The agent must be Ready and the VM must be running. A deallocated VM is skipped by both assessment and maintenance runs; that is expected, not an error.
Read the extension logs without logging in, using Run Command:
# Windows
az vm run-command invoke -g rg-prod -n vm-app01 --command-id RunPowerShellScript \
--scripts "Get-ChildItem C:\WindowsAzure\Logs\Plugins\Microsoft.CPlat.Core.WindowsPatchExtension -Recurse -Filter *.log | Sort-Object LastWriteTime | Select-Object -Last 1 | Get-Content -Tail 60"
# Linux
az vm run-command invoke -g rg-prod -n vm-lnx01 --command-id RunShellScript \
--scripts "ls -t /var/log/azure/Microsoft.CPlat.Core.LinuxPatchExtension/ && tail -n 60 /var/log/azure/Microsoft.CPlat.Core.LinuxPatchExtension/*.log"
Log folder names can carry a version suffix on some builds, so list the parent folder first if the path doesn't exist.
Check 4: the update source inside the guest
Most assessment failures are a broken update source, not an Update Manager fault.
Windows
Get-Service wuauserv, bits | Select-Object Name, Status, StartType
reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" /v WUServer
reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" /v UseWUServer
wuauservstart type Disabled (often set by a hardening GPO) makes every assessment fail. Set it to Manual.- If
UseWUServeris 1, the machine asks the WSUS server inWUServer. If that server is retired or unreachable, assessment fails. Fix the GPO, don't change the registry by hand; Group Policy writes it back. - With no WSUS, the VM needs outbound HTTPS to Windows Update endpoints. A VM in a subnet with no outbound path (see the default outbound access retirement) can't reach them.
Linux
sudo apt-get update # Debian/Ubuntu
sudo dnf check-update || true # RHEL/Alma/Rocky (exit code 100 means updates exist)
sudo zypper refresh # SLES
Look for repository errors, expired GPG keys, or a lock held by unattended-upgrades or another package process. A stuck dpkg lock makes the install fail with a timeout.
Check 5: schedules and assignments
If assessment is healthy but the maintenance run skipped the machine, check the assignment:
az maintenance assignment list \
--resource-group rg-prod --resource-name vm-app01 \
--resource-type virtualMachines --provider-name Microsoft.Compute -o table
No row means the machine isn't in the schedule. For dynamic scopes, check that the machine's subscription, resource group, location, OS type and tags actually match the filter. A tag typo (Patch-Ring vs PatchRing) is a common cause.
Look at the configuration's window too:
az maintenance configuration show -g rg-maint -n mc-ring1-sun02 \
--query "{start:maintenanceWindow.startDateTime, duration:maintenanceWindow.duration, tz:maintenanceWindow.timeZone, recur:maintenanceWindow.recurEvery, reboot:installPatches.rebootSetting}" -o json
Update Manager stops starting new installs near the end of the window so it can reboot inside it. With a short window and a big cumulative update, the run ends with some patches not attempted.
Fix: run it once by hand
After fixing the cause, prove it with an on-demand assessment and install:
az vm assess-patches -g rg-prod -n vm-app01
az vm install-patches -g rg-prod -n vm-app01 \
--maximum-duration PT2H --reboot-setting IfRequired \
--classifications-to-include-win Critical Security
For Linux, use --classifications-to-include-linux Critical Security. Both commands return the status and counts, and the result appears in Resource Graph within a few minutes.
Caution
--reboot-setting IfRequiredorAlwayswill restart the VM. On a production single-instance VM, run this inside an agreed window or useNeverRebootand schedule the restart separately.
Arc-enabled servers
For Arc machines, check the Connected Machine agent first:
az connectedmachine show -g rg-hybrid -n srv-onprem01 --query "{status:status, lastSeen:lastStatusChange, agent:agentVersion}" -o table
A status of Disconnected means no assessment data. Restart the himds service on the server and confirm outbound access to the Arc endpoints. Also make sure the subscription has the Microsoft.HybridCompute and Microsoft.Compute resource providers registered (az provider show -n Microsoft.Compute --query registrationState).
Prevent it next time
- Assign the built-in policies that enable periodic assessment and set the patch orchestration prerequisites, so new VMs are correct on day one.
- Alert on the Resource Graph query in Check 1: any machine with no successful assessment in 48 hours.
- Keep WSUS and proxy changes in the change calendar; retiring a WSUS server breaks assessment for every VM still pointing at it.
- Size maintenance windows for the largest monthly update, not the average one. Leave time for the reboot.
- After each patch cycle, review
patchinstallationresourcesforFailedand partial runs before the next ring starts.