Get-AutopatchHealth.ps1

<#PSScriptInfo
 
.VERSION 1.1.0
 
.GUID 2c80336a-7d9b-41c0-8eb3-a80abef2dbb8
 
.AUTHOR Jeff Gilbert
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS Intune
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
Version 1.0.0 3.10.26 Original published version.
Version 1.1.0 3.10.26 Fixed typo in report output.
#>


<#
.SYNOPSIS
    Automates Autopatch health checks and validation for Windows devices enrolled in Intune.
    This script performs a series of checks to determine if a device is properly configured for Autopatch,
    including policy source verification, WSUS configuration, registry blockers, MDM enrollment status, and recent activity indicators.
 
.DESCRIPTION
    The script checks the following:
    - Whether the device is receiving update policies from MDM (Intune) and if it's tagged for Autopatch.
    - If there are any WSUS server configurations that could interfere with Autopatch.
    - If specific registry keys that would block Autopatch are present.
    - If the device is enrolled in Intune by checking for key registry paths.
    - The last activity date of the Intune Management Extension to assess if the device has been active recently.
 
.EXAMPLE
    .\Get-AutopatchHealth.ps1
    Runs the script to perform Autopatch health checks on the local device.
    The output will indicate the status of each check with color-coded messages for easy identification of issues.
 
.NOTES
    For best results, run this script in a PowerShell terminal with administrative privileges.
#>



# Autopatch Local Health & Blocker Validation Script
    Clear-Host
# -----------------------------
# ChecksStart
# -----------------------------
    Write-Host "=== Autopatch Quick Validation ===" -ForegroundColor Cyan

# -----------------------------
# Branch&AutopatchPolicyCheck
# -----------------------------
    Write-Host "`n[Update Policy Source - is this an Autopatch device?]" -ForegroundColor Yellow
    # --- Paths (use Registry:: to avoid 32-bit WOW64 redirection issues) ---
    $policyStatePath = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UpdatePolicy\PolicyState'
    if (-not (Test-Path $policyStatePath)) { Write-Host "PolicyState registry path not found: $policyStatePath" -ForegroundColor Yellow
        return
    }
    $policyState = Get-ItemProperty -Path $policyStatePath -ErrorAction SilentlyContinue
    # -----------------------------
    # BranchReadinessLevel
    # -----------------------------
        $brl = [string]$policyState.BranchReadinessLevel
        $brlLabel = switch ($brl) {
            'CB'  { 'Current Branch' }
            'B'   { 'Business (SAC)' }
            'RP'  { 'Release Preview' }
            'WIF' { 'Windows Insider Fast' }
            'WIS' { 'Windows Insider Slow' }
            'WIP' { 'Windows Insider Preview' }
            default { $brl }   # fall back to raw value
        }
        Write-Host ("BranchReadinessLevel : {0} ({1})" -f $brl, $brlLabel)
    # -----------------------------
    # PolicySources
    # -----------------------------
        $ps = [int]$policyState.PolicySources
        # Decode PolicySources bitmask
        $psParts = @()
        if (($ps -band 1) -ne 0) { $psParts += 'Group Policy' }
        if (($ps -band 2) -ne 0) { $psParts += 'ConfigMgr' }
        if (($ps -band 4) -ne 0) { $psParts += 'MDM (Intune)' }
        $psLabel = if ($psParts.Count -gt 0) { $psParts -join ' + ' }
        else { 'None / Unknown' }
        # Autopatch tag when MDM is present
        $AutopatchTag = if (($ps -band 4) -ne 0) { 'Autopatch' } else { $null }
        $psDisplay = if ($ps -eq 4) { "$ps (Autopatch" } else { "$ps" }
        if ($AutopatchTag) { 
            Write-Host ("PolicySources : {0})" -f $psDisplay) -ForegroundColor Green }
        else { Write-Host ("PolicySources : {0} ({1})" -f $ps, $psLabel) -ForegroundColor Red }

# -----------------------------
# WSUSConfigurationCheck
# -----------------------------
    Write-Host "`n[WSUS Configuration - is this device looking for a WSUS server?]" -ForegroundColor Yellow
    $wuPolicyPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'
    if (Test-Path $wuPolicyPath) {
        $wu = Get-ItemProperty -Path $wuPolicyPath -ErrorAction Stop
        $wuServer      = [string]$wu.WUServer
        $wuStatusServer = [string]$wu.WUStatusServer
        # Consider "blank" as: $null, empty string, or whitespace-only
        if ([string]::IsNullOrWhiteSpace($wuServer) -and [string]::IsNullOrWhiteSpace($wuStatusServer)) {
            Write-Host "WSUS server information is not configured" -ForegroundColor Green
        }
        else {
            If ($wu.WUServer) {write-host "WSUS VALUE FOUND: WUServer : $($wu.WUServer)" -ForegroundColor Red }
            If ($wu.WUStatusServer) {write-host "WSUS VALUE FOUND: WUStatusServer : $($wu.WUStatusServer)" -ForegroundColor Red }
        }
    } else { Write-Host "No WSUS policy keys found" -ForegroundColor Green }

# -----------------------------
# UpdateSourceKeysCheck
# -----------------------------
    Write-Host "`n[Update Source Registry Check - are Autopatch update source keys set to WSUS?]" -ForegroundColor Yellow
    $wureg = 0
        # --- Paths (use Registry:: to avoid 32-bit WOW64 redirection issues) ---
        $UpdateSourceKeys = @(
                @{ Path = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UpdatePolicy\PolicyState"; ValueName = "SetPolicyDrivenUpdateSourceForDriverUpdates"; WSUSValue = "1"  },
                @{ Path = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UpdatePolicy\PolicyState"; ValueName = "SetPolicyDrivenUpdateSourceForFeatureUpdates"; WSUSValue = "1"  },
                @{ Path = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UpdatePolicy\PolicyState"; ValueName = "SetPolicyDrivenUpdateSourceForOtherUpdates"; WSUSValue = "1"  },
                @{ Path = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UpdatePolicy\PolicyState"; ValueName = "SetPolicyDrivenUpdateSourceForQualityUpdates"; WSUSValue = "1" }         
            )
            ForEach ($Key in $UpdateSourceKeys){               
                $value = Get-ItemProperty -Path $Key.Path -Name $Key.ValueName -ErrorAction SilentlyContinue
                if ($null -ne $value){ 
                    if ([string]$value.$($Key.ValueName) -eq $Key.WSUSValue){ Write-Host "$($Key.ValueName) is set to 1 (WSUS)" -ForegroundColor Red ; $wureg++ }} # WSUS config registry value found
                }
                If ( $wureg -eq 0){ Write-Host "No update source key issues found" -ForegroundColor Green}

# -----------------------------
# AutopatchRegistryBlockerCheck
# -----------------------------
    Write-Host "`n[Registry Blockers - is Autopatch being blocked?]" -ForegroundColor Yellow
    $blockingKey = 0
    # --- Paths (use Registry:: to avoid 32-bit WOW64 redirection issues) ---
    $regChecks = @(
    'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\DoNotConnectToWindowsUpdateInternetLocations',
    'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\DisableWindowsUpdateAccess',
    'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU\NoAutoUpdate'
    )
    foreach ($path in $regChecks) {
    if (Test-Path $path) { Write-Host "BLOCKING KEY FOUND: $path" -ForegroundColor Red ; $blockingKey++ } 
    }
    if ($blockingKey -eq 0){ Write-Host "No Autopatch registry blockers found" -ForegroundColor Green }

# -----------------------------
# MDMEnrollmentCheck
# -----------------------------
    Write-Host "`n[Intune Enrollment - is MDM present?]" -ForegroundColor Yellow
    # --- Paths (use Registry:: to avoid 32-bit WOW64 redirection issues) ---
        $missingKey = 0
        $paths = @(
            'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\IntuneManagementExtension',
            'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PolicyManager\current\device'
        )
        foreach ($p in $paths) {
        if (!(Test-Path $p)){ Write-Host "Enrollment key $p NOT is present" -ForegroundColor Red ; $missingKey++} 
        }
        If ($missingKey -eq 0){ Write-Host "Intune enrollment indicators are present" -ForegroundColor Green }

# -----------------------------
# IMEActivityCheck
# -----------------------------
    Write-Host "`n[Device Activity - has the device been active within the last 30 days?]" -ForegroundColor Yellow
    $latestImeLog = Get-ChildItem 'C:\ProgramData\Microsoft\IntuneManagementExtension\Logs' `
        -Filter '*.log' -ErrorAction SilentlyContinue |
        Sort-Object LastWriteTime -Descending |
        Select-Object -First 1
    if ($latestImeLog) {
        $lastActivity = $latestImeLog.LastWriteTime
        $cutoffDate   = (Get-Date).AddDays(-30)
        Write-Host "Last Intune activity (IME): $lastActivity"
        if ($lastActivity -ge $cutoffDate) { Write-Host "Status: ACTIVE (within last 30 days)" -ForegroundColor Green }
        else { Write-Host "Status: STALE (older than 30 days)" -ForegroundColor Yellow }
    } else { Write-Host "No Intune Management Extension logs found." -ForegroundColor Red }

# -----------------------------
# ChecksComplete
# -----------------------------
    Write-Host "`nValidation complete."`n -ForegroundColor Cyan

Exit