[AUM 4.1] Build Sheet: Hotpatch on APP02 and AZSRV01

APP02 enrolled in hotpatching through Arc with the Virtual Secure Mode gate in front of the enrolment, AZSRV01 confirmed hotpatch-capable through its marketplace image, and both verified in installation records rather than status fields.


By the end of this sheet, APP02 is enrolled in hotpatching through Azure Arc, with the Virtual Secure Mode check passed before the enrolment rather than discovered by it, and AZSRV01 is confirmed hotpatch-capable through its marketplace image with no Arc connection at all. Both are verified in installation records rather than status fields, and you know which month is the next baseline before anyone is told to expect a quiet quarter.


Before step one

You need local administrator on APP02, because the Virtual Secure Mode check and the registry write that follows both require an elevated session. In Azure you need permission to write the Arc machine resource and its licence profile, which the blanket role Azure Connected Machine Resource Administrator carries; Scheduled Patching Contributor does not, because it holds only Microsoft.Maintenance actions and hotpatch enrolment is not a maintenance operation. On AZSRV01 read access is enough, because that half is confirmation. APP02 must already be Arc-connected and showing as Connected; if it is not, that job comes first. Az PowerShell with the Az.ConnectedMachine module is needed only for the scripted path.

One statement of provenance, made once. This sheet is doc-verified rather than lab-run. Every command, portal noun, property name and status value below comes from current Microsoft documentation, checked on the day of writing, and where a live result could differ from the documented shape I say so and tell you to confirm it against your own first record rather than trusting mine.


The prerequisites as they actually appear

SettingValueWhy
Operating system edition, Arc routeWindows Server 2025 Standard or DatacenterThe supported list for Arc-enabled hotpatch is exactly these two. Nothing older qualifies by any route.
Operating system edition, Azure routeWindows Server 2025 Datacenter: Azure EditionHotpatch is already enabled by default and the machine does not need to be Arc-enabled.
Operating system build26100.1742 or laterPreview and Insider builds are excluded: hotpatches are not created for prerelease operating systems.
Installation optionServer with Desktop Experience or Server CoreBoth supported, so this constrains nothing. Recorded so nobody re-opens it.
FirmwareUEFI with Secure Boot enabledThe stated bare minimum for virtualization-based security.
Hyper-V virtual machine generationGeneration 2Follows from UEFI and Secure Boot. Generation is fixed at creation and is not editable later.
VirtualizationBasedSecurityStatus2The portal checks Virtual Secure Mode live. If it is not running, enabling hotpatch fails.
Azure Arc connectionConnected, Arc route onlyMandatory off Azure. On an Azure virtual machine, Arc is not supported in production.
Marketplace image, Azure routePublisher MicrosoftWindowsServer, offer WindowsServer, one of eight Azure Edition SKUsContainer base images, custom images and any other combination are excluded in terms.
Hotpatch charge, Arc routeNone since 19 May 2026No per-core meter, no hourly charge, no separate line item, in any environment or edition.

Naming, and the names you do not get to choose

Two names here are generated by the product. The licence profile carrying the hotpatch enrolment is always called default: you do not name it, and the only correct way to refer to it is by reading it back. The Arc machine resource is named at onboarding and is not guaranteed to match the hostname you know the server by, particularly where a batch was onboarded from a script. Read it once, from the machine, and carry it as a variable through everything that follows.

What you do name is the evidence. Use MACHINE-hotpatch-STAGE-YYYY-MM-DD with the extension the tool produces, so the pre-check output here is APP02-hotpatch-precheck-2026-08-12.txt. It sorts, it names the machine, and six months later it says which stage it belonged to. Whatever your change system calls its records, put the machine name and the month in the title, because the question you get asked is always about one machine in one month.

What travels, and where each file comes from

FileProduced onWhat it is for on arrival
APP02-hotpatch-precheck-DATE.txtAPP02, elevated PowerShellEvidence the Virtual Secure Mode and Secure Boot gate passed before enrolment, or the justification for the planned restart if it did not.
APP02-arcidentity-DATE.jsonAPP02, from azcmagent showThe Azure resource name and resource group, generated at onboarding. Later commands read them from here rather than assuming the hostname.
APP02-licenseprofile-DATE.txtAdmin workstation, from Az.ConnectedMachineThe enrolment record: the hotpatch product feature and its subscription status, read back from Azure rather than asserted.
hotpatch-install-records-YYYY-MM.csvAdmin workstation, exported from Azure Resource GraphThat month’s installation evidence, exported because patchinstallationresources retains thirty days and the question arrives later.

Part one: APP02, the Arc route

This is the substantial half, and the order matters more than the individual commands. Every check that could disqualify the machine happens before the enrolment, because enrolment is the step that fails loudly and wastes a change window, and because the fix for the likeliest failure is itself a restart.

  1. Confirm the edition, the installation type and the build floor. Run both commands in an elevated PowerShell session on APP02 and capture the output to the pre-check file.
Get-ComputerInfo -Property WindowsProductName, WindowsInstallationType

Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' |
    Select-Object CurrentBuildNumber, UBR

Expected: a product name naming Windows Server 2025 Standard or Datacenter, an installation type of Server or Server Core, CurrentBuildNumber of 26100, and a UBR of 1742 or higher. Failure modes: a preview or Insider build, which looks perfectly healthy and is excluded outright, and an edition outside Standard and Datacenter, which no amount of Arc rescues.

  1. Read the Arc identity from the machine rather than assuming it. Run this on APP02 and keep the JSON.
azcmagent show

azcmagent show -j

Expected: an agent status of Connected, a recent heartbeat, and an Azure resource block naming the subscription, resource group and resource name. Take the resource name and resource group from that output. Failure mode: a Disconnected agent, in which case nothing later works and the Arc agent is the job. This command needs no administrator privileges, so it is the cheapest check here.

  1. The gate. Check Virtual Secure Mode and Secure Boot before you enrol anything. Elevated PowerShell on APP02.
Get-CimInstance -Namespace 'root/Microsoft/Windows/DeviceGuard' -ClassName 'win32_deviceGuard' |
    Select-Object -ExpandProperty 'VirtualizationBasedSecurityStatus'

Confirm-SecureBootUEFI

Expected: the first returns 2, meaning Virtual Secure Mode is configured and running, and the second returns True. If both pass, skip step four and go to the enrolment. Three failure modes, meaning three different things. Any value other than 2 means Virtual Secure Mode is not running and the enrolment will fail at the moment you attempt it. Cmdlet not supported on this platform. means the machine is BIOS or non-UEFI, which on Hyper-V means Generation 1, and since generation is fixed at creation that machine is out of scope until it is rebuilt. Unable to set proper privileges. Access was denied. is about your shell, not the server: elevate and run it again.

Verifying after the enrolment tells you something is wrong once you have already spent the change window. Verifying before it costs two commands.

  1. Only if the gate failed on the status value: enable virtualization-based security, and spend the one restart. This is the irreversible-feeling step in this sheet, in the sense that it costs an outage window, so it happens after the check and never speculatively.
New-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\DeviceGuard' -Name 'EnableVirtualizationBasedSecurity' -PropertyType 'Dword' -Value 1 -Force

Restart-Computer

Get-CimInstance -Namespace 'root/Microsoft/Windows/DeviceGuard' -ClassName 'win32_deviceGuard' |
    Select-Object -ExpandProperty 'VirtualizationBasedSecurityStatus'

Expected after the restart: 2. Failure mode: still not 2, which means the physical or virtual hardware requirements are not met and no registry value will change that. At that point it is a virtualisation platform question, not a patching one. For the change request: this restart is the cost of stopping the restarts, and it is the only one enrolment asks for. Say that in the request rather than the retrospective.

  1. Enrol APP02 in Update Manager. In the Azure portal, go to Azure Update Manager. Under Resources, select Machines, and select the Arc-enabled server. Under the Recommended updates section, in Hotpatch, select Change. Choose the option Receive monthly Hotpatch updates, select Enable Hotpatching, then select Confirm. Allow about ten minutes for the change to apply.
  2. Read the status back rather than trusting the wizard. On the same machine’s Updates page, the Hotpatch status appears under Recommended updates. Across the estate, go to Machines, select Edit columns, add Hotpatch status and save; that column reports Azure machines and Arc-enabled machines together, and the Resource Type filter narrows it to Arc-enabled server.

Two notes on the surface. The same enrolment can be made from the Azure Arc blade, by selecting Machines, then the machine, then Hotpatch and Confirm: one operation, two doors. The at-scale path is separate: from Machines, under Settings, select Update settings, then Change update settings, then Add machine, select the resources, add them, set the Hotpatch dropdown to Enable and save. For two machines the per-machine path is faster and more legible in an audit trail.

The status model, which is a licence state and not a delivery guarantee

StatusMeaningWhat to do
Not enrolledLicence is available but not enrolled on this machine.The starting state. Run step five.
PendingInterim status while enrolment is in progress.Wait ten minutes. If it stays here, troubleshoot the Arc agent, not hotpatch.
EnabledLicence is enrolled and the machine is enabled for receiving hotpatch updates.The target state. Proves enrolment, not delivery.
DisabledLicence is enrolled but the machine is disabled for receiving hotpatch updates.Enrolled and switched off, which is not the same as never enrolled.
CanceledLicence has been canceled on the machine.Find out who and when before re-enrolling.

Part one, the scripted equivalent

One block, from an authenticated Az PowerShell session with the Az.ConnectedMachine module available. It reads the current state, enrols, and reads it back. Fill the two variables from the output of step two rather than from memory.

$Machine = 'the resource name from azcmagent show'
$Rg      = 'the resource group from azcmagent show'

# Before: read the licence profile, which the product always names 'default'
Get-AzConnectedLicenseProfile -MachineName $Machine -ResourceGroupName $Rg |
    Select-Object -ExpandProperty ProductProfileProductFeature

# Enrol
$feature = Update-AzConnectedLicenseProfileFeature -Name 'Hotpatch' -SubscriptionStatus 'Enable'
Update-AzConnectedLicenseProfile -MachineName $Machine -ResourceGroupName $Rg -ProductProfileProductType 'WindowsServer' -ProductProfileSubscriptionStatus 'Enabled' -ProductProfileProductFeature $feature

# After: read it back
Get-AzConnectedLicenseProfile -MachineName $Machine -ResourceGroupName $Rg |
    Select-Object -ExpandProperty ProductProfileProductFeature

Expected: the returned product feature collection contains an entry named Hotpatch with a subscription status of Enabled and an enrolment date, alongside whatever other features the machine already carries. Failure mode: if the machine has no licence profile at all, the update verbs have nothing to update and the create verbs apply instead, New-AzConnectedLicenseProfileFeature and New-AzConnectedLicenseProfile, the latter also requiring the machine’s location. Do not construct the profile’s resource identifier by hand. Read it from the object you just retrieved.


Part two: AZSRV01, the Azure route

There is nothing to enable here, which is the point. You are confirming that a decision made when the machine was created still holds, and putting the answer somewhere whoever asks in eighteen months can find it.

  1. Confirm the marketplace image is genuinely one of the eight.
az vm show --resource-group RESOURCE-GROUP --name AZSRV01 --query storageProfile.imageReference

az vm show --resource-group RESOURCE-GROUP --name AZSRV01 --query osProfile.windowsConfiguration.patchSettings

Expected from the first: publisher MicrosoftWindowsServer, offer WindowsServer, and a SKU that is one of 2025-Datacenter-Azure-Edition, 2025-Datacenter-Azure-Edition-smalldisk, 2025-Datacenter-Azure-Edition-Core, 2025-Datacenter-Azure-Edition-Core-smalldisk, or the four equivalent 2022 SKUs. From the second: enableHotpatching reading true, alongside the patch mode and assessment mode.

Failure modes, both terminal rather than fixable. If the SKU reads 2025-Datacenter or anything else, the machine is outside the supported combination list and has no route to hotpatch, because Arc on an Azure virtual machine is not supported in production either. If the image reference carries an identifier pointing at a gallery or community image instead of a publisher, offer and SKU, that is a custom image, excluded in the same sentence. Either way the remedy is a redeploy or an accepted monthly restart, which is a design conversation rather than a fix.

One dependency worth knowing before somebody tidies it up: enableHotpatching does not stand alone. It requires the virtual machine agent to be provisioned and the patch mode to be AutomaticByPlatform. A well-meaning change to the orchestration setting can turn the property off as a side effect, on a machine whose image was never in question.

  1. Confirm the same thing in the portal, because that is where somebody else will check it. Open the virtual machine’s Overview page and under Operations select Updates. The hotpatch status and the most recently installed patches appear under Recommended updates, with Update compliance and Update history as the neighbouring tabs. In Azure Update Manager, the Hotpatch status column added in step six reports Azure machines and Arc machines side by side, which is the view worth bookmarking.
  2. Do not connect AZSRV01 to Arc. It gains nothing: Azure Edition does not need to be Arc-enabled and hotpatch is already on. Attempting it in production is unsupported and returns an error saying so. The temptation is real, because the Arc route is the one you just walked and it feels like the general case. It is not.

Worked example: a month in the life of APP02

APP02 sits in Ring1 under a maintenance configuration that runs on the Saturday after the second Tuesday, with the reboot option set to Reboot if required. Nothing about that configuration changes across the two months below. The only thing that changes is what Microsoft shipped.

May 2026 was a hotpatch month. The Windows Server 2025 release on 12 May was a hotpatch, KB5087423, taking the build to 26100.32772. In the window the machine installs it and keeps running. What you look for in the run record is a successful run whose reboot status reads NotNeeded, with the individual patch showing a reboot requirement of false, and an uptime unbroken across the window. Nobody in the business notices anything, which is the entire product.

July 2026 was a baseline month. The release on 14 July was KB5099536, taking the build to 26100.33158, and a baseline carries everything a comparable cumulative update carries and requires a restart. Same machine, same maintenance configuration, same reboot setting, completely different afternoon. The reboot status moves through Started to Completed. Had the configuration been set to Never reboot, the run would have ended as Completed with warnings, leaving the machine carrying a pending restart into the following week, which is worse in every way that matters.

The design lesson is the one people learn expensively. Size the maintenance window for the baseline month, not the hotpatch month. Four times a year, occasionally five, the window has to fit a full cumulative update plus a restart, and Windows reserves ten minutes of it for that restart before starting an installation it cannot finish. A window tuned to the pleasant months silently skips the install in the serious ones, and that failure presents as an absence rather than an error. The rings build sheet sets the windows; this is the constraint they satisfy.

Between those two months sat June, an unplanned baseline: two restart months back to back on a machine enrolled specifically to have fewer of them. Not a defect, and not a reason to disenrol. It is the exception clause working as documented, and it is why the promise you make is fewer restarts with exceptions.


Validation: proving delivery, not enrolment

A status of Enabled proves a licence state. It does not prove that a hotpatch arrived and the machine stayed up, which are the two facts anybody actually asks for. Two checks, one on the machine and one in Azure, together the end-to-end test.

On APP02, after the first hotpatch month’s window, run these three in an elevated session.

Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' |
    Select-Object CurrentBuildNumber, UBR

(Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime

Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 5

That is the whole test, in one sentence: the revision moved and the boot time did not. The UBR should have advanced to the revision published for that month’s hotpatch, LastBootUpTime should be unchanged from before the window, and the KB should appear among the recently installed updates. A revision that advanced across a restart proves nothing, because that is what an ordinary month looks like. A revision that advanced without one is hotpatch working.

In Azure, the run record. Adapted from the published Update Manager sample queries, it runs in Azure Resource Graph Explorer, or through az graph query or Search-AzGraph if you want it in a file.

patchinstallationresources
| where type !has "softwarepatches"
| extend machineName = tostring(split(id, "/", 8))
| extend prop = parse_json(properties)
| extend lTime = todatetime(prop.lastModifiedDateTime),
         runStatus = tostring(prop.status),
         rebootStatus = tostring(prop.rebootStatus),
         installedPatchCount = tostring(prop.installedPatchCount)
| where lTime > ago(30d)
| project lTime, machineName, runStatus, rebootStatus, installedPatchCount
| sort by lTime desc

The documented values for rebootStatus are NotNeeded, Required, Started, Failed and Completed, and status runs NotStarted, InProgress, Failed, Succeeded and CompletedWithWarnings. For a hotpatch month you want a succeeded run with a reboot status of NotNeeded. I have not run this against a live hotpatch installation, so confirm that combination on your own first record rather than taking it as a promise. Once confirmed, that record is the template every later month is compared against.

To see which update landed, run the per-patch variant, where the KB number lives.

patchinstallationresources
| where type has "softwarepatches"
| extend prop = parse_json(properties)
| extend lTime = todatetime(prop.lastModifiedDateTime),
         patchName = tostring(prop.patchName),
         kbId = tostring(prop.kbId),
         installationState = tostring(prop.installationState),
         rebootRequired = tostring(prop.rebootRequired)
| where lTime > ago(30d)
| project lTime, patchName, kbId, installationState, rebootRequired
| sort by lTime desc

Do not filter this query on the KB number you expected. The month’s KB is published on the Windows Server hotpatch calendar and changes every month, and a query that hard-codes it returns an empty result when you guess wrong, which reads exactly like a machine that installed nothing. Read the KB back from the record and compare it to the calendar entry, in that direction. Then export the result, because this table retains thirty days and every audit question arrives later than that.


What the silent failure looks like from the outside

Enrolment fails loudly. Falling out of eligibility afterwards does not fail at all. Nothing errors, nothing turns red, no alert fires, and the status field may still read Enabled. The machine simply takes an update that requires a restart, and to anyone who does not know what month it was, that looks like an ordinary patching night.

The detector for a lost hotpatch is not an alert. It is a calendar next to a run record, compared by somebody once a month.

So build the check rather than waiting for one. Once a month, take the hotpatch calendar entry for the month just gone and the run record for each enrolled machine. A hotpatch month with a reboot status of Required, Started or Completed is the signal, and it deserves the same week rather than the next quarterly review. Note what I am not telling you: whether a disenrolment takes effect immediately or only at the next baseline is not documented anywhere I can find, so do not build a rule that depends on knowing. Build it on what the records say the machine did, which is observable.


Where this goes next

Two machines now do the same thing for entirely different reasons, both verified in records rather than dashboards, with the next baseline month on the calendar before anybody has been promised a quiet quarter.

None of it moved a single byte of content off WSUS. APP02 still asks a box in the comms room what it is allowed to install, and so does everything else here except the unjoined machine in Azure. Hotpatch changed when the restarts happen. The next article changes where the updates come from, which is Group Policy work from beginning to end and the piece with the traps in it.


Azure Update Manager
‹ Previous: [AUM 4] Hotpatch: Buying Back the Reboot
Next: [AUM 5] The Cutover: Leaving WSUS Without Breaking the Estate