Reset-WindowsUpdate function

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

  1. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    508
    750
    30
    #1 GodHand, Jun 5, 2019
    Last edited: Apr 19, 2020
    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 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 -Path $Path) { Rename-Item -Path $Path -NewName $NewName -Force -Verbose }
            }
    
            $DefaultErrorActionPreference = $ErrorActionPreference
            $DefaultProgressPreference = $ProgressPreference
            $ErrorActionPreference = 'SilentlyContinue'
            $ProgressPreference = 'SilentlyContinue'
    
            $Services = [Collections.Generic.List[String]]@('BITS', 'wuauserv', 'AppIDSvc', 'CryptSvc')
    
            $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'
            }
        }
        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 -Seconds 1
                            If ((Get-Service -Name $Service).Status -eq 'Running') { Start-Sleep -Seconds 5 }
                            $Retries--
                        }
                    }
                }
    
                # 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
    
                # 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.
                Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate -Name AccountDomainSid -Force -Verbose -ErrorAction Ignore
                Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate -Name PingID -Force -Verbose -ErrorAction Ignore
                Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate -Name SusClientId -Force -Verbose -ErrorAction Ignore
                Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate -Name SusClientIDValidation -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, clear the DNS cache 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
                Clear-DnsClientCache -Verbose
                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 -Seconds 1
                            If ((Get-Service -Name $Service).Status -ne 'Running') { Start-Sleep -Seconds 5 }
                            $Retries--
                        }
                    }
                }
    
                # 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 -Seconds 1
                    }
                    Restart-Computer
                }
            }
        }
        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.
     
  2. pf100

    pf100 MDL Expert

    Oct 22, 2010
    1,812
    2,723
    60
    Works great. Thanks.
     
  3. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    508
    750
    30
    Updated.
     
  4. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    508
    750
    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.
     
  5. niceman28

    niceman28 MDL Novice

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

    GodHand MDL Addicted

    Jul 15, 2016
    508
    750
    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.
     
  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
    508
    750
    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.
     
  10. KleineZiege

    KleineZiege MDL Member

    Dec 11, 2018
    107
    20
    10
    does the powershell also work with the 20H1 19041?
    there is no restart
     
  11. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    508
    750
    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.