Public/windowsupdate/Reset-WindowsUpdateComponent.ps1

#Requires -Version 5.1
function Reset-WindowsUpdateComponent {
    <#
        .SYNOPSIS
            Resets the Windows Update service stack to a clean state
 
        .DESCRIPTION
            Stops the Windows Update related services, deletes the BITS queue, backs up the
            SoftwareDistribution and Catroot2 folders, resets the BITS and wuauserv service
            security descriptors, and reregisters the Windows Update DLLs before restarting
            the services and triggering a fresh detection. Optionally resets the Winsock and
            WinHTTP proxy network stack. This is the PSWinOps equivalent of
            PSWindowsUpdate's Reset-WUComponents and is used to recover a corrupted Windows
            Update client.
 
        .PARAMETER ComputerName
            One or more computer names to target. 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 operations.
 
        .PARAMETER IncludeNetworkReset
            When specified, also resets the Winsock catalog (netsh winsock reset) and
            WinHTTP proxy settings (netsh winhttp reset proxy).
            WARNING: netsh winsock reset requires a reboot to take effect and resets the
            Winsock catalog. On a remote machine it can drop the WinRM session and
            connectivity until the machine is rebooted. Sets RebootRequired = $true.
 
        .EXAMPLE
            Reset-WindowsUpdateComponent
 
            Resets the Windows Update component stack on the local computer.
 
        .EXAMPLE
            Reset-WindowsUpdateComponent -ComputerName 'SRV01'
 
            Resets the Windows Update component stack on the remote server SRV01.
 
        .EXAMPLE
            Reset-WindowsUpdateComponent -ComputerName 'SRV01' -IncludeNetworkReset
 
            Resets the Windows Update stack on SRV01 and additionally resets the Winsock
            catalog and WinHTTP proxy. A reboot will be required on SRV01 after this runs.
 
        .EXAMPLE
            'SRV01', 'SRV02' | Reset-WindowsUpdateComponent
 
            Resets the Windows Update component stack on SRV01 and SRV02 via pipeline.
 
        .OUTPUTS
            PSWinOps.WindowsUpdateResetResult
            Returns one object per machine with ComputerName, Status, ServicesStopped,
            ServicesStarted, backup paths, DLL counts, network reset flags, Failures,
            Notes, and Timestamp.
 
        .NOTES
            Author: Franck SALLET
            Version: 1.0.0
            Last Modified: 2026-06-24
            Requires: PowerShell 5.1+ / Windows only
            Requires: Administrator privileges (stops/starts services, edits SDDL, deletes system folders)
            WARNING: -IncludeNetworkReset resets Winsock/WinHTTP and requires a reboot; may drop a remote session.
 
        .LINK
            https://github.com/k9fr4n/PSWinOps
 
        .LINK
            https://learn.microsoft.com/en-us/troubleshoot/windows-client/installing-updates-features-roles/additional-resources-for-windows-update
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType('PSWinOps.WindowsUpdateResetResult')]
    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,

        [Parameter(Mandatory = $false)]
        [switch]$IncludeNetworkReset
    )

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

        $resetScriptBlock = ${Function:Invoke-WindowsUpdateReset}
    }

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

            # Elevation guard — local execution requires Administrator privileges
            if (-not (Test-IsAdministrator)) {
                Write-Error -Message "[$($MyInvocation.MyCommand)] Administrator privileges are required. Failed on '$computer'."
                [PSCustomObject]@{
                    PSTypeName                 = 'PSWinOps.WindowsUpdateResetResult'
                    ComputerName               = $computer
                    Status                     = 'Failed'
                    ServicesStopped            = @()
                    ServicesStarted            = @()
                    SoftwareDistributionBackup = ''
                    Catroot2Backup             = ''
                    QmgrFilesDeleted           = 0
                    DllsReregistered           = 0
                    DllsFailed                 = 0
                    NetworkResetPerformed      = $false
                    RebootRequired             = $false
                    Failures                   = @('Not elevated: Administrator privileges required.')
                    Notes                      = @()
                    Timestamp                  = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
                }
                continue
            }

            # Main ShouldProcess gate
            $mainAction = 'Reset Windows Update service components. Cleanup SoftwareDistribution & Catroot2 folder, reset service security descriptors, and reregister Windows Update DLLs'
            if (-not $PSCmdlet.ShouldProcess($computer, $mainAction)) {
                continue
            }

            # Sub-check for destructive network reset
            $doNetworkReset = $false
            if ($IncludeNetworkReset) {
                $networkAction = 'Reset Winsock catalog and WinHTTP proxy settings (requires reboot; may drop remote WinRM session)'
                $doNetworkReset = $PSCmdlet.ShouldProcess($computer, $networkAction)
            }

            try {
                $invokeParams = @{
                    ComputerName = $computer
                    ScriptBlock  = $resetScriptBlock
                    ArgumentList = @([bool]$doNetworkReset)
                }
                if ($PSBoundParameters.ContainsKey('Credential')) {
                    $invokeParams['Credential'] = $Credential
                }

                $rawResult = Invoke-RemoteOrLocal @invokeParams

                [PSCustomObject]@{
                    PSTypeName                 = 'PSWinOps.WindowsUpdateResetResult'
                    ComputerName               = $computer
                    Status                     = $rawResult.Status
                    ServicesStopped            = $rawResult.ServicesStopped
                    ServicesStarted            = $rawResult.ServicesStarted
                    SoftwareDistributionBackup = $rawResult.SoftwareDistributionBackup
                    Catroot2Backup             = $rawResult.Catroot2Backup
                    QmgrFilesDeleted           = $rawResult.QmgrFilesDeleted
                    DllsReregistered           = $rawResult.DllsReregistered
                    DllsFailed                 = $rawResult.DllsFailed
                    NetworkResetPerformed      = $rawResult.NetworkResetPerformed
                    RebootRequired             = $rawResult.RebootRequired
                    Failures                   = $rawResult.Failures
                    Notes                      = $rawResult.Notes
                    Timestamp                  = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
                }
            } catch {
                Write-Error -Message "[$($MyInvocation.MyCommand)] Failed on '${computer}': $_"
            }
        }
    }

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