Hotpatching is available as an option for Aure Arc connected Windows Server 2025 Datacenter and Standard machines. To learn more about hotpatching please review this article. Azure Arc portal provides a way for you to enroll or disenroll from hotpatching. To manage hotpatch updates, you can use Azure Update Manager (AUM) or any other patch management tool that has built the hotpatch management capability. You can also choose to use the published APIs to manage hotpatch enrollment and updates.
This article provides a sample script of using the APIs to perform the different actions. Always test these scripts in your development environment and tweak to your company’s security posture.
Below are the high-level management actions you may perform
- Enrolling/Disenrolling from hotpatch for Arc connected servers
- Temporarily opt-in or opt-out of without disenrolling from hotpatch
- Use Windows Update (WU) API to get the hotpatch update
You can perform these steps today through the Azure Arc portal and AUM. However, the ability to manage updates is not limited to AUM and any update management tool can make tooling changes to facilitate hotpatch management.
Hotpatch Enrollment & Disenrollment
Windows Server 2025 machines that are on-premises or on non-Azure cloud, and are Azure Arc connected can enroll into hotpatch servicing. Traditionally you would have used the Azure Arc portal and manually enrolled the machine for hotpatch servicing. You can also perform the same action using APIs at your disposal. Besides enrollment, you can also disenroll from hotpatch service using these APIs.
For details on these API review the specification here.
Sample Scripts:
Sample scripts that are used in this article may be found here: (GitHub). Examples have been added including utilizing a service principle within the scripts to assisting with automation.
See sample code for Enrollment:
# Enrollment workflow
$subscriptionId = '' #Your subscription id
$resourceGroupName = '' # your Resource Group
$machineName = '' # Arc resource name
$location = "" # The region where the test machine is arc enabled.
$subscriptionStatus = "Enable"; # Set SubscriptionStatus to "Disable" for disenrollment
$account = Connect-AzAccount -Subscription $subscriptionId
$context = Set-azContext -Subscription $subscriptionId
$profile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
$profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.rmProfileClient]::new( $profile )
$token = $profileClient.AcquireAccessToken($context.Subscription.TenantId)
$header = @{
'Content-Type'='application/json'
'Authorization'='Bearer ' + $token.AccessToken
}
$uri = [System.Uri]::new( "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.HybridCompute/machines/$machineName/licenseProfiles/default?api-version=2023-10-03-preview" )
$contentType = "application/json"
$data = @{
location = $location;
properties = @{
productProfile = @{
productType = "WindowsServer";
productFeatures = @(@{name = "Hotpatch"; subscriptionStatus = $subscriptionStatus};)
};
};
};
$json = $data | ConvertTo-Json -Depth 4;
# To create a license profile resource use PUT call
$response = Invoke-RestMethod -Method PUT -Uri $uri.AbsoluteUri -ContentType $contentType -Headers $header -Body $json;
# To update a license profile resource use PATCH call
#$response = Invoke-RestMethod -Method PATCH -Uri $uri.AbsoluteUri -ContentType $contentType -Headers $header -Body $json;
$response.properties.licenseProfile
Once this script is completed the hotpatch should be Enabled as shown in the Azure Portal below.
When you click into the Hotpatch overview tile, it will display the Virtualization-based security (VBS) status, the license is checked for receiving monthly hotpatches, and the “Enable hotpatching” toggle is turned On.
Sample code for Disenrollment
# Disenrollment workflow
$subscriptionId = '' #Your subscription id
$resourceGroupName = '' # your Resource Group
$machineName = '' # Arc resource name
$location = "" # The region where the test machine is arc enabled.
$subscriptionStatus = "Disable"; # Set SubscriptionStatus to "Enable" for enrollment
$account = Connect-AzAccount -Subscription $subscriptionId
$context = Set-azContext -Subscription $subscriptionId
$profile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
$profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.rmProfileClient]::new( $profile )
$token = $profileClient.AcquireAccessToken($context.Subscription.TenantId)
$header = @{
'Content-Type'='application/json'
'Authorization'='Bearer ' + $token.AccessToken
}
$uri = [System.Uri]::new( "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.HybridCompute/machines/$machineName/licenseProfiles/default?api-version=2023-10-03-preview" )
$contentType = "application/json"
$data = @{
location = $location;
properties = @{
productProfile = @{
productType = "WindowsServer";
productFeatures = @(@{name = "Hotpatch"; subscriptionStatus = $subscriptionStatus};)
};
};
};
$json = $data | ConvertTo-Json -Depth 4;
# To create a license profile resource use PUT call
$response = Invoke-RestMethod -Method PUT -Uri $uri.AbsoluteUri -ContentType $contentType -Headers $header -Body $json;
# To update a license profile resource use PATCH call
#$response = Invoke-RestMethod -Method PATCH -Uri $uri.AbsoluteUri -ContentType $contentType -Headers $header -Body $json;
$response.properties.licenseProfile
The Hotpatch tile displays Canceled after Disenrollment.
The checkbox is unchecked as well for receiving monthly hotpatches.
OPT-in /OPT-out
There could be scenarios where users want to temporarily opt out of Hotpatch, without disenrolling from Hotpatch service. That could be for troubleshooting or any other scenario. The option to opt-in and opt-out is available on the Azure Arc Portal, once you have enrolled in the Hotpatch service. It appears as a toggle switch for “Enable hotpatching” option. To do this programmatically review the sample code below.
Opt out Sample code:
# Change the following params for your test machine.
$subscriptionId = '' #Your subscription id
$resourceGroupName = '' # your Resource Group
$machineName = '' # Arc resource name
$location = "" # The region where the test machine is arc enabled.
$hotpatchStatus = $false # Do you want to opt-in ($true) or Opt-Out ($false) for receiving hotpatch without changing the license to get hotpatch.
$account = Connect-AzAccount -Subscription $subscriptionId
$context = Set-azContext -Subscription $subscriptionId
$profile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
$profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.rmProfileClient]::new( $profile )
$token = $profileClient.AcquireAccessToken($context.Subscription.TenantId)
$header = @{
'Content-Type'='application/json'
'Authorization'='Bearer ' + $token.AccessToken
}
$uri = [System.Uri]::new( "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.HybridCompute/machines/$machineName ?api-version=2024-07-10" )
$contentType = "application/json"
$data = @{
location = $location;
properties = @{
osProfile = @{
windowsConfiguration=@{
patchSettings=@{
enableHotpatching=$hotpatchStatus;
};
};
};
};
};
$json = $data | ConvertTo-Json -Depth 4; $response = Invoke-RestMethod -Method PATCH -Uri $uri.AbsoluteUri -ContentType $contentType -Headers $header -Body $json;
The Hotpatch tile now shows disabled after Opting out.
The Enable hotpatching slider is turned off.
Opt in Sample code:
# Change the following params for your test machine.
$subscriptionId = '' #Your subscription id
$resourceGroupName = '' # your Resource Group
$machineName = '' # Arc resource name
$location = "" # The region where the test machine is arc enabled.
$hotpatchStatus = $true # Do you want to opt-in ($true) or Opt-Out ($false) for receiving hotpatch without changing the license to get hotpatch.
$account = Connect-AzAccount -Subscription $subscriptionId
$context = Set-azContext -Subscription $subscriptionId
$profile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
$profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.rmProfileClient]::new( $profile )
$token = $profileClient.AcquireAccessToken($context.Subscription.TenantId)
$header = @{
'Content-Type'='application/json'
'Authorization'='Bearer ' + $token.AccessToken
}
$uri = [System.Uri]::new( "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.HybridCompute/machines/$machineName ?api-version=2024-07-10" )
$contentType = "application/json"
$data = @{
location = $location;
properties = @{
osProfile = @{
windowsConfiguration=@{
patchSettings=@{
enableHotpatching=$hotpatchStatus;
};
};
};
};
};
$json = $data | ConvertTo-Json -Depth 4; $response = Invoke-RestMethod -Method PATCH -Uri $uri.AbsoluteUri -ContentType $contentType -Headers $header -Body $json;
The Enable hotpatching slider is turned on after the Opting In.
Hotpatch Management
To manage the Hotpatches you can leverage existing Windows Update APIs along with some additional functionalities that gives you even more control over the updates.
Below is a sample code that allows you to scan for updates relevant to your machine. You can loop through the updates and determine if the updates will cause a reboot or not. Hotpatch updates should not cause a reboot unless any of the below two conditions is true:
- The device has a pending reboot from a previous update
- The device was on an update prior to the latest baseline update. So, when a hotpatch is offered during a hotpatch month it installs the latest baseline update followed by the hotpatch. The baseline update needs a reboot while the hotpatch itself is installed without a restart.
# Create Update Session
$session = New-Object -ComObject "Microsoft.Update.Session"
$session.ClientApplicationID = "Sample Code"
# Create Update Searcher and search for updates
Write-Host "Creating Update Searcher and searching for updates.."
$updateSearcher = $session.CreateUpdateSearcher()
$searchResult = $updateSearcher.Search("IsInstalled=0 and DeploymentAction='Installation'")
# If no updates found, exit
if ($searchResult.Updates.Count -eq 0)
{
Write-Host "No updates found!"
Exit
}
# Loop over each update in the Search result Update Collection
foreach ($update in $searchResult.Updates)
{
Write-Host "Update title: '$($update.Title)'"
Write-Host "Querying static 'ContainsUpdateBootstrapper' property.."
# Query for static extended property
# https://learn.microsoft.com/en-us/windows/win32/api/wuapi/nf-wuapi-iupdateex-get_extendedstaticproperty
$containsUpdateBootstrapper = $update.ExtendedStaticProperty("ContainsUpdateBootstrapper")
# If the update contains bootstrapper, perform pre-download and query for dynamic extended property
if ($containsUpdateBootstrapper)
{
Write-Host "Update '$($update.Title)' contains update bootstrapper"
Write-Host "Creating Update Downloader and performing pre-download of update bootstrapper.."
# Create an update collection and add updates to download
$downloadColl = New-Object -ComObject "Microsoft.Update.UpdateColl"
$downloadColl.Add($update)
# Create an Update Downloader and set the update collection to download
$downloader = $Session.CreateUpdateDownloader()
$downloader.Updates = $downloadColl
# Perform pre-download to download only update bootstrapper
# https://learn.microsoft.com/en-us/windows/win32/api/wuapi/nf-wuapi-iupdatedownloaderex-download2
$downloadResult = $downloader.Download2(1)
if ($downloadResult.HResult -eq 0)
{
Write-Host "Successfully downloaded update bootstrapper"
Write-Host "Evaluating dynamic 'DoesUpdateRequireReboot' property.."
# Query for dynamic extended property
# https://learn.microsoft.com/en-us/windows/win32/api/wuapi/nf-wuapi-iupdateex-evaluateextendeddynamicproperty
$doesUpdateRequireReboot = $update.EvaluateExtendedDynamicProperty("DoesUpdateRequireReboot")
if ($doesUpdateRequireReboot)
{
# If update requires reboot, skip download/install
Write-Host "Update: '$($update.Title)' requires reboot, skipping"
}
else
{
# If update does not require reboot, it is a rebootless update, hence perform full download and install
Write-Host "Update: '$($update.Title)' does not require reboot"
Write-Host "Performing full download of update '$($update.Title)'.."
# Perform full download of the update
$fullDownloadResult = $downloader.Download()
if ($fullDownloadResult.HResult -eq 0)
{
Write-Host "Successfully downloaded update: '$($update.Title)'"
Write-Host "Creating Update Installer to install update: '$($update.Title)'.."
# Create an update collection and add updates to install
$installColl = New-Object -ComObject "Microsoft.Update.UpdateColl"
$installColl.Add($update)
# Create an Update Installer, set the update collection to install and install the updates
$installer = $Session.CreateUpdateInstaller()
$installer.Updates = $installColl
# Install the updates
$installResult = $installer.Install()
if ($installResult.HResult -eq 0)
{
Write-Host "Successfully installed update: '$($update.Title)'"
}
else
{
Write-Host "Failed to install update: '$($update.Title)'!"
Exit
}
}
else
{
Write-Host "Failed to download update: '$($update.Title)'!"
Exit
}
}
}
else
{
Write-Host "Failed to download update bootstrapper for update: '$($update.Title)'!"
Exit
}
}
else
{
Write-Host "Update '$($update.Title)' does not contain update bootstrapper, skipping"
}
}
Below is a sample snippet of an update. This shows that the cumulative update does not require a reboot and that it was Hotpatch capable. This update will be installed as part of this update check.
Use Azure Resource Graph to determine which machines are enabled for hot patching.
Resources
| where type == "microsoft.hybridcompute/machines"
| extend hotpatchEnabled = tostring(properties.osProfile.windowsConfiguration.patchSettings.enableHotpatching)
| extend hotpatchStatus = tostring(properties.osProfile.windowsConfiguration.patchSettings.status.hotpatchEnablementStatus)
| project name, location, resourceGroup, hotpatchEnabled, hotpatchStatus
| order by hotpatchEnabled desc
In summary, effective management of hotpatching for Azure Arc connected machines requires a thoughtful approach that balances automation, security, and operational needs. By leveraging the provided sample scripts and APIs, organizations can streamline patch deployment while maintaining control over their environments. It is essential to rigorously test all automation in a development setting and adapt solutions to align with your company’s security posture. By embracing these strategies, organizations can proactively safeguard their systems, minimize downtime, and stay ahead in an ever-evolving cloud environment.
If you have questions, please feel to reach out to our team: hotpatchfeedback@microsoft.com
*** Disclaimer ***
The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.