{"id":5048,"date":"2025-05-24T11:46:22","date_gmt":"2025-05-24T10:46:22","guid":{"rendered":"https:\/\/mortenknudsen.net\/?p=5048"},"modified":"2025-06-06T16:32:32","modified_gmt":"2025-06-06T15:32:32","slug":"troubleshooting-autopatch-wufb-feature-update-not-deploying","status":"publish","type":"post","link":"https:\/\/mortenknudsen.net\/?p=5048","title":{"rendered":"Troubleshooting Windows Feature Updates not being deploying using Intune\/AutoPatch\/WUfB"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">I have been banging my head why some machines wouldn&#8217;t deploy Windows 11 Windows Feature Updates (24H2) as part of Intune AutoPatch \/ WUfB.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I have seen many reasons, both related to Windows 11 24H2 requirements like TPM which wasn&#8217;t enabled, diskspace issues, client issues &#8211; but lastly also backend issues, where WUfB enrollment state was &#8220;stuck&#8221; in Offering\/Enrolling-state. <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"has-large-font-size wp-block-paragraph\" id=\"Workaround\"><strong>June 2025 Workaround (FINALLY!) &#8211; Update stack remains stuck in a local scan state and doesn&#8217;t ask for deployments in WUFB related to Feature Updates<\/strong><\/p>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\"><strong>Issue:  <\/strong>Microsoft support has informed me, that in sporadic occurrences, there is a bug, where the Update stack remains stuck in a local scan state and doesn&#8217;t ask for deployments in WUFB related to Feature Updates.<\/p>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\"><strong>Problem: <\/strong> Some devices that are running Windows 10 or Windows 11, version 19H1 and above,\u00a0aren&#8217;t able to install monthly security updates because of file or metadata\u00a0corruption within the servicing stack. These devices may not be able to become up to date without a Feature Update.\u00a0<\/p>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\"><strong>Solution\/Workaround:  <\/strong>Microsoft is working on an fix, which will be included in Autopatch at some point this year, but until then we can fix it manually by added the below mentioned command on the client.<\/p>\n\n\n\n<pre class=\"wp-block-code has-medium-font-size\"><code>(Win11 23H2\/24H2 only)\nreg.exe Add HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion \/v AllowInplaceUpgrade \/t REG_DWORD \/f \/d 4\n\n(Win10 22H2\/Win11 22H2 only)\nReg.exe Add HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion \/v UpgradeEligible \/t REG_DWORD \/f \/d 1\n\nNote: \n* This registry value will be removed once the in-place upgrade is complete. \n* It can take up to 48 hours for the in-place upgrade to be offered to the device.\n* After the in-place upgrade, the device will be able to take new updates normally\n* Don't confuse with the same regkey AllowInplaceUpgrade which normally resides in HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate. For this parameter to work with value = 4, it must be set in HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"963\" height=\"356\" src=\"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-24.png\" alt=\"\" class=\"wp-image-5186\" srcset=\"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-24.png 963w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-24-300x111.png 300w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-24-768x284.png 768w\" sizes=\"auto, (max-width: 963px) 100vw, 963px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\">In a rather cryptic way, this is also covered in this <a href=\"https:\/\/support.microsoft.com\/en-au\/topic\/windows-update-troubleshooter-for-devices-experiencing-recurring-issues-installing-monthly-security-updates-dbf23d6f-086d-4caf-a6d5-92a1afc503bb\" data-type=\"link\" data-id=\"https:\/\/support.microsoft.com\/en-au\/topic\/windows-update-troubleshooter-for-devices-experiencing-recurring-issues-installing-monthly-security-updates-dbf23d6f-086d-4caf-a6d5-92a1afc503bb\">Microsoft article<\/a>, even though the above reg-keys and values are not mentioned. <\/p>\n\n\n\n<ul class=\"wp-block-list\"><\/ul>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"block-d8faf7bb-89bc-4b62-b861-1ef6278e9f01\">This blog contains the scripts I used to solve this &#8211; both related to backend-issues like device is stuck in &#8216;Offering&#8217; state &#8211; and also endpoint-related issues.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"block-d8faf7bb-89bc-4b62-b861-1ef6278e9f01\">Microsoft has also released a troubleshooting guide, which you can find <a href=\"https:\/\/techcommunity.microsoft.com\/blog\/intunecustomersuccess\/troubleshooting-windows-feature-updates-in-microsoft-intune\/4401828\" data-type=\"link\" data-id=\"https:\/\/techcommunity.microsoft.com\/blog\/intunecustomersuccess\/troubleshooting-windows-feature-updates-in-microsoft-intune\/4401828\">here<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;m using the Microsoft Graph API endpoint mentioned in the Microsoft guide (&#8220;https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets&#8221;). You can see link in each section below to the Graph documentation. <a href=\"https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-unenrollassets?view=graph-rest-beta&amp;tabs=http\" data-type=\"link\" data-id=\"https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-unenrollassets?view=graph-rest-beta&amp;tabs=http\">Click here<\/a> to access the documentation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As always, be patience when you make backend changes (I have seen from a few hours to up to 72 hours).<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"437\" height=\"156\" src=\"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/enrollment.jpg\" alt=\"\" class=\"wp-image-5086\" srcset=\"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/enrollment.jpg 437w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/enrollment-300x107.jpg 300w\" sizes=\"auto, (max-width: 437px) 100vw, 437px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"103\" src=\"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-3-1024x103.png\" alt=\"\" class=\"wp-image-5052\" srcset=\"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-3-1024x103.png 1024w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-3-300x30.png 300w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-3-768x77.png 768w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-3.png 1482w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Intune\/AutoPatch\/WUfB troubleshooting<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"#auth\" data-type=\"internal\" data-id=\"#auth\">Authentication Microsoft Graph (prereq)<\/a><\/li>\n\n\n\n<li><a href=\"#overview\" data-type=\"internal\" data-id=\"#overview\">Intune: Get overview of enrollment status in AutoPatch\/WUfB<\/a><\/li>\n\n\n\n<li><a href=\"#unenroll-notenrolled-fp\" data-type=\"internal\" data-id=\"#unenroll-notenrolled-fp\">Intune: Force Un-enroll Windows Client devices from WUfB AutoPatch, where state is NOT &#8216;Enrolled&#8217; (Feature Updates only)<\/a><\/li>\n\n\n\n<li><a href=\"#force-delete-stuck\" data-type=\"internal\" data-id=\"#force-delete-stuck\">Intune: Force Delete All Windows Client devices from WUfB AutoPatch, where Feature Update enrollment state is stuck in &#8216;enrolling&#8217; or &#8216;unenrolling&#8217;<\/a><\/li>\n\n\n\n<li><a href=\"#enroll-notenrolled\" data-type=\"internal\" data-id=\"#enroll-notenrolled\">Intune: Enroll Windows Client devices into WUfB AutoPatch with &#8216;NotEnrolled&#8217; state (Feature Updates only)<\/a><\/li>\n\n\n\n<li><a href=\"#force-unenroll-fp\" data-type=\"internal\" data-id=\"#force-unenroll-fp\">Intune: Force Un-enroll Windows Client devices from WUfB AutoPatch (Feature Updates only)<\/a><\/li>\n\n\n\n<li><a href=\"#delete-all\" data-type=\"internal\" data-id=\"#delete-all\">Intune: Force Delete All Windows Client devices from WUfB AutoPatch (Only use, if you want complete reset !!!)<\/a><\/li>\n\n\n\n<li><a href=\"#enroll-all-quality\" data-type=\"internal\" data-id=\"#enroll-all-quality\">Intune: Enroll All Windows Client devices into WUfB AutoPatch (Quality Updates only)<\/a><\/li>\n\n\n\n<li><a href=\"#enroll-all-driver\" data-type=\"internal\" data-id=\"#enroll-all-driver\">Intune: Enroll All Windows Client devices into WUfB AutoPatch (Driver Updates only)<\/a><\/li>\n\n\n\n<li><a href=\"#enroll-quality-notenrolled\" data-type=\"internal\" data-id=\"#enroll-quality-notenrolled\">Intune: Enroll Windows Client devices into WUfB AutoPatch with &#8216;NotEnrolled&#8217; state (Quality Updates only)<\/a><\/li>\n\n\n\n<li><a href=\"#enroll-notenrolled-driver\" data-type=\"internal\" data-id=\"#enroll-notenrolled-driver\">Intune: Enroll Windows Client devices into WUfB AutoPatch with &#8216;NotEnrolled&#8217; state (Driver Updates only)<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Windows Client troubleshooting<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"#endpoint-troubleshoot\" data-type=\"internal\" data-id=\"#endpoint-troubleshoot\">Endpoint: Windows client troubleshooting<\/a><\/li>\n\n\n\n<li><a href=\"#stuckupdateworkaround\" data-type=\"internal\" data-id=\"#stuckupdateworkaround\">Workaround for Stuck Update Stack (client-fix)<\/a><\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"565\" src=\"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-5-1024x565.png\" alt=\"\" class=\"wp-image-5080\" srcset=\"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-5-1024x565.png 1024w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-5-300x166.png 300w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-5-768x424.png 768w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-5.png 1187w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"stuckupdateworkaround\">Workaround for Stuck Update Stack<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I have done a support-case with Microsoft support, where they have concluded that in sporadic occurrences, there is a backend bug, where the Update stack remains stuck in a local scan state and doesn&#8217;t ask for deployments in WUFB related to Feature Updates. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cause for this issue is, when devices are un-enrolling and enrolling in WUfB and the Managed Update Session Orchestrator (MUSE) in some scenarios doesn&#8217;t handle this as intended.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Microsoft is working on an fix, which will be included in Autopatch at some point this year, but until then we can fix it manually by added the below mentioned command on the client. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I waited 48 hours and then Windows Feature Packs was offered on the impacted devices.<\/p>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code>reg.exe Add HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion \/v AllowInplaceUpgrade \/t REG_DWORD \/f \/d 4<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Note: <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>This registry value will be removed once the in-place upgrade is complete. <\/li>\n\n\n\n<li>It can take up to 48 hours for the in-place upgrade to be offered to the device.<\/li>\n\n\n\n<li>After the in-place upgrade, the device will be able to take new updates normally.<\/li>\n<\/ul>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Scripts (Github)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">All scripts are shown below, but you can also get them from my Github.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/raw.githubusercontent.com\/KnudsenMorten\/Intune-Windows-Update-Troubleshooting\/refs\/heads\/main\/Intune-Windows-Update-Backend-Troubleshoot.ps1\" data-type=\"link\" data-id=\"https:\/\/raw.githubusercontent.com\/KnudsenMorten\/Intune-Windows-Update-Troubleshooting\/refs\/heads\/main\/Intune-Windows-Update-Backend-Troubleshoot.ps1\">Intune-Windows-Update-Backend-Troubleshoot.ps1<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/raw.githubusercontent.com\/KnudsenMorten\/Intune-Windows-Update-Troubleshooting\/refs\/heads\/main\/Windows-Client-Troubleshooting-WU.ps1\" data-type=\"link\" data-id=\"https:\/\/raw.githubusercontent.com\/KnudsenMorten\/Intune-Windows-Update-Troubleshooting\/refs\/heads\/main\/Windows-Client-Troubleshooting-WU.ps1\">Windows-Client-Troubleshooting-WU.ps1<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"overview\">Intune: Get overview of enrollment status in AutoPatch\/WUfB<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Get Status about Windows Client devices from WUfB AutoPatch\n\nfunction Get-WUfBEnrollmentStatus {\n    &lt;#\n    .SYNOPSIS\n        Retrieves the WUfB AutoPatch enrollment status for Windows client devices managed by Intune (MDM).\n    \n    .PARAMETER ShowStatus\n        Optional. If specified, displays output during processing using Write-Host.\n\n    .OUTPUTS\n        &#91;PSCustomObject] array with DeviceName, DeviceId, EnrollmentStateFeature, EnrollmentStateQuality, EnrollmentStateDriver\n    #&gt;\n\n    param (\n        &#91;switch]$ShowStatus = $false\n    )\n\n    $StatusEnrollmentWUfB = @()\n\n    if ($ShowStatus) {\n        Write-Host \"\"\n        Write-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n    }\n\n    $devices = @()\n    $uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\n    do {\n        $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n        $devices += $response.value\n        $uri = $response.'@odata.nextLink'\n    } while ($uri)\n\n    $ScopedDevices = $devices | Where-Object {\n        $_.managementAgent -eq \"mdm\" -and $_.operatingSystem -like \"Windows*\"\n    }\n\n    if ($ShowStatus) {\n        Write-Host \"\"\n        Write-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n    }\n\n    foreach ($device in $ScopedDevices) {\n        $deviceName = $device.deviceName\n        $deviceId = $device.azureADDeviceId\n\n        if ($ShowStatus) {\n            Write-Host \"\"\n            Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n        }\n\n        $enrollmentStateFeature = $null\n        $enrollmentStateQuality = $null\n        $enrollmentStateDriver = $null\n        $errorsWUfB = 0\n\n        try {\n            $wuAsset = Invoke-MgGraphRequest -Method GET -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/$deviceId\"\n            $enrollmentStateFeature = $wuAsset.enrollment.feature.enrollmentState\n            $enrollmentStateQuality = $wuAsset.enrollment.quality.enrollmentState\n            $enrollmentStateDriver  = $wuAsset.enrollment.driver.enrollmentState\n            $errorsWUfB = if ($wuAsset.errors) { $wuAsset.errors.Count } else { 0 }\n\n            if ($ShowStatus) {\n                Write-Host \"\ud83d\udccb Enrollment State Feature: $enrollmentStateFeature\"\n                Write-Host \"\ud83d\udccb Enrollment State Quality: $enrollmentStateQuality\"\n                Write-Host \"\ud83d\udccb Enrollment State Driver : $enrollmentStateDriver\"\n                Write-Host \"\ud83d\udccb Errors (WUfB)           : $errorsWUfB\"\n            }\n\n            $StatusObj = &#91;PSCustomObject]@{\n                DeviceName             = $deviceName\n                DeviceId               = $deviceId\n                EnrollmentStateFeature = $enrollmentStateFeature\n                EnrollmentStateQuality = $enrollmentStateQuality\n                EnrollmentStateDriver  = $enrollmentStateDriver\n                ErrorsWUfB             = $errorsWUfB\n            }\n\n            $StatusEnrollmentWUfB += $StatusObj\n        }\n        catch {\n            if ($_.Exception.Response.StatusCode.value__ -eq 404) {\n                if ($ShowOut) {\n                    Write-Host \"\u2139\ufe0f Device not found in WUfB AutoPatch\"\n                }\n            } else {\n                if ($ShowStatus) {\n                    Write-Host \"\u274c Failed to process $deviceName ($deviceId): $($_.Exception.Message)\"\n                }\n            }\n            continue\n        }\n    }\n\n    return $StatusEnrollmentWUfB\n}\n\n<strong># Call the function and save results\n$WUStatus = Get-WUfBEnrollmentStatus -ShowStatus\n\n# Optionally view as a table\n$WUStatus | Sort-Object EnrollmentStateFeature, EnrollmentStateQuality, EnrollmentStateDriver -Descending | Format-Table -AutoSize<\/strong><\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Call the function and save results\n$WUStatus = Get-WUfBEnrollmentStatus -ShowStatus\n\n# Optionally view as a table\n$WUStatus | Sort-Object EnrollmentStateFeature, EnrollmentStateQuality, EnrollmentStateDriver -Descending | Format-Table -AutoSize<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"auth\">Authentication (Microsoft Graph)<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># First, ensure you're connected to Graph with the right scopes:\nConnect-MgGraph -Scopes \"DeviceManagementManagedDevices.ReadWrite.All\", \"WindowsUpdates.ReadWrite.All\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"unenroll-notenrolled-fp\">Intune: Force Un-enroll Windows Client devices from WUfB AutoPatch, where state is NOT &#8216;Enrolled&#8217; (Feature Updates only)<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Force Un-enroll Windows Client devices from WUfB AutoPatch, where state is NOT 'Enrolled' (Feature Updates only)\n\n# https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-unenrollassets?view=graph-rest-beta&amp;tabs=http\n\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n\n$devices = @()\n$uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\ndo {\n    $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n    $devices += $response.value\n    $uri = $response.'@odata.nextLink'\n} while ($uri)\n\nwrite-host \"Get enrollment status for devices ... Pleae Wait !\"\n$StatusEnrollmentWUfB = Get-WUfBEnrollmentStatus\n\n# Build a lookup dictionary from enrollment status for fast access\n$StatusLookup = @{}\nforeach ($entry in $StatusEnrollmentWUfB) {\n    $StatusLookup&#91;$entry.DeviceId] = $entry\n}\n\n# Filter: MDM-managed Windows devices that are NOT enrolled for 'feature' updates\n$ScopedDevices = $devices | Where-Object {\n    $_.managementAgent -eq \"mdm\" -and\n    $_.operatingSystem -like \"Windows*\" -and\n    $StatusLookup.ContainsKey($_.azureADDeviceId) -and\n    $StatusLookup&#91;$_.azureADDeviceId].EnrollmentStateFeature -ne \"enrolled\"\n}\n\nWrite-Host \"\"\nWrite-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n\n# === LOOP THROUGH DEVICES ===\nforeach ($device in $ScopedDevices) {\n    $deviceName = $device.deviceName\n    $deviceId = $device.azureADDeviceId\n\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n\n    # Build JSON body for (un)enrollment\n    $body = @{\n        updateCategory = \"feature\"\n        assets = @(\n            @{\n                \"@odata.type\" = \"#microsoft.graph.windowsUpdates.azureADDevice\"\n                id = $deviceId\n            }\n        )\n    }\n    $jsonBody = $body | ConvertTo-Json -Depth 5\n\n    Invoke-MgGraphRequest -Method POST `\n        -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/unenrollAssets\" `\n        -Body $jsonBody `\n        -ContentType \"application\/json\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"force-delete-stuck\">Intune: Force Delete All Windows Client devices from WUfB AutoPatch, where Feature Update enrollment state is stuck in &#8216;enrolling&#8217; or &#8216;unenrolling&#8217;<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Force Delete All Windows Client devices from WUfB AutoPatch, where Feature Update enrollment state is stuck in 'enrolling' or 'unenrolling'\n\n# https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-delete?view=graph-rest-beta&amp;tabs=http\n\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n\n$devices = @()\n$uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\ndo {\n    $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n    $devices += $response.value\n    $uri = $response.'@odata.nextLink'\n} while ($uri)\n\n# Filter only Windows client devices managed by Intune\nwrite-host \"Get enrollment status for devices ... Please Wait !\"\n$StatusEnrollmentWUfB = Get-WUfBEnrollmentStatus\n\n# Build a lookup dictionary from enrollment status for fast access\n$StatusLookup = @{}\nforeach ($entry in $StatusEnrollmentWUfB) {\n    $StatusLookup&#91;$entry.DeviceId] = $entry\n}\n\n# Filter: MDM-managed Windows devices that are NOT enrolled for 'feature' updates\n$ScopedDevices = $devices | Where-Object {\n    $_.managementAgent -eq \"mdm\" -and\n    $_.operatingSystem -like \"Windows*\" -and\n    $StatusLookup.ContainsKey($_.azureADDeviceId) -and\n    $StatusLookup&#91;$_.azureADDeviceId].EnrollmentStateFeature -eq \"enrolling\" -or $StatusLookup&#91;$_.azureADDeviceId].EnrollmentStateFeature -eq \"unenrolling\"\n}\nWrite-Host \"\"\nWrite-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n\n# === LOOP THROUGH DEVICES ===\nforeach ($device in $ScopedDevices) {\n    $deviceName = $device.deviceName\n    $deviceId = $device.azureADDeviceId\n\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n\n    Invoke-MgGraphRequest -Method DELETE `\n        -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/$deviceId\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"enroll-fp\">Intune: Enroll Windows Client devices into WUfB AutoPatch (Feature Updates only) &#8211; will also include devices, that was deleted before (catch-up)<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Enroll Windows Client devices into WUfB AutoPatch (Feature Updates only) - will also include devices, that was deleted before (catch-up)\n\n# https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-enrollassets?view=graph-rest-beta&amp;tabs=http\n\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n\n$devices = @()\n$uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\ndo {\n    $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n    $devices += $response.value\n    $uri = $response.'@odata.nextLink'\n} while ($uri)\n\n# Filter only Windows client devices managed by Intune\n$ScopedDevices = $devices | Where-Object { $_.managementAgent -eq \"mdm\" -and $_.operatingSystem -like \"Windows*\"}\nWrite-Host \"\"\nWrite-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n\n# === LOOP THROUGH DEVICES ===\nforeach ($device in $ScopedDevices) {\n    $deviceName = $device.deviceName\n    $deviceId = $device.azureADDeviceId\n\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n\n    # Build JSON body for enrollment\n    $body = @{\n        updateCategory = \"feature\"\n        assets = @(\n            @{\n                \"@odata.type\" = \"#microsoft.graph.windowsUpdates.azureADDevice\"\n                id = $deviceId\n            }\n        )\n    }\n    $jsonBody = $body | ConvertTo-Json -Depth 5\n\n    Invoke-MgGraphRequest -Method POST `\n        -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/enrollAssets\" `\n        -Body $jsonBody `\n        -ContentType \"application\/json\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"enroll-notenrolled\">Intune: Enroll Windows Client devices into WUfB AutoPatch with &#8216;NotEnrolled&#8217; state (Feature Updates only)<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Enroll Windows Client devices into WUfB AutoPatch with 'NotEnrolled' state (Feature Updates only)\n\n# https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-enrollassets?view=graph-rest-beta&amp;tabs=http\n\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n\n$devices = @()\n$uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\ndo {\n    $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n    $devices += $response.value\n    $uri = $response.'@odata.nextLink'\n} while ($uri)\n\nwrite-host \"Get enrollment status for devices ... Please Wait !\"\n$StatusEnrollmentWUfB = Get-WUfBEnrollmentStatus\n\n# Build a lookup dictionary from enrollment status for fast access\n$StatusLookup = @{}\nforeach ($entry in $StatusEnrollmentWUfB) {\n    $StatusLookup&#91;$entry.DeviceId] = $entry\n}\n\n# Filter: MDM-managed Windows devices that are NOT enrolled for 'feature' updates\n$ScopedDevices = $devices | Where-Object {\n    $_.managementAgent -eq \"mdm\" -and\n    $_.operatingSystem -like \"Windows*\" -and\n    $StatusLookup.ContainsKey($_.azureADDeviceId) -and\n    $StatusLookup&#91;$_.azureADDeviceId].EnrollmentStateFeature -eq \"notEnrolled\"   # filter for 'NotEnrolled' state. We don't want to include 'unenrolling', but let them finish first\n}\nWrite-Host \"\"\nWrite-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n\n# === LOOP THROUGH DEVICES ===\nforeach ($device in $ScopedDevices) {\n    $deviceName = $device.deviceName\n    $deviceId = $device.azureADDeviceId\n\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n\n    #####################\n    # Feature Updates\n    #####################\n        # Build JSON body for enrollment\n        $body = @{\n            updateCategory = \"feature\"\n            assets = @(\n                @{\n                    \"@odata.type\" = \"#microsoft.graph.windowsUpdates.azureADDevice\"\n                    id = $deviceId\n                }\n            )\n        }\n        $jsonBody = $body | ConvertTo-Json -Depth 5\n\n        Invoke-MgGraphRequest -Method POST `\n            -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/enrollAssets\" `\n            -Body $jsonBody `\n            -ContentType \"application\/json\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"force-unenroll-fp\">Intune: Force Un-enroll Windows Client devices from WUfB AutoPatch (Feature Updates only)<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Force Un-enroll Windows Client devices from WUfB AutoPatch (Feature Updates only)\n\n# https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-unenrollassets?view=graph-rest-beta&amp;tabs=http\n\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n\n$devices = @()\n$uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\ndo {\n    $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n    $devices += $response.value\n    $uri = $response.'@odata.nextLink'\n} while ($uri)\n\n# Filter only Windows client devices managed by Intune\n$ScopedDevices = $devices | Where-Object { $_.managementAgent -eq \"mdm\" -and $_.operatingSystem -like \"Windows*\"}\nWrite-Host \"\"\nWrite-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n\n# === LOOP THROUGH DEVICES ===\nforeach ($device in $ScopedDevices) {\n    $deviceName = $device.deviceName\n    $deviceId = $device.azureADDeviceId\n\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n\n    # Build JSON body for (un)enrollment\n    $body = @{\n        updateCategory = \"feature\"\n        assets = @(\n            @{\n                \"@odata.type\" = \"#microsoft.graph.windowsUpdates.azureADDevice\"\n                id = $deviceId\n            }\n        )\n    }\n    $jsonBody = $body | ConvertTo-Json -Depth 5\n\n    Invoke-MgGraphRequest -Method POST `\n        -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/unenrollAssets\" `\n        -Body $jsonBody `\n        -ContentType \"application\/json\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"delete-all\">Intune: Force Delete All Windows Client devices from WUfB AutoPatch (Only use, if you want complete reset !!!)<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Force Delete All Windows Client devices from WUfB AutoPatch (Only use, if you want complete reset !!!)\n\n# https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-delete?view=graph-rest-beta&amp;tabs=http\n\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n\n$devices = @()\n$uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\ndo {\n    $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n    $devices += $response.value\n    $uri = $response.'@odata.nextLink'\n} while ($uri)\n\n# Filter only Windows client devices managed by Intune\n$ScopedDevices = $devices | Where-Object { $_.managementAgent -eq \"mdm\" -and $_.operatingSystem -like \"Windows*\"}\nWrite-Host \"\"\nWrite-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n\n# === LOOP THROUGH DEVICES ===\nforeach ($device in $ScopedDevices) {\n    $deviceName = $device.deviceName\n    $deviceId = $device.azureADDeviceId\n\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n\n    Invoke-MgGraphRequest -Method DELETE `\n        -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/$deviceId\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"enroll-all-quality\">Intune: Enroll All Windows Client devices into WUfB AutoPatch (Quality Updates only)<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Enroll All Windows Client devices into WUfB AutoPatch (Quality Updates only)\n\n# https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-enrollassets?view=graph-rest-beta&amp;tabs=http\n\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n\n$devices = @()\n$uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\ndo {\n    $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n    $devices += $response.value\n    $uri = $response.'@odata.nextLink'\n} while ($uri)\n\n# Filter only Windows client devices managed by Intune\n$ScopedDevices = $devices | Where-Object { $_.managementAgent -eq \"mdm\" -and $_.operatingSystem -like \"Windows*\"}\nWrite-Host \"\"\nWrite-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n\n# === LOOP THROUGH DEVICES ===\nforeach ($device in $ScopedDevices) {\n    $deviceName = $device.deviceName\n    $deviceId = $device.azureADDeviceId\n\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n\n    #####################\n    # Quality Updates\n    #####################\n    # Build JSON body for enrollment\n    $body = @{\n        updateCategory = \"quality\"\n        assets = @(\n            @{\n                \"@odata.type\" = \"#microsoft.graph.windowsUpdates.azureADDevice\"\n                id = $deviceId\n            }\n        )\n    }\n    $jsonBody = $body | ConvertTo-Json -Depth 5\n\n    Invoke-MgGraphRequest -Method POST `\n        -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/enrollAssets\" `\n        -Body $jsonBody `\n        -ContentType \"application\/json\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"enroll-all-driver\">Intune: Enroll All Windows Client devices into WUfB AutoPatch (Driver Updates only)<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Enroll All Windows Client devices into WUfB AutoPatch (Driver Updates only)\n\n# https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-enrollassets?view=graph-rest-beta&amp;tabs=http\n\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n\n$devices = @()\n$uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\ndo {\n    $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n    $devices += $response.value\n    $uri = $response.'@odata.nextLink'\n} while ($uri)\n\n# Filter only Windows client devices managed by Intune\n$ScopedDevices = $devices | Where-Object { $_.managementAgent -eq \"mdm\" -and $_.operatingSystem -like \"Windows*\"}\nWrite-Host \"\"\nWrite-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n\n# === LOOP THROUGH DEVICES ===\nforeach ($device in $ScopedDevices) {\n    $deviceName = $device.deviceName\n    $deviceId = $device.azureADDeviceId\n\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n\n    #####################\n    # Driver Updates\n    #####################\n        # Build JSON body for enrollment\n        $body = @{\n            updateCategory = \"driver\"\n            assets = @(\n                @{\n                    \"@odata.type\" = \"#microsoft.graph.windowsUpdates.azureADDevice\"\n                    id = $deviceId\n                }\n            )\n        }\n        $jsonBody = $body | ConvertTo-Json -Depth 5\n\n        Invoke-MgGraphRequest -Method POST `\n            -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/enrollAssets\" `\n            -Body $jsonBody `\n            -ContentType \"application\/json\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Intune: Enroll Windows Client devices into WUfB AutoPatch with &#8216;NotEnrolled&#8217; state (Quality Updates only)<\/h2>\n\n\n\n<pre id=\"enroll-quality-notenrolled\" class=\"wp-block-code has-small-font-size\"><code># Enroll Windows Client devices into WUfB AutoPatch with 'NotEnrolled' state (Quality Updates only)\n\n# https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-enrollassets?view=graph-rest-beta&amp;tabs=http\n\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n\n$devices = @()\n$uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\ndo {\n    $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n    $devices += $response.value\n    $uri = $response.'@odata.nextLink'\n} while ($uri)\n\nwrite-host \"Get enrollment status for devices ... Please Wait !\"\n$StatusEnrollmentWUfB = Get-WUfBEnrollmentStatus\n\n# Build a lookup dictionary from enrollment status for fast access\n$StatusLookup = @{}\nforeach ($entry in $StatusEnrollmentWUfB) {\n    $StatusLookup&#91;$entry.DeviceId] = $entry\n}\n\n# Filter: MDM-managed Windows devices that are NOT enrolled for 'feature' updates\n$ScopedDevices = $devices | Where-Object {\n    $_.managementAgent -eq \"mdm\" -and\n    $_.operatingSystem -like \"Windows*\" -and\n    $StatusLookup.ContainsKey($_.azureADDeviceId) -and\n    $StatusLookup&#91;$_.azureADDeviceId].EnrollmentStateQuality -eq \"notEnrolled\"   # filter for 'NotEnrolled' state. We don't want to include 'unenrolling', but let them finish first\n}\nWrite-Host \"\"\nWrite-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n\n# === LOOP THROUGH DEVICES ===\nforeach ($device in $ScopedDevices) {\n    $deviceName = $device.deviceName\n    $deviceId = $device.azureADDeviceId\n\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n\n    #####################\n    # Quality Updates\n    #####################\n        # Build JSON body for enrollment\n        $body = @{\n            updateCategory = \"quality\"\n            assets = @(\n                @{\n                    \"@odata.type\" = \"#microsoft.graph.windowsUpdates.azureADDevice\"\n                    id = $deviceId\n                }\n            )\n        }\n        $jsonBody = $body | ConvertTo-Json -Depth 5\n\n        Invoke-MgGraphRequest -Method POST `\n            -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/enrollAssets\" `\n            -Body $jsonBody `\n            -ContentType \"application\/json\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"enroll-notenrolled-driver\">Intune: Enroll Windows Client devices into WUfB AutoPatch with &#8216;NotEnrolled&#8217; state (Driver Updates only)<\/h2>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code># Enroll Windows Client devices into WUfB AutoPatch with 'NotEnrolled' state (Driver Updates only)\n\n# https:\/\/learn.microsoft.com\/en-us\/graph\/api\/windowsupdates-updatableasset-enrollassets?view=graph-rest-beta&amp;tabs=http\n\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce5 Retrieving all devices from Intune...\"\n\n$devices = @()\n$uri = \"https:\/\/graph.microsoft.com\/beta\/deviceManagement\/managedDevices\"\n\ndo {\n    $response = Invoke-MgGraphRequest -Method GET -Uri $uri\n    $devices += $response.value\n    $uri = $response.'@odata.nextLink'\n} while ($uri)\n\nwrite-host \"Get enrollment status for devices ... Please Wait !\"\n$StatusEnrollmentWUfB = Get-WUfBEnrollmentStatus\n\n# Build a lookup dictionary from enrollment status for fast access\n$StatusLookup = @{}\nforeach ($entry in $StatusEnrollmentWUfB) {\n    $StatusLookup&#91;$entry.DeviceId] = $entry\n}\n\n# Filter: MDM-managed Windows devices that are NOT enrolled for 'feature' updates\n$ScopedDevices = $devices | Where-Object {\n    $_.managementAgent -eq \"mdm\" -and\n    $_.operatingSystem -like \"Windows*\" -and\n    $StatusLookup.ContainsKey($_.azureADDeviceId) -and\n    $StatusLookup&#91;$_.azureADDeviceId].EnrollmentStateDriver -eq \"notEnrolled\"   # filter for 'NotEnrolled' state. We don't want to include 'unenrolling', but let them finish first\n}\nWrite-Host \"\"\nWrite-Host \"\u2705 Scoped devices found: $($ScopedDevices.Count)\"\n\n# === LOOP THROUGH DEVICES ===\nforeach ($device in $ScopedDevices) {\n    $deviceName = $device.deviceName\n    $deviceId = $device.azureADDeviceId\n\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Processing device: $deviceName ($deviceId)\"\n\n    #####################\n    # Driver Updates\n    #####################\n        # Build JSON body for enrollment\n        $body = @{\n            updateCategory = \"driver\"\n            assets = @(\n                @{\n                    \"@odata.type\" = \"#microsoft.graph.windowsUpdates.azureADDevice\"\n                    id = $deviceId\n                }\n            )\n        }\n        $jsonBody = $body | ConvertTo-Json -Depth 5\n\n        Invoke-MgGraphRequest -Method POST `\n            -Uri \"https:\/\/graph.microsoft.com\/beta\/admin\/windows\/updates\/updatableAssets\/enrollAssets\" `\n            -Body $jsonBody `\n            -ContentType \"application\/json\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"endpoint-troubleshoot\">Endpoint: Windows client troubleshooting<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"565\" src=\"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-6-1024x565.png\" alt=\"\" class=\"wp-image-5081\" srcset=\"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-6-1024x565.png 1024w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-6-300x166.png 300w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-6-768x424.png 768w, https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/image-6.png 1187w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This script can be used on the Windows endpoint. You must adjust the variables to your needs<\/p>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code>&lt;#\n    .NAME\n    Windows Client Troubleshooting (\n\n    .SYNOPSIS\n\n    .NOTES\n    \n    .VERSION\n    1.0\n    \n    .AUTHOR\n    Morten Knudsen, Microsoft MVP - https:\/\/mortenknudsen.net\n\n    .LICENSE\n    Licensed under the MIT license.\n\n    .PROJECTURI\n    https:&#47;&#47;github.com\/KnudsenMorten\/Intune-Windows-Update-Troubleshooting\n\n\n    .WARRANTY\n    Use at your own risk, no warranty given!\n#&gt;\n\n# === CONFIGURATION ===\n$CollectCompleteLogs = $false\n$CollectWULogsOnly = $false\n$RunSystemRepairs = $false\n$RunWUReset = $true\n$RunPolicySimulation = $true\n$RunWUCheck = $true\n$RunTelemetryCheck = $true\n$RunIMECheck = $true\n$RunDSREGCheck = $true\n$RunIMERepair = $false\n\n\n# === SETUP ===\n$LogPath = \"C:\\ProgramData\\Microsoft\\IntuneManagementExtension\\Logs\\FeatureUpdateValidation.log\"\nNew-Item -ItemType Directory -Path (Split-Path $LogPath) -Force | Out-Null\nStart-Transcript -Path $LogPath -Append\n\n\n# === ADMIN CHECK ===\nif (-not (&#91;Security.Principal.WindowsPrincipal]&#91;Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(\n    &#91;Security.Principal.WindowsBuiltInRole]::Administrator)) {\n    Write-Host \"\"\n    Write-Host \"\u274c Please run this script as Administrator.\"\n    Stop-Transcript\n    exit 1\n}\n\n\n# === SYSTEM REPAIR SECTION ===\nif ($RunSystemRepairs) {\n    Write-Host \"\"\n    Write-Host \"\u2699 Performing system file integrity checks...\"\n    sfc \/scannow\n    dism \/online \/cleanup-image \/scanhealth\n    DISM \/Online \/Cleanup-Image \/RestoreHealth\n    Dism.exe \/online \/Cleanup-Image \/StartComponentCleanup \/ResetBase\n}\n\n\n# === TELEMETRY &amp; SERVICE CHECKS ===\nif ($RunTelemetryCheck) {\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd0d Checking telemetry and diagnostics settings...\"\n\n    function Check-RegistryValue {\n        param (&#91;string]$Path, &#91;string]$Name)\n        try {\n            $value = Get-ItemProperty -Path $Path -Name $Name -ErrorAction Stop\n            return $value.$Name\n        } catch {\n            return $null\n        }\n    }\n\n    $diagTrack = Get-Service -Name \"DiagTrack\" -ErrorAction SilentlyContinue\n    Write-Host \"\"\n    if ($diagTrack -and $diagTrack.Status -eq 'Running') {\n        Write-Host \"\u2705 DiagTrack service is running.\"\n    } else {\n        Write-Host \"\u274c DiagTrack service is not running.\"\n    }\n\n    $telemetryLevel = Check-RegistryValue -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\DataCollection\" -Name \"AllowTelemetry\"\n    Write-Host \"\"\n    if ($telemetryLevel -ge 3) {\n        Write-Host \"\u2705 Telemetry Level: $telemetryLevel\"\n    } else {\n        Write-Host \"\u274c Telemetry Level too low: $telemetryLevel (min: 3)\"\n    }\n\n    $policyTelemetry = Check-RegistryValue -Path \"HKLM:\\SOFTWARE\\Microsoft\\PolicyManager\\current\\device\\System\" -Name \"AllowTelemetry\"\n    Write-Host \"\"\n    if ($policyTelemetry) {\n        Write-Host \"\u2705 PolicyManager Telemetry Level: $policyTelemetry\"\n    } else {\n        Write-Host \"\u274c PolicyManager Telemetry Level not set.\"\n    }\n\n    $commercialId = Check-RegistryValue -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\DataCollection\" -Name \"CommercialId\"\n    Write-Host \"\"\n    if ($commercialId) {\n        Write-Host \"\u2705 Commercial ID set: $commercialId\"\n    } else {\n        Write-Host \"\u274c Commercial ID not set.\"\n    }\n\n    $safeguardHold = Check-RegistryValue -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\TargetVersionUpgradeExperienceIndicators\" -Name \"UpgEx\"\n    Write-Host \"\"\n    if ($safeguardHold) {\n        Write-Host \"\u274c Safeguard Hold Detected: $safeguardHold\"\n    } else {\n        Write-Host \"\u2705 No Safeguard Holds detected.\"\n    }\n}\n\n\n# === WINDOWS UPDATE CHECK ===\nif ($RunWUCheck) {\n    Write-Host \"\"\n    Write-Host \"\ud83e\uddf9 Checking for and removing policy keys that block Windows Update...\"\n\n    $pathsToClean = @(\n        @{ Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate\"; Name = \"DoNotConnectToWindowsUpdateInternetLocations\" },\n        @{ Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate\"; Name = \"DisableWindowsUpdateAccess\" },\n        @{ Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU\"; Name = \"NoAutoUpdate\" }\n    )\n\n    foreach ($item in $pathsToClean) {\n        try {\n            $property = Get-ItemProperty -Path $item.Path -Name $item.Name -ErrorAction SilentlyContinue\n            if ($null -ne $property) {\n                Remove-ItemProperty -Path $item.Path -Name $item.Name -ErrorAction SilentlyContinue\n                Write-Host \"\u2705 Removed $($item.Name) from $($item.Path)\"\n            }\n        } catch {\n            # Do nothing\n        }\n    }\n}\n\n\n\n\n# === WINDOWS UPDATE RESET ===\nif ($RunWUReset) {\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd04 Performing Windows Update Reset...\"\n\n    Stop-Service wuauserv -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue\n    Stop-Service bits -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue\n    Stop-Service cryptSvc -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue\n    Stop-Service msiserver -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue\n    Stop-Service uhssvc -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue\n\n    $sdPath = \"C:\\Windows\\SoftwareDistribution\"\n    $catrootPath = \"C:\\Windows\\System32\\catroot2\"\n\n    if (Test-Path $sdPath) {\n        Remove-Item -Path $sdPath -Recurse -Force -ErrorAction SilentlyContinue\n        New-Item -Path $sdPath -ItemType Directory -Force | Out-Null\n    }\n\n    if (Test-Path $catrootPath) {\n        Remove-Item -Path $catrootPath -Recurse -Force -ErrorAction SilentlyContinue\n        New-Item -Path $catrootPath -ItemType Directory -Force | Out-Null\n    }\n\n    Start-Service wuauserv -ErrorAction SilentlyContinue  -WarningAction SilentlyContinue\n    Start-Service bits -ErrorAction SilentlyContinue  -WarningAction SilentlyContinue\n    Start-Service cryptSvc -ErrorAction SilentlyContinue  -WarningAction SilentlyContinue\n    Start-Service msiserver -ErrorAction SilentlyContinue  -WarningAction SilentlyContinue\n    Start-Service uhssvc -ErrorAction SilentlyContinue  -WarningAction SilentlyContinue\n\n    Write-Host \"\"\n    Write-Host \"\u2705 Windows Update services restarted and folders reset.\"\n}\n\n\n# === DSREG STATUS ===\nif ($RunDSREGCheck) {\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd10 Checking Entra ID Join Status...\"\n    $aad = dsregcmd \/status | Select-String \"AzureAdJoined\"\n    $hybrid = dsregcmd \/status | Select-String \"DomainJoined\"\n\n    Write-Host \"\"\n    if ($aad -match \"YES\") {\n        Write-Host \"\u2705 Device is Enta ID joined.\"\n    } else {\n        Write-Host \"\u274c Not Entra ID joined.\"\n    }\n\n    Write-Host \"\"\n    if ($hybrid -match \"YES\") {\n        Write-Host \"\u2705 Device is Hybrid joined.\"\n    } else {\n        Write-Host \"\u274c Not Hybrid joined.\"\n    }\n}\n\n\n# === INTUNE MANAGEMENT EXTENSION CHECK ===\nif ($RunIMECheck) {\n    Write-Host \"\"\n    Write-Host \"\ud83d\udee0 Restarting Intune Management Extension...\"\n    Stop-Service IntuneManagementExtension -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue\n    Start-Service IntuneManagementExtension -ErrorAction SilentlyContinue -WarningAction SilentlyContinue\n}\n\n# === INTUNE MANAGEMENT EXTENSION REPAIR ===\nif ($RunIMERepair) {\n    # Uninstall\n    msiexec \/x \"{1F8496D2-52D3-4DA4-BC6D-48A74D1C42E0}\" \/quiet \/norestart\n\n    # Re-download and reinstall\n    Invoke-WebRequest \"https:\/\/go.microsoft.com\/fwlink\/?linkid=2156826\" -OutFile \"$env:TEMP\\IME.msi\"\n    Start-Process msiexec.exe -ArgumentList \"\/i `\"$env:TEMP\\IME.msi`\" \/quiet \/norestart\" -Wait\n}\n\n\n# === POLICY SIMULATION ===\nif ($RunPolicySimulation) {\n    Write-Host \"\"\n    Write-Host \"\ud83d\udd12 Simulating MDM policy update to trigger MDM policy to refresh...\"\n    reg add \"HKLM\\SOFTWARE\\Microsoft\\PolicyManager\\current\\device\\System\" \/v \"DummyPolicy\" \/t REG_DWORD \/d 1 \/f &gt;$null 2&gt;&amp;1\n    Start-Sleep -Seconds 3\n    reg delete \"HKLM\\SOFTWARE\\Microsoft\\PolicyManager\\current\\device\\System\" \/v \"DummyPolicy\" \/f &gt;$null 2&gt;&amp;1\n    Write-Host \"\"\n    Write-Host \"\u2705 Policy refresh simulated.\"\n}\n\n\n# === LOG COLLECTION ===\nif ($CollectCompleteLogs) {\n    Write-Host \"\"\n    Write-Host \"\ud83d\udcc1 Collecting logs...\"\n\n    $timestamp = Get-Date -Format \"yyyy-MM-dd_HH-mm-ss\"\n    $tempFolder = \"C:\\Temp\\IntuneLogs_$timestamp\"\n    $zipPath = \"$tempFolder.zip\"\n\n    New-Item -ItemType Directory -Path $tempFolder -Force | Out-Null\n\n    wevtutil epl \"Microsoft-Windows-DeviceManagement-Enterprise-Diagnostics-Provider\/Admin\" \"$tempFolder\\MDM_EventLog.evtx\"\n    wevtutil epl \"Microsoft-Windows-WindowsUpdateClient\/Operational\" \"$tempFolder\\WU_EventLog.evtx\"\n    Get-WindowsUpdateLog -LogPath \"$tempFolder\\WindowsUpdate.log\"\n\n    mdmdiagnosticstool.exe -area \"DeviceProvisioning;DeviceEnrollment;Autopilot;DeviceManagementEnterprise-Diagnostics-Provider;Accounts;ModernApps;Connectivity;WNS;PushNotifications\" -cab \"$tempFolder\\MDMDiag.cab\"\n    expand.exe \"$tempFolder\\MDMDiag.cab\" -F:* $tempFolder\n    Remove-Item \"$tempFolder\\MDMDiag.cab\" -Force\n\n    Compress-Archive -Path $tempFolder\\* -DestinationPath $zipPath -Force\n\n    Write-Host \"\"\n    Write-Host \"\u2705 Logs collected and saved to: $zipPath\"\n}\n\nif ($CollectWULogsOnly) {\n    Write-Host \"\"\n    Write-Host \"\ud83d\udce4 Building Windows Update Logs ... Please Wait !\"\n    $timestamp = Get-Date -Format \"yyyy-MM-dd_HH-mm-ss\"\n    $tempFolder = \"C:\\Temp\\IntuneLogs_$timestamp\"\n\n    New-Item -ItemType Directory -Path $tempFolder -Force | Out-Null\n\n    Get-WindowsUpdateLog -LogPath \"$tempFolder\\WindowsUpdate.log\" -ForceFlush -Confirm:$false\n}\n\n\n# === FORCE COMPATIBILITY DATA SUBMISSION | Touching or updating LastDeviceScanTime can help re-initiate the compatibility assessment pipeline ===\nWrite-Host \"\"\nWrite-Host \"\ud83d\udce4 Forcing compatibility data submission to Windows Update for Business...\"\n\n# This registry key is often touched to ensure data is reprocessed\n$null = New-ItemProperty -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\TargetVersionUpgradeExperienceIndicators\" `\n    -Name \"LastDeviceScanTime\" -Value (&#91;System.Management.ManagementDateTimeConverter]::ToDmtfDateTime((Get-Date))) `\n    -PropertyType String -Force\n\nStart-Process -FilePath \"C:\\Windows\\System32\\compattelrunner.exe\" -ArgumentList \"-maintenance\" -NoNewWindow -Wait\n\nWrite-Host \"\u2705 Compatibility scan &amp; data submission triggered.\"\n\n\n# Scan for updates (recommended method)\nwrite-host \"Trigger 'Scan for Updates'\"\nUsoClient StartScan\nwuauclt \/detectnow\n\n# UsoClient StartDownload         # Begin downloading available updates\n# UsoClient StartInstall          # Install downloaded updates\n# UsoClient ScanInstallWait       # Full flow: scan + download + install\n\n\n# PSWindowsUpdate method\n&lt;# \n Get-WindowsUpdate -MicrosoftUpdate\n#&gt;\n\n# COM method\n&lt;# \n $UpdateSession = New-Object -ComObject Microsoft.Update.Session\n $UpdateSearcher = $UpdateSession.CreateUpdateSearcher()\n $SearchResult = $UpdateSearcher.Search(\"IsInstalled=0\")\n $SearchResult.Updates | Select-Object Title, IsDownloaded, IsInstalled\n#&gt;\n\nStop-Transcript\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have been banging my head why some machines wouldn&#8217;t deploy Windows 11 Windows Feature Updates (24H2) as part of &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"Troubleshooting Windows Feature Updates not being deploying using Intune\/AutoPatch\/WUfB\" class=\"read-more button\" href=\"https:\/\/mortenknudsen.net\/?p=5048#more-5048\" aria-label=\"Read more about Troubleshooting Windows Feature Updates not being deploying using Intune\/AutoPatch\/WUfB\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":5089,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"ngg_post_thumbnail":0,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[160,156,133,165,60,132],"tags":[186,185,182,183,190,17,189,108,184,188,187],"class_list":["post-5048","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-intune","category-microsoft-graph","category-microsoft-security","category-microsoftsecurityupdates","category-scripting","category-security","tag-autopatch","tag-feature-update","tag-intune","tag-patching","tag-script","tag-security","tag-troubleshooting","tag-update","tag-windows-11","tag-windows-update","tag-wufb","infinite-scroll-item","resize-featured-image"],"featured_image_src":"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/endpoint.jpg","author_info":{"display_name":"Morten Knudsen","author_link":"https:\/\/mortenknudsen.net\/?author=1"},"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_featured_media_url":"https:\/\/mortenknudsen.net\/wp-content\/uploads\/2025\/05\/endpoint.jpg","_links":{"self":[{"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=\/wp\/v2\/posts\/5048","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5048"}],"version-history":[{"count":62,"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=\/wp\/v2\/posts\/5048\/revisions"}],"predecessor-version":[{"id":5191,"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=\/wp\/v2\/posts\/5048\/revisions\/5191"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=\/wp\/v2\/media\/5089"}],"wp:attachment":[{"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5048"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5048"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mortenknudsen.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5048"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}