Public/windowsupdate/Get-WindowsUpdateConfiguration.ps1

#Requires -Version 5.1
function Get-WindowsUpdateConfiguration {
    <#
        .SYNOPSIS
            Retrieves Windows Update configuration from local or remote computers
 
        .DESCRIPTION
            Reads Windows Update configuration from the registry on local or remote computers.
            The function queries two registry paths under HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate
            to retrieve WSUS, Windows Update for Business (WUFB), and Auto Update GPO settings.
            When the WindowsUpdate policy key exists, the computer is considered GPO-configured.
            The UpdateSource property is determined by analyzing UseWUServer, WUServer, and deferral
            settings to classify the source as WSUS, WUFB, WindowsUpdate, or Unknown.
 
        .PARAMETER ComputerName
            One or more computer names to query. Defaults to the local computer.
            Accepts pipeline input by value and by property name.
 
        .PARAMETER Credential
            Optional PSCredential for authenticating to remote computers.
            Not required for local queries or when the current user has sufficient permissions.
 
        .EXAMPLE
            Get-WindowsUpdateConfiguration
 
            Retrieves Windows Update configuration from the local computer.
 
        .EXAMPLE
            Get-WindowsUpdateConfiguration -ComputerName 'SRV01' -Credential (Get-Credential)
 
            Retrieves Windows Update configuration from SRV01 using explicit credentials.
 
        .EXAMPLE
            'SRV01', 'SRV02' | Get-WindowsUpdateConfiguration | Where-Object -Property UpdateSource -NE -Value 'WSUS'
 
            Queries multiple servers via pipeline and filters for those not using WSUS.
 
        .OUTPUTS
            PSWinOps.WindowsUpdateConfiguration
            Returns an object per computer with UpdateSource, WSUS URLs, auto-update settings,
            deferral policies, branch readiness level, target group, and GPO configuration status.
 
        .NOTES
            Author: Franck SALLET
            Version: 1.0.0
            Last Modified: 2026-04-08
            Requires: PowerShell 5.1+ / Windows only
 
        .LINK
            https://github.com/k9fr4n/PSWinOps
 
        .LINK
            https://learn.microsoft.com/en-us/windows/deployment/update/waas-wu-settings
    #>

    [CmdletBinding()]
    [OutputType('PSWinOps.WindowsUpdateConfiguration')]
    param(
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias('CN', 'DNSHostName')]
        [string[]]$ComputerName = $env:COMPUTERNAME,

        [Parameter(Mandatory = $false)]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential
    )

    begin {
        Write-Verbose -Message "[$($MyInvocation.MyCommand)] Starting"

        $auOptionsMap = @{
            1 = 'Disabled'
            2 = 'NotifyDownload'
            3 = 'AutoDownload'
            4 = 'ScheduledInstall'
            5 = 'AllowLocalAdmin'
        }

        $scheduledInstallDayMap = @{
            0 = 'EveryDay'
            1 = 'Sunday'
            2 = 'Monday'
            3 = 'Tuesday'
            4 = 'Wednesday'
            5 = 'Thursday'
            6 = 'Friday'
            7 = 'Saturday'
        }

        $branchReadinessMap = @{
            16 = 'SemiAnnualPreview'
            32 = 'SemiAnnual'
            64 = 'LongTermServicing'
        }

        $registryScriptBlock = {
            $wuPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'
            $auPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU'

            $gpoConfigured = Test-Path -Path $wuPath
            $wuProps = Get-ItemProperty -Path $wuPath -ErrorAction SilentlyContinue
            $auProps = Get-ItemProperty -Path $auPath -ErrorAction SilentlyContinue

            [PSCustomObject]@{
                IsGPOConfigured                 = $gpoConfigured
                WUServer                        = $wuProps.WUServer
                WUStatusServer                  = $wuProps.WUStatusServer
                TargetGroup                     = $wuProps.TargetGroup
                TargetGroupEnabled              = $wuProps.TargetGroupEnabled
                DeferFeatureUpdates             = $wuProps.DeferFeatureUpdates
                DeferFeatureUpdatesPeriodInDays = $wuProps.DeferFeatureUpdatesPeriodInDays
                DeferQualityUpdates             = $wuProps.DeferQualityUpdates
                DeferQualityUpdatesPeriodInDays = $wuProps.DeferQualityUpdatesPeriodInDays
                BranchReadinessLevel            = $wuProps.BranchReadinessLevel
                PauseFeatureUpdatesStartTime    = $wuProps.PauseFeatureUpdatesStartTime
                PauseQualityUpdatesStartTime    = $wuProps.PauseQualityUpdatesStartTime
                UseWUServer                     = $auProps.UseWUServer
                NoAutoUpdate                    = $auProps.NoAutoUpdate
                AUOptions                       = $auProps.AUOptions
                ScheduledInstallDay             = $auProps.ScheduledInstallDay
                ScheduledInstallTime            = $auProps.ScheduledInstallTime
                NoAutoRebootWithLoggedOnUsers   = $auProps.NoAutoRebootWithLoggedOnUsers
            }
        }
    }

    process {
        foreach ($computer in $ComputerName) {
            Write-Verbose -Message "[$($MyInvocation.MyCommand)] Processing $computer"

            try {
                $invokeParams = @{
                    ComputerName = $computer
                    ScriptBlock  = $registryScriptBlock
                }

                if ($PSBoundParameters.ContainsKey('Credential')) {
                    $invokeParams['Credential'] = $Credential
                }

                $rawData = Invoke-RemoteOrLocal @invokeParams

                # Determine UpdateSource
                $updateSource = 'Unknown'
                if ($rawData.UseWUServer -eq 1 -and -not [string]::IsNullOrEmpty($rawData.WUServer)) {
                    $updateSource = 'WSUS'
                } elseif ($rawData.DeferFeatureUpdates -eq 1 -or $rawData.DeferQualityUpdates -eq 1) {
                    $updateSource = 'WUFB'
                } elseif ($rawData.IsGPOConfigured -eq $false) {
                    $updateSource = 'WindowsUpdate'
                }

                # Map AUOptions
                $mappedAUOption = $null
                if ($null -ne $rawData.AUOptions) {
                    $mappedAUOption = $auOptionsMap[[int]$rawData.AUOptions]
                }

                # Map ScheduledInstallDay
                $mappedInstallDay = $null
                if ($null -ne $rawData.ScheduledInstallDay) {
                    $mappedInstallDay = $scheduledInstallDayMap[[int]$rawData.ScheduledInstallDay]
                }

                # Map BranchReadinessLevel
                $mappedBranchReadiness = $null
                if ($null -ne $rawData.BranchReadinessLevel) {
                    $mappedBranchReadiness = $branchReadinessMap[[int]$rawData.BranchReadinessLevel]
                }

                [PSCustomObject]@{
                    PSTypeName                    = 'PSWinOps.WindowsUpdateConfiguration'
                    ComputerName                  = $computer
                    UpdateSource                  = $updateSource
                    WUServerUrl                   = $rawData.WUServer
                    WUStatusServerUrl             = $rawData.WUStatusServer
                    UseWUServer                   = ($rawData.UseWUServer -eq 1)
                    AutoUpdateEnabled             = ($rawData.NoAutoUpdate -ne 1)
                    AutoUpdateOption              = $mappedAUOption
                    ScheduledInstallDay           = $mappedInstallDay
                    ScheduledInstallTime          = $rawData.ScheduledInstallTime
                    NoAutoRebootWithLoggedOnUsers = ($rawData.NoAutoRebootWithLoggedOnUsers -eq 1)
                    DeferFeatureUpdatesDays       = $rawData.DeferFeatureUpdatesPeriodInDays
                    DeferQualityUpdatesDays       = $rawData.DeferQualityUpdatesPeriodInDays
                    BranchReadinessLevel          = $mappedBranchReadiness
                    PauseFeatureUpdatesStartTime  = $rawData.PauseFeatureUpdatesStartTime
                    PauseQualityUpdatesStartTime  = $rawData.PauseQualityUpdatesStartTime
                    TargetGroup                   = $rawData.TargetGroup
                    TargetGroupEnabled            = ($rawData.TargetGroupEnabled -eq 1)
                    IsGPOConfigured               = $rawData.IsGPOConfigured
                    Timestamp                     = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
                }
            } catch {
                Write-Error -Message "[$($MyInvocation.MyCommand)] Failed to retrieve Windows Update configuration from ${computer}: $_"
                continue
            }
        }
    }

    end {
        Write-Verbose -Message "[$($MyInvocation.MyCommand)] Completed"
    }
}