Navigating the upgrade from Windows 10 to Windows 11 can be effortless when you know how to use Autopatch Groups for a smooth Windows 11 upgrade. This post will discuss some practical strategies to streamline and control your transition. I’ll also reveal a handy PowerShell script I utilize in an Azure runbook, ensuring new devices are automatically added to the correct Autopatch group. This guide can help you transition to Windows 11 before the October 14, 2025 deadline for Windows 10 support.
Table Of Contents
- Windows Autopatch
- Smooth Windows 11 Upgrade Using Autopatch
- Getting Started with Windows Autopatch
- Autopatch Release Management
- Feature Releases for Windows 10 and Windows 11
- Autopatch Device Onboarding
- Exclude AutoPatch Devices From Traditional Managed Updates
- Assign Devices To Correct Autopatch Group
- Manual VS Automatic Device Assignment
- Automation Using PowerShell Script
- Automation With An Azure Runbook
- Creating The Azure Runbook
- Give Access To The Managed Identity
- Add Graph Modules To The Runbook
- Create The Runbbok
- Validating the Vision: Smooth Windows 11 Upgrade
- Autopatch Reporting
- Log Analytics
- AutoPatch Troubleshooting
- Wrapping Up The Smooth Windows 11 Upgrade
- External References
Windows Autopatch
Windows Autopatch in Microsoft Intune is now the primary solution for managing updates. It combines features from the old Windows Update for Business deployment service, making the process easier and more efficient. With Windows Autopatch, keeping your devices up to date is straightforward. It uses automation to ensure smooth updates for the Windows operating system, Microsoft 365 Apps for enterprise, Microsoft Teams, and Microsoft Edge.
Read the following Microsoft Blog for the latest news on Consolidating update management for enterprises – Windows IT Pro Blog (microsoft.com)
Smooth Windows 11 Upgrade Using Autopatch
In many organizations, especially those with a legacy, the IT landscape is often a mixed bag of operating system versions. It’s common to see systems running everything from Windows 10 to the latest Windows 11, with some machines even running versions no longer supported. As we approach the end-of-support date for Windows 10 next year, businesses face a crucial decision. Security concerns are at the forefront of this discussion, as outdated systems become a prime target for cyberattacks.
The simple answer to mitigating these risks? Patching.
The goal for every business should be clear: all computers should be running the latest, fully supported operating system version—Windows 11. But how do you achieve this goal controlled and systematically, ensuring minimal disruption? Join me as I enter Windows Autopatch at a house with mixed levels of Windows operating systems.
Getting Started with Windows Autopatch
Windows Autopatch is an automated patching solution for Windows 10/11 Pro and Enterprise, where Enterprise E3 or higher licensed customers can enroll. Read the complete license requirements here: Windows Autopatch – Frequently Asked Questions (FAQ), License Requirements | Microsoft Learn
Tenant Enrollment
Enrollment in the Windows Autopatch service is started from the Microsoft Intune Admin Center under “Tenant Administration—Windows Autopatch—Tenant enrollment.”
The following picture series shows a typical onboarding routine. Click the pictures to see the full-screen details and quickly navigate between them.
The onboarding should be smooth sailing with no significant roadblocks – except for the part where you must dig up contact information for two technical contacts. That might be the most time-consuming task because, you know, tracking down people is always an adventure!
Windows Autopatch Device Configurations
Looking at Windows device configurations, I see that the onboarding to Windows Autopatch has added some new configuration policies. These policies target the different parts of Autpatch, like the Edge updates, Office updates, etc.
These new policies are all targeted by some new Entra ID security groups created to operate the service.
Conflicting Existing Policies
Be aware that these new policies might conflict with existing policies in your Intune configuration.
Follow the error markers through the devices to identify and correct conflicting settings.
The changes must be made to your custom policies, as the Windows Autopatch service will not allow changes to its policy set.
These numbers of conflicts might change as new groups of devices are onboarded to the Windows Autopilot service. Therefore, you should monitor configuration policies with failures, errors, and conflicts as you onboard devices to Windows Autopilot.
Tenant Administration Autopatch Admin Contacts
The admin contacts are one of the few things you can manage under Windows Autopatch found in the Tenant Administrator blade of Microsoft Intune:
The other Autopatch blades in the Tenant administration blade provide information about the service and the option to raise support requests.
Autopatch Release Management
Looking at Windows Autopatch Release Management available in Devices – Windows, we have a default configuration targeting Windows 10 version 22H2.
This could be great for targeting the Windows 10 devices in the tenant to take control of them and ensure they are patched. The default Windows Autopatch group targets the default release.
The phases/rings can be configured dynamically, or onboarded devices can be manually moved to a preferred ring.
Autopatch Device Onboarding to Autopatch Group
As seen in the configuration of the Autopatch group, a security group in Entra ID is used to onboard devices.
Adding devices to this group will eventually onboard them to Windows Autopatch. I can manually add devices to this group or another group that already holds the devices I want to target.
Devices added to the group will appear onboarded when looking at the Windows Autopatch device blade.
Here, we will see information about the assigned autopatch group and dynamically assigned ring.
Separate Autopatch Groups For Windows 10 and 11
In an environment with a high rate of Windows 10 devices, I want to control which devices are upgraded to Windows 11. To achieve this control, I will implement a new Autopatch group dedicated to Windows 11 devices.
Each Autopatch group will have a security group in Entra ID for onboarding. Each autopatch group can also be assigned individually to feature update release schedules.
The configuration of the new Autopatch group for Windows 11 devices is done like this (as earlier; click the pictures for large versions):
During the configuration in the pictures above, I started by creating the Entra ID security group to onboard the devices. The new Autopatch group is given a name and a description. The Entra ID group is then linked into the Deployment rings blade, where I also add the number of rings and a percentage-based dynamic group distribution. The Windows update settings can be adjusted to my liking before completing the Autopatch group.
The Many New Entra ID Security Groups
Adding a new autopatch group will create many new security groups in Entra ID.
These correspond with the different rings configured. These groups should not be touched since they are managed by the Autopatch logic, where devices are dynamically distributed through the rings. The groups can, however, be used for targeting generally throughout Intune, where you need a ring approach.
Feature Releases for Windows 10 and Windows 11
I already have a Feature update configuration for Windows 10 targeted by the default release schedule. I now want to create a new Feature update configuration for Windows 11, and this process runs as seen in the following pictures. (You know the drill now – click the images for better reading and navigation between them)
The process starts from Devices—Windows—Windows Autopatch—Release Management—Release Schedule—Windows Feature Updates—New Release. A target version (Windows 11 22H2) is selected, and a corresponding name and description are given.
The important part is to target this to the newly created Windows 11 Autopatch group. This configuration can’t be changed later. A release schedule can also be set, where I can define the pace of rollout through the different phases where the rings can be assigned. I can adjust this to the speed I prefer for this feature pack rollout.
Windows 11 24H2
While writing this post, Windows 11 24H2 has been released to General Availability (GA), making it the natural choice for those seeking the latest in security, performance, and user experience. This release is packed with new features aimed at keeping your data and identity secure, so it should, therefore, be the natural choice when setting up the new release in Windows Autopatch.
Be aware that you can’t assign 24H2 to an Autopatch group if it has an active ongoing process. You must cancel that deployment before assigning the new 24H2 to the autopatch group.
This will look like this after canceling Windows 11 22H2 and starting Windows 11 24H2.
Jeremy Chapman, Director of Microsoft 365, highlights how Windows 11 24H2 helps organizations remain secure, productive, and future-ready in the following video:
Microsoft also has a great online resource walking through all the news inside this update.
This page will give you a detailed overview of all the news in recent updates.
Autopatch Device Onboarding
As mentioned, device onboarding is done by adding devices to a group. I now have a configuration with two different Autopatch groups—one intended for Windows 10 and the other for Windows 11.
The illustration above shows the flow chart, and the groups in my environment now look like the following.
The default group has been renamed for the uniform experience.
Exclude AutoPatch Devices From Traditional Managed Updates
If you use traditional feature updates and update rings in Intune, you should exclude Autopatch devices from these policies.
To do this, add the group “Windows Autopatch – Devices All” as an excluded group in the policies.
Assign Devices To Correct Autopatch Group
The next step in my process is assigning devices to their respective security groups in Entra ID for device onboarding into Autopatch. This step is crucial because it provides the level of control I aim for. Devices assigned to the Windows 10 Autopatch group will remain on Windows 10, while those assigned to the Windows 11 Autopatch group will be upgraded to Windows 11 as needed.
Once a device is onboarded to an Autopatch group, it is automatically added to a dynamic ring for updates. However, adding devices to these groups—manually or automatically—requires careful consideration.
Manual VS Automatic Device Assignment
One option I’m considering is using dynamic groups in Entra ID that target all Windows 10 and Windows 11 devices, automatically placing them into the corresponding Autopatch device registration groups based on their running operating system.
While this would simplify the onboarding process, it could present challenges later. Moving devices between Autopatch groups—such as when upgrading from Windows 10 to Windows 11—could become more complicated if dynamic rules control the assignments. This rigidity might limit my flexibility when it comes to reassigning individual devices. At the same time, I am not interested in a fully manual routine that risks leaving devices out of Windows AutoPatch.
Automation Using PowerShell Script
Alternatively, I could assign devices to their respective Windows 10 or Windows 11 Autopatch groups using a PowerShell script. This method would give me greater control by building the logic I need while allowing me to decide when and how devices are moved between groups. For instance, if I want to upgrade a specific device to Windows 11, I can manually move it from the Windows 10 group into the Windows 11 group.
Automation With An Azure Runbook
While dynamic groups might simplify the initial onboarding, device management could be too rigid in the long run. By using a hybrid approach—combining PowerShell scripting for flexibility and an Azure Runbook for automation, I can scale my solution and reduce manual effort without losing control over how devices are lifted from Windows 10 to Windows 11.
I will go with the PowerShell script and create an Azure Automation Account Runbook that runs the PowerShell script on a schedule.
This script will check for all devices in the Tenant, also new devices, and automatically add them to the appropriate Windows 10 or Windows 11 group based on their running operating system. This way, I ensure devices are correctly assigned without constant manual intervention.
The script will also handle scenarios where a device is manually moved between groups. This becomes handy when Admins move devices to the Windows 11 group while still running Windows 10. I need the script to respect this, ensuring the devices are not reassigned to the Windows 10 group during the next update cycle.
This way, I can target Windows 10 devices to be updated to Windows 11 using Windows Autopatch.
Creating The Azure Runbook
It’s time to create the Azure Runbook. This standard routine follows the following four screenshots (click them for large view).
The most important part here is to create the system-assigned managed identity, which will be used to authenticate with Azure resources from the runbook.
Give Access To The Managed Identity
I will use my community script to assign permissions to the managed identity needed to run the automation in the Runbook. The rights required by the managed identity for this project are the following:
“DeviceManagementManagedDevices.Read.All”, “GroupMember.ReadWrite.All”, “Device.Read.All”
The script is available at my GitHub and looks like this:
<#
.SYNOPSIS
Created on: 19.02.2024
Created by: Simon Skotheimsvik
Info: Entra Role Assignments
.DESCRIPTION
Routine to add necessary roles to System Assigned Managed Identity in Function App or Automation Account.
#>
# Install-Module Microsoft.Graph -Scope CurrentUser
Import-Module Microsoft.Graph.Applications
# Variables
$TenantID = "YOUR VALUE HERE"
$managedIdentityId = "YOUR VALUE HERE"
$roleNames = "DeviceManagementManagedDevices.Read.All", "GroupMember.ReadWrite.All", "Device.Read.All"
# Connect to Graph
Connect-MgGraph -Scopes Application.Read.All, AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory -TenantId $TenantID -NoWelcome
# Get the Microsoft Graph Service Principal
$msgraph = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
# Set the app role assignments
foreach ($roleName in $roleNames) {
write-host $roleName
$role = $Msgraph.AppRoles| Where-Object {$_.Value -eq $roleName}
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $managedIdentityId -PrincipalId $managedIdentityId -ResourceId $msgraph.Id -AppRoleId $role.Id
}
Disconnect-MgGraph
PowerShellRunning this will set the necessary permissions on the managed identity.
Looking at the identity in Enterprise Applications, the permissions can be seen.
Add Graph Modules To The Runbook
The PowerShell script will use several Graph modules, which must be added to the Azure Runbook. The following screenshots show how to add one of the modules (as you might have tested already – click the pictures for a closer look).
The following modules will be added this way: Microsoft.Graph.Authentication, Microsoft.Graph.DeviceManagement, Microsoft.Graph.Groups and Microsoft.Graph.Identity.DirectoryManagement.
Create The Runbbok
With all the prerequisites, it’s time to create a runbook inside the Azure Automation Account.
The Runbook will now be created, and I can add my PowerShell script,, which contains the logic of my automation routine.
The script added is available in my GitHub.
<#
.SYNOPSIS
This script separates Windows devices based on their operating system version and adds them to respective Entra ID groups.
.DESCRIPTION
This script retrieves Windows devices from Intune, separates them into Windows 10 and Windows 11 devices based on their OS version, and adds them to the corresponding Entra ID groups.
.NOTES
Author: Simon Skotheimsvik
Version:
1.0.0 - 2024-09-23 - Initial release, Simon Skotheimsvik
1.0.1 - 2024-09-24 - Added the ability to run the script in Azure Automation using managed identity, Simon Skotheimsvik
#>
# Define the Entra ID group names
$windows10GroupName = "Windows 10 Autopatch - Device Registration"
$windows11GroupName = "Windows 11 Autopatch - Device Registration"
# Import the module
$RequiredModules = @('Microsoft.Graph.Authentication', 'Microsoft.Graph.DeviceManagement', 'Microsoft.Graph.Groups', 'Microsoft.Graph.Identity.DirectoryManagement')
foreach ($RequiredModule in $RequiredModules) {
try {
Write-Host "Importing $RequiredModule"
Import-Module $RequiredModule -Force
}
catch {
Write-Error -Exception $_.Exception.Message
}
}
# Authenticate to Microsoft Graph interactively
# Connect-MgGraph -Scopes "DeviceManagementManagedDevices.Read.All", "GroupMember.ReadWrite.All", "Device.Read.All" -NoWelcome
# Authenticate to Microsoft Graph using managed identity in Runbook
Connect-MgGraph -Identity -NoWelcome
# Get group IDs based on group names
$windows10Group = Get-MgGroup -Filter "DisplayName eq '$windows10GroupName'"
$windows11Group = Get-MgGroup -Filter "DisplayName eq '$windows11GroupName'"
$windows10GroupId = $windows10Group.Id
$windows11GroupId = $windows11Group.Id
# Initialize arrays
$windows10Devices = @()
$windows11Devices = @()
# Get all Windows devices from Intune
$devices = Get-MgDeviceManagementManagedDevice | Where-Object { $_.OperatingSystem -like "Windows*" }
# Separate devices based on OS version
foreach ($device in $devices) {
if ($device.OSVersion -lt "10.0.22000") {
$windows10Devices += $device
}
elseif ($device.OSVersion -ge "10.0.22000") {
$windows11Devices += $device
}
}
# Retrieve all current group members
$windows10GroupMembers = @()
$windows11GroupMembers = @()
$windows10GroupMembers = Get-MgGroupMember -GroupId $windows10GroupId -All | Select-Object -ExpandProperty Id
$windows11GroupMembers = Get-MgGroupMember -GroupId $windows11GroupId -All | Select-Object -ExpandProperty Id
# Add devices to respective Entra ID groups
# Add Windows 11 devices to the Windows 11 group
foreach ($device in $windows11Devices) {
$deviceId = (Get-MgDevice -Filter "DeviceID eq '$($device.AzureAdDeviceId)'").id
if ($windows11GroupMembers -notcontains $deviceId) {
try {
New-MgGroupMember -GroupId $windows11GroupId -DirectoryObjectId $deviceId
Write-Warning "Added $($device.DeviceName) to Windows 11 group."
}
catch {
Write-Error "Failed to add $($device.DeviceName) to Windows 11 group: $_"
}
}
else {
Write-Output "$($device.DeviceName) is already a member of the Windows 11 group."
}
}
# Loop through Windows 10 devices and check if it is not part of the Win10 or Win11 group, then add them to the Windows 10 group
# If the device is part of the Windows 11 group, remove it from the Windows 10 group.
# This can happen if devices are moved manually between groups in Entra ID to do autopatching.
foreach ($device in $windows10Devices) {
$deviceId = (Get-MgDevice -Filter "DeviceID eq '$($device.AzureAdDeviceId)'").id
if ($windows10GroupMembers -notcontains $deviceId -and $windows11GroupMembers -notcontains $deviceId) {
try {
New-MgGroupMember -GroupId $windows10GroupId -DirectoryObjectId $deviceId
Write-Warning "Added $($device.DeviceName) to Windows 10 group."
}
catch {
Write-Error "Failed to add $($device.DeviceName) to Windows 10 group: $_"
}
}
elseif ($windows11GroupMembers -contains $deviceId -and $windows10GroupMembers -contains $deviceId) {
try {
write-host $deviceId
Remove-MgGroupMemberByRef -GroupId $windows10GroupId -DirectoryObjectId $deviceId
Write-Warning "Removed $($device.DeviceName) from Windows 10 group as it is a member of the Windows 11 group."
}
catch {
Write-Error "Failed to remove $($device.DeviceName) from Windows 10 group: $_"
}
}
else {
Write-Output "$($device.DeviceName) is already a member of the Windows 10 or Windows 11 group."
}
}
PowerShellWith the script in place, the runbook can be saved, published, and started.
The script will provide logs for all changes in the AutoPatch onboarding groups. I will link a schedule to this runbook to ensure it runs automatically.
Validating the Vision: Smooth Windows 11 Upgrade
With all the configuration and the vital script, it’s time to test the water. Do I have a smooth Windows 11 upgrade journey ahead?
1 – Onboarding A Windows 10 Test Device
Device onboarding is the first step in introducing a device into management. I will use Windows Autopilot to onboard (1) a Windows 10 21H1 pilot device.
This is a straightforward Autopilot process, but I will include the screenshots just for reference. As always, click the picture for a more detailed view.
Once this is finished, I will have an Entra ID joined Windows 10 21H1 device managed by Intune.
2 – Runbook Onboarding To Autopatch Group
With this new device onboarded, the idea is for the Rubook (2) to catch up on the device and add it to the Windows 10 Autopatch onboarding group.
Looking in the log of the running job, I find information of the device being added to the group.
Looking at the Windows Autopatch devices in Intune, the new device will now appear as onboarded.
I can speed up the process by clicking the Discover Devices button.
3 – Autopatch Biting On The Device
Looking at the device, I can now see how Autopatch (3) is biting on this device. Initially a Windows 10 21H1 device. The new managed feature update is downloaded and installed behind the scenes before the end user is requested to reboot. (Still, clicking the pictures brings the details closer)
The screenshots show that the feature update runs automatically without the end user’s need to interact.
Most of the windows above are opened just for reference as screenshots in this blog. The end user typically gets the popup saying it’s time to reboot. The routine would be the same if the device onboarded were running Windows 11. The Runbook would then add it to the Windows 11 Autopatch group, and the device would be patched accordingly.
4 – Manually Move The Device To Autopatch Group For Windows 11
After a successful Autopatch pilot run to keep Windows 10 and Windows 11 up to date, it’s time to manually upgrade a Windows 10 device to Windows 11 by moving (4) it to the onboarding group for the Windows 11 Autopatch Group.
Nothing fancy about this. I am just adding the device to the Entra ID Security group.
If I don’t remove it from the Windows 10 group, my automation job running on the schedule in the Azure Runbook will remove the device from the Windows 10 group upon the its next run—and it will also leave the device in the Windows 11 group despite the fact that it is still running Windows 10.
I can now force the discovery of Windows Autopatch devices (or wait for this to happen automatically). This verifies that the device is moved over to the Windows 11 Autopatch group and dynamically assigned a ring.
I will move my device to the Test ring to speed up my test.
This process assigns the device to a specific ring, and at the same time, the other dynamically assigned devices are recalculated to respect the specific percentage configuration for each ring.
5 – Verify Windows 11 Is Coming Down
With the device now in the Windows 11 Autopatch group (5), it’s time to verify the configuration bites on the device for a smooth Windows 11 upgrade.
As mentioned before, this mostly happens behind the scenes. However, we can sneak a peek at what’s going on. The transparent process can look like this, and clicking the pictures brings the details to you.
Windows 11 is downloaded and installed all in the background before the user gets notified to update and restart. When the device is back up, it is running Windows 11.
Autopatch Reporting
A handful of pilot devices can be managed well, but for larger environments, the new Intune reports for Windows Autopatch are of great support.
If I jump to Reports—Windows Autopatch—Windows feature updates, I will find a nice overview of the status per Autopatch group.
I can even expand each group and see the status for the phases/rings in the Autopatch group.
Clicking one of the phases/rings, or any of the numbers brings me to the detailed device report with feature update status.
Using these views combined with filtering, searching and selective use of columns give pretty good insights into the Autopatch device health.
Here is one example of my Windows 10 pilot device found in the report when it was running Windows 10 21H1. It clearly states that it is “out of servicing” at the current state, and it is targeted for Windows 10 22H2 by Windows Autopatch.
After the update, the device is listed in a more healthy state in the report.
Looking at Devices – Windows – Windows Autopatch – Devices, I will find an overview of all onboarded devices. Using the filter function, I can quickly get an overview of which devices are onboarded to which Autopatch groups and what deployment ring they have been dynamically assigned.
While looking at these data, you might find it weird that none of these devices assigned to the Windows 11 22H2 Autopatch group are listed within their corresponding phases in the Windows Feature Updates report.
This is simply because these phases still haven’t started. They are in a scheduled status.
The report will list the devices once these phases/rings become active and work on my smooth Windows 11 upgrade. As seen below, phase 2 of the Windows 11 22H2 feature update has become active (1). This will also give a new figure in the graphs (2) where devices in progress of running the Windows 11 update is marked. This graph does, however, not show how each feature update is evolving in the environment. For that view, I will use Log Analytics.
Also, pay attention to the differences between phases and rings.
Rings are defined in the Autopatch group, while the phases are defined in the Feature Update release. A phase is made of one or more Autopatch group deployment rings.
Log Analytics
In addition to the Autopatch reports, I can use advanced hunting with KQL in Log Analytics to find information and statistics on the Windows feature level.
Timechart
One exciting query could be to get a time chart of the development of the Windows versions over a given period. The following KQL query will provide insights on the reported Windows versions over a defined period.
IntuneDevices
| where OS == "Windows"
| where TimeGenerated >= ago(30d) // Look back 60 days
| extend OSName = case(
OSVersion startswith "10.0.19045", "Win 10 22H2",
OSVersion startswith "10.0.19044", "Win 10 21H2",
OSVersion startswith "10.0.19043", "Win 10 21H1",
OSVersion startswith "10.0.19042", "Win 10 20H2",
OSVersion startswith "10.0.19041", "Win 10 2004",
OSVersion startswith "10.0.18363", "Win 10 19H2",
OSVersion startswith "10.0.17763", "Win 10 1809",
OSVersion startswith "10.0.17134", "Win 10 1804",
OSVersion startswith "10.0.22000", "Win 11 21H2",
OSVersion startswith "10.0.22621", "Win 11 22H2",
OSVersion startswith "10.0.22631", "Win 11 23H2",
OSVersion startswith "10.0.26100", "Win 11 24H2",
"Other/Unknown" // Default case if OSVersion doesn't match the known builds
)
| summarize UniqueDeviceCount = dcount(DeviceId) by bin(TimeGenerated, 1d), OSName // Count unique devices by day and OSName
| order by TimeGenerated asc, OSName asc
| render timechart
KustoRunning the code as seen above will create a graph like the following. The lines will indicate where Windows Autopatch is being added to the environment with new feature updates, and I can see how each Windows version is evolving in my environment over time.
As the lines show, some Windows features are growing while others are shrinking, meaning devices are effectively autopatched.
Depending on the retention settings for your Log Analytics workspace, you can change the date range of the query and look even further back in time. The following is a 180-day view from an environment where Windows Autopatch is starting to bite recently.
As the graph shows, it is actively patching devices, and the numbers will move between the feature releases as soon as users reboot their devices.
The following query can provide a more detailed view of all the different OS versions.
IntuneDevices
| where OS == "Windows"
| where TimeGenerated >= ago(180d) // Look back 60 days
| summarize UniqueDeviceCount = dcount(DeviceId) by bin(TimeGenerated, 1d), OSVersion // Count unique devices by day and OSName
| order by TimeGenerated asc, OSVersion asc
| render timechart
KustoThis shows the waves of patching through the environment.
Hovering the lines or the legend will focus on that development. This graph also reveals some builds that do not seem to be effectively patched, as they persist over time.
Stacked Area Chart
An alternative visualization of the development over time is a stacked area chart showing the percentages of devices running the different operating system variants.
let LookBackPeriod = 90d; // Days in the period to look back
IntuneDevices
| where OS == "Windows"
| where TimeGenerated >= ago(LookBackPeriod) // Variable of days in period
| extend OSName = case(
OSVersion startswith "10.0.19045", "Win 10 22H2",
OSVersion startswith "10.0.19044", "Win 10 21H2",
OSVersion startswith "10.0.19043", "Win 10 21H1",
OSVersion startswith "10.0.19042", "Win 10 20H2",
OSVersion startswith "10.0.19041", "Win 10 2004",
OSVersion startswith "10.0.18363", "Win 10 19H2",
OSVersion startswith "10.0.17763", "Win 10 1809",
OSVersion startswith "10.0.17134", "Win 10 1804",
OSVersion startswith "10.0.22000", "Win 11 21H2",
OSVersion startswith "10.0.22621", "Win 11 22H2",
OSVersion startswith "10.0.22631", "Win 11 23H2",
OSVersion startswith "10.0.26100", "Win 11 24H2",
"Other/Unknown" // Default case if OSVersion doesn't match the known builds
)
| summarize UniqueDeviceCount = dcount(DeviceId) by bin(TimeGenerated, 1d), OSName // Count unique devices by day and OSName
| summarize TotalDevices = sum(UniqueDeviceCount) by TimeGenerated // Calculate the total number of devices for each day
| join kind=inner (
IntuneDevices
| where OS == "Windows"
| where TimeGenerated >= ago(LookBackPeriod) // Variable of days in period
| extend OSName = case(
OSVersion startswith "10.0.19045", "Win 10 22H2",
OSVersion startswith "10.0.19044", "Win 10 21H2",
OSVersion startswith "10.0.19043", "Win 10 21H1",
OSVersion startswith "10.0.19042", "Win 10 20H2",
OSVersion startswith "10.0.19041", "Win 10 2004",
OSVersion startswith "10.0.18363", "Win 10 19H2",
OSVersion startswith "10.0.17763", "Win 10 1809",
OSVersion startswith "10.0.17134", "Win 10 1804",
OSVersion startswith "10.0.22000", "Win 11 21H2",
OSVersion startswith "10.0.22621", "Win 11 22H2",
OSVersion startswith "10.0.22631", "Win 11 23H2",
OSVersion startswith "10.0.26100", "Win 11 24H2",
"Other/Unknown" // Default case if OSVersion doesn't match the known builds
)
| summarize UniqueDeviceCount = dcount(DeviceId) by bin(TimeGenerated, 1d), OSName // Count unique devices by day and OSName
) on TimeGenerated // Join on TimeGenerated to get daily counts by OSName
| project TimeGenerated, OSName, Percent = 100.0 * UniqueDeviceCount / TotalDevices // Calculate percentage for each OS type
| render areachart kind=stacked // Use stacked area chart to show distribution
KustoThe code above will give a stacked area chart looking like this:
This clearly shows the percentage distribution between the Windows feature updates within your environment.
I also found it interesting that this view shows the actual number of devices instead of the percentage.
let LookBackPeriod = 180d; // Days in the period to look back
IntuneDevices
| where OS == "Windows"
| where TimeGenerated >= ago(LookBackPeriod) // Variable of days in period
| extend OSName = case(
OSVersion startswith "10.0.19045", "Win 10 22H2",
OSVersion startswith "10.0.19044", "Win 10 21H2",
OSVersion startswith "10.0.19043", "Win 10 21H1",
OSVersion startswith "10.0.19042", "Win 10 20H2",
OSVersion startswith "10.0.19041", "Win 10 2004",
OSVersion startswith "10.0.18363", "Win 10 19H2",
OSVersion startswith "10.0.17763", "Win 10 1809",
OSVersion startswith "10.0.17134", "Win 10 1804",
OSVersion startswith "10.0.22000", "Win 11 21H2",
OSVersion startswith "10.0.22621", "Win 11 22H2",
OSVersion startswith "10.0.22631", "Win 11 23H2",
OSVersion startswith "10.0.26100", "Win 11 24H2",
"Other/Unknown" // Default case if OSVersion doesn't match the known builds
)
| summarize UniqueDeviceCount = dcount(DeviceId) by bin(TimeGenerated, 1d), OSName // Count unique devices by day and OSName
| render areachart kind=stacked // Visualize as stacked area chart
PowerShellThis will now look like this in an environment where Windows Autopatch started apply feature updates:
An alternative take is focusing on the distribution between Windows 10 and 11 within your devices.
let LookBackPeriod = 180d; // Days in the period to look back
IntuneDevices
| where OS == "Windows"
| where TimeGenerated >= ago(LookBackPeriod) // Variable of days in period
| extend OSName = case(
OSVersion startswith "10.0.1", "Win 10",
OSVersion startswith "10.0.2", "Win 11",
"Other/Unknown" // Default case if OSVersion doesn't match the known builds
)
| summarize UniqueDeviceCount = dcount(DeviceId) by bin(TimeGenerated, 1d), OSName // Count unique devices by day and OSName
| summarize TotalDevices = sum(UniqueDeviceCount) by TimeGenerated // Calculate the total number of devices for each day
| join kind=inner (
IntuneDevices
| where OS == "Windows"
| where TimeGenerated >= ago(LookBackPeriod) // Variable of days in period
| extend OSName = case(
OSVersion startswith "10.0.1", "Win 10",
OSVersion startswith "10.0.2", "Win 11",
"Other/Unknown" // Default case if OSVersion doesn't match the known builds
)
| summarize UniqueDeviceCount = dcount(DeviceId) by bin(TimeGenerated, 1d), OSName // Count unique devices by day and OSName
) on TimeGenerated // Join on TimeGenerated to get daily counts by OSName
| project TimeGenerated, OSName, Percent = 100.0 * UniqueDeviceCount / TotalDevices // Calculate percentage for each OS type
| render areachart kind=stacked // Use stacked area chart to show distribution
KustoThis code will give you the distribution between Windows 10 and 11 over the defined period.
Make sure to have a solid color graph for Windows 11 by October 2025, as Windows 10 will be out of support by then!
Barchart
An alternative approach is to run a KQL query that takes the latest Windows version for each Windows device and creates a bar chart of the different active Windows feature packs running in my environment.
IntuneDevices
| where OS == "Windows"
| where TimeGenerated >= ago(60d) // Look back 60 days
| summarize arg_max(TimeGenerated, OSVersion) by DeviceId // Get the latest OSVersion for each DeviceId
| extend OSName = case(
OSVersion startswith "10.0.19045", "Win 10 22H2",
OSVersion startswith "10.0.19044", "Win 10 21H2",
OSVersion startswith "10.0.19043", "Win 10 21H1",
OSVersion startswith "10.0.19042", "Win 10 20H2",
OSVersion startswith "10.0.19041", "Win 10 2004",
OSVersion startswith "10.0.18363", "Win 10 19H2",
OSVersion startswith "10.0.17763", "Win 10 1809",
OSVersion startswith "10.0.17134", "Win 10 1804",
OSVersion startswith "10.0.22000", "Win 11 21H2",
OSVersion startswith "10.0.22621", "Win 11 22H2",
OSVersion startswith "10.0.22631", "Win 11 23H2",
OSVersion startswith "10.0.26100", "Win 11 24H2",
"Other/Unknown" // Default case if OSVersion doesn't match the known builds
)
| summarize UniqueDeviceCount = dcount(DeviceId) by OSName // Count unique devices for each OSName
| order by OSName asc, UniqueDeviceCount desc
| render barchart
KustoThe result from the Kusto Query (KQL) above could be like this:
It is also possible to look at the Results tab to get the facts of the numbers.
KQL queries like these can supplement the native Windows Autopatch reports in Intune, supporting a smooth Windows 11 upgrade journey.
List 24H2 Devices
If you jump to Windows 11 24H2, the following KQL will give you a list of devices that have already been updated.
IntuneDevices
| where TimeGenerated >= ago(30d)
| where OSVersion startswith "10.0.26100"
| distinct DeviceName, OSVersion, Manufacturer, Model, UserName, JoinType, ManagedBy
KustoIf you don’t have a Feature Update policy established in Intune, your devices may automatically upgrade to Windows 11 24H2 without control. The query above will help you determine if this has occurred.
AutoPatch Troubleshooting
Along the route, you might encounter issues that require troubleshooting and fixing.
Need Attention
If you filter on the status “Need attention,” these issues might be visible in the Windows Autopatch Devices blade.
This blade will not give you details about the issue on these devices.
Not Ready (Preview)
Looking at the “Not ready” blade, which is currently in preview, I can get more information about the challenges for each device.
In the examples above, I see two different types of challenges.
Device Status: Inactive
One of the device statuses I see is the Inactive status.
This indicates that the device has been offline for over 28 days, meaning Windows AutoPatch has not contacted it.
Device Status: Need attention
The device status “Need attention” contains some more data about the problem in the message that appears when clicking on the device.
The example above is from a conflicting configuration in the registry key SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU\NoAutoUpdate
.
Registry Setting Blocking Windows Auto Update
Some administrators previously implemented the NoAutoUpdate
policy to suppress repeated Windows Update notifications on Windows 7 systems. While this approach has mostly fallen out of use, I still come across devices at client sites where this registry key remains embedded in older configurations. MSEndpointMGR also mentions this in their blog post, Windows Update Settings Compliance.
To deal with this, I have created an Intune Remediation available from my GitHub.
<#
.NOTES
Created on: 07.10.2024
Created by: Simon Skotheimsvik
Filename: Autopatch-NoAutoUpdate-Detection.ps1
Info: https://skotheimsvik.no
Version: 1.0.1
.DESCRIPTION
This detection script checks if the registry key exists and holds a different value than expected.
#>
$RegContent = @"
RegKeyPath,Key,Value,Type
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU","NoAutoUpdate","0","Dword"
"@
$RegData = $RegContent | ConvertFrom-Csv -delimiter ","
foreach ($Reg in $RegData) {
# Suppress errors for non-existent registry paths using -ErrorAction SilentlyContinue
$Item = Get-Item -Path $($Reg.RegKeyPath) -ErrorAction SilentlyContinue
if ($Item) {
try {
# Try to get the value for the specified key
$ExistingValue = $Item.GetValue($($Reg.Key), $null) # Returns $null if the key does not exist
if ($ExistingValue -ne $null) {
# Check if the existing value matches the expected value
if ($ExistingValue -ne $($Reg.Value)) {
Write-Host "$($Reg.Key) exist as $($ExistingValue), and is not equal to $($Reg.Value)."
Exit 1
}
else {
# Write-Host $($Reg.Key) "Equal"
}
}
}
catch {
# Do nothing if GetValue encounters an error
}
}
}
Exit 0
PowerShellRemediation:
<#
.NOTES
Created on: 07.10.2024
Created by: Simon Skotheimsvik
Filename: Autopatch-NoAutoUpdate-Remediation.ps1
Info: https://skotheimsvik.no
Version: 1.0.5
.DESCRIPTION
This remediation script checks if the registry key exists and holds a different value than expected. If the value is different, the script will clean up the registry key.
#>
$RegKeyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
$RegKey = "NoAutoUpdate"
$ExpectedValue = 0
try {
if (Test-Path -Path $RegKeyPath) {
try {
$ExistingValue = Get-ItemPropertyValue -Path $RegKeyPath -Name $RegKey -ErrorAction SilentlyContinue
if ($ExistingValue -eq $null) {
Write-Host "Registry key does not exist. No remediation needed."
Exit 0 # No changes required
} elseif ($ExistingValue -ne $ExpectedValue) {
# Stop the Windows Update service
Stop-Service -Name wuauserv -Force
Set-ItemProperty -Path $RegKeyPath -Name $RegKey -Value $ExpectedValue -ErrorAction SilentlyContinue
Write-Host "Set key: $RegKey in path $RegKeyPath to the expected value $ExpectedValue."
# Start the Windows Update service
Start-Service -Name wuauserv
Exit 1 # Remediation successful
} else {
Write-Host "$($RegKey) in path $($RegKeyPath) equals $($ExistingValue). No remediation needed."
Exit 0 # No changes required
}
} catch {
Write-Host "$_. No remediation needed."
Exit 0 # No changes required
}
} else {
Write-Host "Registry path does not exist. No remediation needed."
Exit 0 # No changes required
}
} catch {
Write-Host "Error accessing registry path ${RegKeyPath}: $_"
Exit 1 # Error occurred
}
PowerShellThis can be added and assigned to the group “Windows Autopatch – Devices All”. The following pictures show the implementation.
Once these settings are remediated on the devices, the Autopatch service should start biting the devices.
Alerts
Looking in the Windows Feature Updates report, I can also find alerts leading to details about the device challenges.
These alerts might give insights into expectations for self-healing or actions needed to be performed on the devices, like freeing up storage space, for instance.
Wrapping Up The Smooth Windows 11 Upgrade
Windows Autopatch is the new secret sauce to ensure your devices are patched at pace. As the Windows 11 migration deadline approaches, upgrading soon is vital for better security, performance, and user experience. Upgrading ensures your systems stay supported and secure, avoiding vulnerabilities and compliance issues as Microsoft phases out older versions.
Windows Autopatch automates updates for you, reducing administrative burden and downtime while keeping systems up-to-date and secure. I hope my automation routine helps you onboard to Windows Autopatch, maintains control over device migration from Windows 10 to Windows 11, and allows your IT team to focus on strategic initiatives.
Combining Windows Autopatch with Azure Runbook and PowerShell scripting simplifies upgrading and maintaining devices at scale. This ensures timely patches without manual interventions for a smooth Windows 11 upgrade throughout the device’s lifetime.
Time to make the leap! Windows 11 24H2 is here, and the Windows 10 farewell party isn’t until October 14, 2025—plenty of time to say your goodbyes before the retirement home calls.
Be First to Comment