Reset-WindowsUpdate function

Discussion in 'Scripting' started by GodHand, Jun 5, 2019.

  1. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    534
    926
    30
    #1 GodHand, Jun 5, 2019
    Last edited: Aug 22, 2020
    Updated 08-21-2020
    - Added full support for Windows 10 build 19041+ that includes the Update Orchestrator Service.

    Updated 04-19-2020
    - Added an -IncludePolicies switch that, along with resetting all Windows Update components, will also reset all Windows Update policies.
    - Improved issued command verbosity.

    Updated 04-07-2020
    - Changed the output to pipeline verbosity similar to my Start-WindowsCleanup function as opposed to writing to the host shell.

    Updated 02-25-2020
    - Updated the DACLs to include the CryptSvc and TrustedInstaller
    - Updated the services that are automatically started upon completion of the reset to include the DcomLauncher
    - If a system restart is approved after the reset, a 30 second countdown progress bar will now show.

    Updated 01-16-2020
    - Works with the latest Windows 10 builds and does not revert custom policy settings set in Group Policy.
    - Added the option to restart the device to clear additional system cache reserves and to apply the default service discretionary access control lists DACLs.
    - Code has been optimized.

    Code:
    Function Reset-WindowsUpdate
    {
        <#
        .SYNOPSIS
            Cleans-up Windows Update containers and resets all dependent services, policies and modules to their default state.
        
        .DESCRIPTION
            Stops all Windows Update dependent services and cleans all directories used by Windows Update.
            Regenerates the default file structure that Windows requires for proper Windows Update Service access and authentication.
            Returns all dependent files, discretionary access control lists and permissions back to their default state.
            Optionally resets Windows Update policy settings.
            Restarts all Windows Update dependent services that were stopped at the beginning of the reset process.
            Allows for the system to be rebooted after the reset to clear cache storage reserves and apply the default service discretionary access control lists.
            Returns all issued commands the function is performing to the console window.
        
        .PARAMETER IncludePolicies
            Includes the reset of all Windows Update policy settings.
        
        .EXAMPLE
            PS C:\> Reset-WindowsUpdate
            
            This command will reset the Windows Update components.
        
        .EXAMPLE
            PS C:\> Reset-WindowsUpdate -IncludePolicies
            
            This command will reset the Windows Update components and policy settings.
        
        .NOTES
            It is highly recommended to restart the system immediately after running the reset to clear additional system cache reserves and to apply the default service discretionary access control lists DACLs.
        #>
        
        [CmdletBinding(ConfirmImpact = 'High',
            SupportsShouldProcess = $true)]
        Param
        (
            [Parameter(HelpMessage = 'Includes the reset of all Windows Update policy settings.')]
            [Switch]$IncludePolicies
        )
        
        Begin
        {
            If (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "This script requires elevated access. Please relaunch Reset-WindowsUpdate with administrator permissions."; Break }
            
            Function Rename-Container
            {
                [CmdletBinding()]
                Param
                (
                    [String]$Path,
                    [String]$NewName
                )
                
                If (Test-Path -LiteralPath $Path) { Rename-Item -LiteralPath $Path -NewName $NewName -Force -Verbose -ErrorAction SilentlyContinue }
            }
            
            $DefaultErrorActionPreference = $ErrorActionPreference
            $DefaultProgressPreference = $ProgressPreference
            $ErrorActionPreference = 'SilentlyContinue'
            $ProgressPreference = 'SilentlyContinue'
            
            $ResetItem = [Ordered]@{
                Pending         = "$Env:SystemRoot\WinSxS\pending.xml"
                PendingBackup   = 'pending.xml.bak'
                Software        = "$Env:SystemRoot\SoftwareDistribution"
                SoftwareBackup  = 'SoftwareDistribution.bak'
                Catroot         = "$Env:SystemRoot\System32\Catroot2"
                CatrootBackup   = 'Catroot2.bak'
                UpdateLog       = "$Env:SystemRoot\WindowsUpdate.log"
                UpdateLogBackup = 'WindowsUpdate.log.bak'
            }
            
            If ((Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 19041) { $Services = [Collections.Generic.List[String]]@('BITS', 'wuauserv', 'AppIDSvc', 'CryptSvc', 'UsoSvc') }
            Else { $Services = [Collections.Generic.List[String]]@('BITS', 'wuauserv', 'AppIDSvc', 'CryptSvc') }
        }
        Process
        {
            If ($PSCmdlet.ShouldProcess($Env:COMPUTERNAME, 'Reset Windows Update.'))
            {
                Clear-Host
                
                # Stop the BITS, wuauserv, AppIDSvc and CryptSvc services.
                ForEach ($Service In $Services)
                {
                    If ((Get-Service -Name $Service).Status -ne 'Stopped')
                    {
                        $Retries = 5
                        While ($Retries -gt 0 -and ((Get-Service -Name $Service).Status -ne 'Stopped'))
                        {
                            Stop-Service -Name $Service -Force -Confirm:$false -Verbose
                            Start-Sleep 1
                            If ((Get-Service -Name $Service).Status -eq 'Running') { Start-Sleep 5 }
                            $Retries--
                        }
                    }
                }
                
                # Clear the DNS cache.
                Clear-DnsClientCache -Verbose
                
                # Remove any old directories created by previous resets of Windows Update and create new backup directories for the current reset.
                @("$Env:ALLUSERSPROFILE\Application Data\Microsoft\Network\Downloader\qmgr*.dat", "$Env:ALLUSERSPROFILE\Microsoft\Network\Downloader\qmgr*.dat", "$Env:SystemRoot\WinSxS\pending.xml.bak", "$Env:SystemRoot\SoftwareDistribution.bak", "$Env:SystemRoot\System32\Catroot2.bak", "$Env:SystemRoot\WindowsUpdate.log.bak", (Get-ChildItem -Path $Env:SystemRoot\Logs\WindowsUpdate\* -Force)) | Remove-Item -Recurse -Force -Verbose -ErrorAction Ignore
                (Get-Item -Path @("$Env:SystemRoot\SoftwareDistribution", "$Env:SystemRoot\System32\Catroot2", "$Env:SystemRoot\WindowsUpdate.log") -Force).Attributes = 0x80
                If (Get-WindowsOptionalFeature -Online | Where-Object -Property State -EQ Pending)
                {
                    Start-Process -FilePath TAKEOWN -ArgumentList ('/F "{0}" /A' -f $ResetItem.Pending) -WindowStyle Hidden -Wait
                    (Get-Item -Path $ResetItem.Pending -Force).Attributes = 0x80
                    Rename-Container -Path $ResetItem.Pending -NewName $ResetItem.PendingBackup
                }
                Rename-Container -Path $ResetItem.Software -NewName $ResetItem.SoftwareBackup
                Rename-Container -Path $ResetItem.Catroot -NewName $ResetItem.CatrootBackup
                Rename-Container -Path $ResetItem.UpdateLog -NewName $ResetItem.UpdateLogBackup
                
                # For Windows 10 builds 19041 and above, remove the Update Session Orchestrator logs.
                If ($Services.Contains('UsoSvc')) { Remove-Item -Path "$Env:ProgramData\USOPrivate\UpdateStore\*" -Recurse -Force -Verbose -ErrorAction Ignore }
                
                # Reset wuauserv, BITS, CryptSvc and TrustedInstaller services to the default Discretionary Access Control List.
                Write-Verbose "Resetting the wuauserv DACL." -Verbose
                Start-Process -FilePath SC.EXE -ArgumentList ('SDSET wuauserv D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)') -WindowStyle Hidden -Wait
                Write-Verbose "Resetting the BITS DACL." -Verbose
                Start-Process -FilePath SC.EXE -ArgumentList ('SDSET BITS D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)') -WindowStyle Hidden -Wait
                Write-Verbose "Resetting the CryptSvc DACL." -Verbose
                Start-Process -FilePath SC.EXE -ArgumentList ('SDSET CryptSvc D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCLCSWRPWPDTLOCRRC;;;SO)(A;;CCLCSWLORC;;;AC)(A;;CCLCSWLORC;;;S-1-15-3-1024-3203351429-2120443784-2872670797-1918958302-2829055647-4275794519-765664414-2751773334)') -WindowStyle Hidden -Wait
                Write-Verbose "Resetting the TrustedInstaller DACL." -Verbose
                Start-Process -FilePath SC.EXE -ArgumentList ('SDSET TrustedInstaller D:(A;CI;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCDCLCSWRPWPDTLOCRRC;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)S:(AU;SAFA;WDWO;;;BA)') -WindowStyle Hidden -Wait
                
                If ($IncludePolicies.IsPresent)
                {
                    # Reset Windows Update policies.
                    @("HKCU:\Software\Policies\Microsoft\Windows\WindowsUpdate", "HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\WindowsUpdate", "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\WindowsUpdate") | Remove-Item -Recurse -Force -Verbose -ErrorAction Ignore
                    Start-Process -FilePath GPUPDATE -ArgumentList ('/FORCE') -WindowStyle Hidden -Wait
                }
                
                # Remove Windows Update client settings.
                @('AccountDomainSid', 'PingID', 'SusClientId', 'SusClientIDValidation') | ForEach-Object -Process { Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate -Name $PSItem -Force -Verbose -ErrorAction Ignore }
                
                # Reregister the BITS and Windows Update modules.
                @('atl.dll', 'urlmon.dll', 'mshtml.dll', 'shdocvw.dll', 'browseui.dll', 'jscript.dll', 'vbscript.dll', 'scrrun.dll', 'msxml.dll', 'msxml3.dll', 'msxml6.dll', 'actxprxy.dll', 'softpub.dll', 'wintrust.dll', 'dssenh.dll', 'rsaenh.dll', 'gpkcsp.dll', 'sccbase.dll', 'slbcsp.dll', 'cryptdlg.dll', 'oleaut32.dll', 'ole32.dll', 'shell32.dll', 'initpki.dll', 'wuapi.dll', 'wuaueng.dll', 'wuaueng1.dll', 'wucltui.dll', 'wups.dll', 'wups2.dll', 'wuweb.dll', 'qmgr.dll', 'qmgrprxy.dll', 'wucltux.dll', 'muweb.dll', 'wuwebv.dll', 'wudriver.dll') | ForEach-Object -Process { Write-Verbose ('Reregistering Module: {0}' -f $PSItem) -Verbose; Start-Process -FilePath REGSVR32 -ArgumentList ('/S "{0}"' -f "$Env:SystemRoot\System32\$($PSItem)") -WindowStyle Hidden -Wait }
                
                # Reset Winsock and WinHTTP and remove any BITS transfers.
                Write-Verbose "Resetting the Winsock Catalog and WinHTTP Settings." -Verbose
                Start-Process -FilePath NETSH -ArgumentList ('WINSOCK RESET') -WindowStyle Hidden -Wait
                Start-Process -FilePath NETSH -ArgumentList ('WINHTTP RESET PROXY') -WindowStyle Hidden -Wait
                Get-BitsTransfer | Remove-BitsTransfer -Verbose
                
                # Set the startup mode for the BITS, wuauserv, AppIDSvc, CryptSvc, DcomLaunch and TrustedInstaller services to automatic.
                Get-Service -Name ($Services + 'DcomLaunch' + 'TrustedInstaller') | Where-Object -Property StartType -NE Automatic | Set-Service -StartupType Automatic -Verbose
                $Services.Add('DcomLaunch')
                
                # Start the BITS, wuauserv, AppIDSvc and CryptSvc services.
                ForEach ($Service In $Services)
                {
                    If ((Get-Service -Name $Service).Status -ne 'Running')
                    {
                        $Retries = 5
                        While ($Retries -gt 0 -and ((Get-Service -Name $Service).Status -ne 'Running'))
                        {
                            Start-Service -Name $Service -Confirm:$false -Verbose
                            Start-Sleep 1
                            If ((Get-Service -Name $Service).Status -ne 'Running') { Start-Sleep 5 }
                            $Retries--
                        }
                    }
                }
                
                # For Windows 10 builds 19041 and above, use the UsoClient to refresh the Update Session Orchestrator settings.
                If ($Services.Contains('UsoSvc')) { Start-Process -FilePath USOCLIENT -ArgumentList ('REFRESHSETTINGS') -WindowStyle Hidden -Wait }
                
                # Restart the computer if requested.
                $Restart = (New-Object -ComObject Wscript.Shell).Popup("Restart $Env:COMPUTERNAME in 30 seconds to complete the reset?", 10, "Restart System", 4 + 32)
                If ($Restart -eq 6)
                {
                    $ProgressPreference = 'Continue'
                    ForEach ($Count In (1 .. 30))
                    {
                        Write-Progress -Id 1 -Activity "Restarting $Env:COMPUTERNAME to complete the reset" -Status "Restarting in 30 seconds, $(30 - $Count) seconds left" -PercentComplete (($Count / 30) * 100)
                        Start-Sleep 1
                    }
                    Restart-Computer -Force -ErrorAction SilentlyContinue
                }
            }
        }
        End
        {
            $ErrorActionPreference = $DefaultErrorActionPreference
            $ProgressPreference = $DefaultProgressPreference
        }
    }
    
    You can simply copy this entire function, and paste it in PowerShell ISE and run it with Reset-WindowsUpdate, or add it to any personal PowerShell module you have so you can run it from anywhere at any time.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  2. pf100

    pf100 Duct Tape Coder

    Oct 22, 2010
    2,069
    3,447
    90
    Works great. Thanks.
     
  3. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    534
    926
    30
    Updated.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  4. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    534
    926
    30
    Updated 01-16-2020
    - Works with the latest Windows 10 builds and does not revert custom policy settings set in Group Policy.
    - Added the option to restart the device to clear additional system cache reserves and to apply the default service discretionary access control lists DACLs.
    - Code has been optimized.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  5. niceman28

    niceman28 MDL Novice

    Jan 25, 2020
    8
    3
    0
    thanks it works very well
     
  6. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    534
    926
    30
    Updated 02-25-2020
    - Updated the DACLs to include the CryptSvc and TrustedInstaller
    - Updated the services that are automatically started upon completion of the reset to include the DcomLauncher
    - If a system restart is approved after the reset, a 30 second countdown progress bar will now show.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  7. wenger20

    wenger20 MDL Novice

    Aug 10, 2012
    5
    6
    0
    Save reg or bat file? Bro
     
  8. #8 Deleted member 1071488, Mar 24, 2020
    Last edited by a moderator: Mar 24, 2020
    its a powershell script code . save as
    Reset-WindowsUpdate.ps1

    as suggested by developer at last of the post :
    You can simply copy this entire function, and paste it in PowerShell ISE and run it with Reset-WindowsUpdate, or add it to any personal PowerShell module you have so you can run it from anywhere at any time.
     
  9. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    534
    926
    30
    Updated 04-07-2020
    - Changed the output to pipeline verbosity similar to my Start-WindowsCleanup function as opposed to writing to the host shell.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  10. KleineZiege

    KleineZiege MDL Expert

    Dec 11, 2018
    1,680
    1,924
    60
    does the powershell also work with the 20H1 19041?
    there is no restart
     
  11. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    534
    926
    30
    Updated 04-19-2020
    - Added an -IncludePolicies switch that, along with resetting all Windows Update components, will also reset all Windows Update policies.
    - Improved issued command verbosity.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  12. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    534
    926
    30
    Updated 08-21-2020
    - Added full support for Windows 10 build 19041+ that includes the Update Orchestrator Service.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...