Updated Windows Cleanup and Windows Update Reset

Discussion in 'Scripting' started by GodHand, Oct 1, 2018.

  1. GodHand

    GodHand MDL Senior Member

    Jul 15, 2016
    492
    666
    10
    #1 GodHand, Oct 1, 2018
    Last edited: Aug 29, 2019
    Updated 08-28-19

    Code:
    Function Start-WindowsCleanup
    {
        <#
        .SYNOPSIS
            Clean-up temporary files and folders and reclaim disk space.
    
        .DESCRIPTION
            Cleans-up temporary Windows files, folders and directories.
            Sets the proper StateFlags on all available VolumeCaches in the registry and runs the advanced CleanMgr.exe as a .NET process to circumvent the GUI that requires one to manually select each section to clean.
            Monitors the .NET processes until completion and then continues with the script.
            Enables all options by adding the StateFlags registry property name to the VolumeCache registry keys.
            These options are then disabled, after CleanMgr.exe has run, to ensure no other programs can utilize its features without permission.
            logs all actions processed to a transcript that located in 'C:\Temp'.
    
        .PARAMETER StateFlags
            An optional integer for the StateFlags registry property name in case a specific value is required.
            The default value is 1.
    
        .EXAMPLE
            PS C:\> Start-WindowsCleanup
            PS C:\> Start-WindowsCleanup -StateFlags 1234
    
        .NOTES
            The Download folder is not included in the clean-up with Windows Disk Clean-up Manager to make sure no downloaded files are removed without explicit permission.
            Any permissions errors returned when cleaning can be ignored.
        #>
    
        [CmdletBinding(ConfirmImpact = 'High',
            SupportsShouldProcess = $true)]
        Param
        (
            [Parameter(Mandatory = $false,
                HelpMessage = 'An optional integer for the StateFlags registry property.')]
            [ValidateRange(1, 9999)]
            [int]$StateFlags = 5432
        )
    
        Begin
        {
            $PropertyName = "StateFlags{0:D4}" -f $StateFlags
            $ReportLog = Join-Path -Path "$Env:SystemRoot\Temp" -ChildPath "Start-WindowsCleanup_$(Get-Date -Format "MM-dd-yyyy").log"
            Function Clear-Item
            {
                [CmdletBinding()]
                Param
                (
                    [string]$Path,
                    [switch]$Remove
                )
                If ($Remove.IsPresent) { Remove-Item -Path $($Path) -Recurse -Force -Verbose -ErrorAction SilentlyContinue }
                Else { Get-ChildItem -Path $($Path) -Recurse -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -Verbose -Erroraction SilentlyContinue }
            }
            $PreClean = Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object -Property DriveType -EQ 3 | Select-Object -Property SystemName,
            @{ Name = "Drive"; Expression = { ($_.DeviceID) } },
            @{ Name = "Size (GB)"; Expression = { "{0:N1}" -f ($_.Size / 1GB) } },
            @{ Name = "FreeSpace (GB)"; Expression = { "{0:N1}" -f ($_.Freespace / 1GB) } },
            @{ Name = "PercentFree"; Expression = { "{0:P1}" -f ($_.FreeSpace / $_.Size) } } | Format-Table -AutoSize | Out-String
            Start-Transcript -Path $ReportLog
        }
        Process
        {
            If ($PSCmdlet.ShouldProcess("$Env:COMPUTERNAME, All distribution, logging and temporary content will be lost"))
            {
                Clear-Host
                Clear-Item -Path "$Env:TEMP\*"
                Clear-Item -Path "$Env:SystemRoot\Temp\*"
                Clear-Item -Path "C:\Users\*\AppData\Local\Temp\*"
                Clear-Item -Path "$Env:SystemRoot\SoftwareDistribution\Download\*"
                Clear-Item -Path "C:\Users\*\AppData\Local\Microsoft\Windows\Temporary Internet Files\*"
                Clear-Item -Path "C:\Users\*\AppData\Local\Microsoft\Windows\IECompatCache\*"
                Clear-Item -Path "C:\Users\*\AppData\Local\Microsoft\Windows\IECompatUaCache\*"
                Clear-Item -Path "C:\Users\*\AppData\Local\Microsoft\Windows\IEDownloadHistory\*"
                Clear-Item -Path "C:\Users\*\AppData\Local\Microsoft\Windows\INetCache\*"
                Clear-Item -Path "C:\Users\*\AppData\Local\Microsoft\Windows\INetCookies\*"
                Clear-Item -Path "C:\Users\*\AppData\Local\Microsoft\Terminal Server Client\Cache\*"
                Clear-Item -Path "C:\Users\*\AppData\Local\Microsoft\Windows\WER\*"
                Clear-Item -Path "$Env:ProgramData\Microsoft\Windows\WER\*"
                Clear-Item -Path "$Env:SystemRoot\Logs\*"
                Clear-Item -Path "$Env:SystemRoot\WinSxS\ManifestCache\*"
                Clear-Item -Path "$Env:SystemDrive\swtools\*"
                Clear-Item -Path "$Env:SystemDrive\swsetup\*"
                Clear-Item -Path "$Env:SystemRoot\drivers\*"
                Clear-Item -Path "$Env:SystemDrive\Users\Administrator\Downloads\*"
                Clear-Item -Path "$Env:SystemRoot\minidump\*"
                Clear-Item -Path "$Env:SystemRoot\Prefetch\*"
                Clear-Item -Path "$Env:HOMEDRIVE\inetpub\logs\LogFiles\*"
                Clear-Item -Path "$Env:HOMEDRIVE\PerfLogs\*"
                Clear-Item -Path "$Env:HOMEDRIVE\Intel\*"
                Clear-Item -Path "$Env:HOMEDRIVE\Config.Msi" -Remove
                Clear-Item -Path "$Env:SystemRoot\*.log" -Remove
                Clear-Item -Path "$Env:SystemRoot\Logs\CBS\*.log" -Remove
                Clear-Item -Path "$Env:SystemRoot\Logs\DISM\*.log*" -Remove
                Clear-Item -Path "$Env:SystemRoot\memory.dmp" -Remove
                If ($PSVersionTable.PSVersion.Major -lt 5) { (New-Object -ComObject Shell.Application).NameSpace(0xA).Items() | ForEach-Object -Process { Remove-Item -Path $_.Path -Force -Recurse -Verbose -ErrorAction SilentlyContinue } }
                Else { Clear-RecycleBin -Force -Confirm:$false -Verbose -ErrorAction SilentlyContinue }
                Remove-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit" -Name LastKey -Force -ErrorAction SilentlyContinue
                Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches" -Exclude "Downloaded Program Files", "DownloadsFolder" -ErrorAction SilentlyContinue | ForEach-Object { Set-ItemProperty -Path $($_.PsPath) -Name $PropertyName -Value 2 -Force -ErrorAction SilentlyContinue }
                $ProcessInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo
                $ProcessInfo.FileName = "$Env:SystemRoot\System32\cleanmgr.exe"
                $ProcessInfo.Arguments = '/SAGERUN:{0}' -f $StateFlags.ToString()
                $ProcessInfo.CreateNoWindow = $true
                $Process = New-Object -TypeName System.Diagnostics.Process
                $Process.StartInfo = $ProcessInfo
                [void]$Process.Start()
                $Process.WaitForExit()
                Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches" -Exclude "Downloaded Program Files", "DownloadsFolder" -ErrorAction SilentlyContinue | ForEach-Object { Remove-ItemProperty -Path $($_.PsPath) -Name $PropertyName -Force -ErrorAction SilentlyContinue }
            }
        }
        End
        {
            $PostClean = Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object -Property DriveType -EQ 3 | Select-Object -Property SystemName,
            @{ Name = "Drive"; Expression = { ($_.DeviceID) } },
            @{ Name = "Size (GB)"; Expression = { "{0:N1}" -f ($_.Size / 1GB) } },
            @{ Name = "FreeSpace (GB)"; Expression = { "{0:N1}" -f ($_.Freespace / 1GB) } },
            @{ Name = "PercentFree"; Expression = { "{0:P1}" -f ($_.FreeSpace / $_.Size) } } | Format-Table -AutoSize | Out-String
            Write-Output "`n`n`t`t`tBefore Clean-up:`n$PreClean" -Verbose
            Write-Output "`t`t`tAfter Clean-up:`n$PostClean" -Verbose
            Stop-Transcript
        }
    }
    Code:
    Function Reset-WindowsUpdate
    {
        <#
        .SYNOPSIS
            Cleans-up Windows Update distribution folders and resets all dependent services to their default state to mitigate any updating problems.
    
        .DESCRIPTION
            Stops all Windows Update dependent services, then proceeds to clean all folders used by Windows Update.
            Regenerates the default file structure that Windows requires for proper WSUS service access and authentication.
            Returns all dependent files, discretionary access control lists and permissions back to their default state.
            Resets the local network.
            Restarts any stopped Windows Update dependent services.
            Optionally, the system will display a progress bar countdown before restarting to further clear any cache storage.
    
        .EXAMPLE
            PS C:\> Reset-WindowsUpdate
    
        .NOTES
            Works with the latest Windows 10 builds and does not revert custom policy settings set in Group Policy.
            It is highly recommended to restart the device after running this function to clear additional system cache reserves and to apply the default service DACLs.
        #>
    
        [CmdletBinding()]
        Param ()
    
        Begin
        {
            $DefaultErrorAction = $ErrorActionPreference
            $ErrorActionPreference = 'SilentlyContinue'
        }
        Process
        {
            Clear-Host
            Write-Host "Stopping the BITS, wuauserv, AppIDSvc and CryptSvc services..." -NoNewline -ForegroundColor Cyan
            Get-Service -Name BITS, wuauserv, AppIDSvc, CryptSvc | Where-Object -Property Status -EQ Running | Stop-Service -Force
            Clear-DnsClientCache
            Write-Host "[Complete]" -ForegroundColor Cyan
            Write-Host "Resetting Update Containers..." -NoNewline -ForegroundColor Cyan
            Remove-Item -Path "$Env:ALLUSERSPROFILE\Application Data\Microsoft\Network\Downloader\qmgr*.dat" -Force
            Remove-Item -Path "$Env:ALLUSERSPROFILE\Microsoft\Network\Downloader\qmgr*.dat" -Force
            Get-ChildItem -Path $Env:SystemRoot\Logs\WindowsUpdate\* | Remove-Item -Recurse -Force
            If (Test-Path -Path $Env:SystemRoot\WinSxS\pending.xml.bak) { Remove-Item -Path $Env:SystemRoot\WinSxS\pending.xml.bak -Force }
            If (Test-Path -Path $Env:SystemRoot\SoftwareDistribution.bak) { Remove-Item -Path $Env:SystemRoot\SoftwareDistribution.bak -Recurse -Force }
            If (Test-Path -Path $Env:SystemRoot\System32\Catroot2.bak) { Remove-Item -Path $Env:SystemRoot\System32\Catroot2.bak -Recurse -Force }
            If (Test-Path -Path $Env:SystemRoot\WindowsUpdate.log.bak) { Remove-Item -Path $Env:SystemRoot\WindowsUpdate.log.bak -Force }
            If (Test-Path -Path $Env:SystemRoot\WinSxS\pending.xml)
            {
                Start-Process -FilePath TAKEOWN -ArgumentList ('/F "{0}"' -f "$Env:SystemRoot\WinSxS\pending.xml") -WindowStyle Hidden -Wait
                Start-Process -FilePath ATTRIB -ArgumentList ('-R -S -H /S /D "{0}"' -f "$Env:SystemRoot\WinSxS\pending.xml") -WindowStyle Hidden -Wait
                Rename-Item -Path $Env:SystemRoot\WinSxS\pending.xml -NewName pending.xml.bak -Force
            }
            If (Test-Path -Path $Env:SystemRoot\SoftwareDistribution)
            {
                Start-Process -FilePath ATTRIB -ArgumentList ('-R -S -H /S /D "{0}"' -f "$Env:SystemRoot\SoftwareDistribution") -WindowStyle Hidden -Wait
                Rename-Item -Path $Env:SystemRoot\SoftwareDistribution -NewName SoftwareDistribution.bak -Force
                If (Test-Path -Path $Env:SystemRoot\SoftwareDistribution) { Write-Warning "Failed to reset the Software Distribution folder."; Break }
            }
            If (Test-Path -Path $Env:SystemRoot\System32\Catroot2)
            {
                Start-Process -FilePath ATTRIB -ArgumentList ('-R -S -H /S /D "{0}"' -f "$Env:SystemRoot\System32\Catroot2") -WindowStyle Hidden -Wait
                Rename-Item -Path $Env:SystemRoot\System32\Catroot2 -NewName Catroot2.bak -Force
            }
            If (Test-Path -Path $Env:SystemRoot\WindowsUpdate.log)
            {
                Start-Process -FilePath ATTRIB -ArgumentList ('-R -S -H /S /D "{0}"' -f "$Env:SystemRoot\WindowsUpdate.log") -WindowStyle Hidden -Wait
                Rename-Item -Path $Env:SystemRoot\WindowsUpdate.log -NewName WindowsUpdate.log.bak -Force
            }
            Write-Host "[Complete]" -ForegroundColor Cyan
            Write-Host "Resetting the wuauserv and BITS discretionary access control list (DACL)..." -NoNewline -ForegroundColor Cyan
            Start-Process -FilePath SC.EXE -ArgumentList ('SDSET wuauserv D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)') -WindowStyle Hidden -Wait
            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-Host "[Complete]" -ForegroundColor Cyan
            Write-Host "Re-registering Update Modules..." -NoNewline -ForegroundColor Cyan
            Set-Location -Path $Env:SystemRoot\System32
            @('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 { Start-Process -FilePath REGSVR32 -ArgumentList ('/S {0}' -f $($_)) -WindowStyle Hidden -Wait }
            Write-Host "Removing Windows Update Client Settings..." -NoNewline -ForegroundColor Cyan
            Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" -Name AccountDomainSid -Force
            Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" -Name PingID -Force
            Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" -Name SusClientId -Force
            Write-Host "Restarting the BITS, wuauserv, AppIDSvc and CryptSvc services..." -NoNewline -ForegroundColor Cyan
            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
            Get-Service -Name BITS, wuauserv, AppIDSvc, CryptSvc, DcomLaunch, TrustedInstaller | Set-Service -StartupType Automatic
            Get-Service -Name BITS, wuauserv, AppIDSvc, CryptSvc | Where-Object -Property Status -EQ Stopped | Start-Service
            Write-Host "[Complete]" -ForegroundColor Cyan
            $Wshell = New-Object -ComObject Wscript.Shell
            $Popup = $Wshell.Popup("Restart $($Env:COMPUTERNAME) complete the reset?", 10, "Restart Device", 4 + 32)
            If ($Popup -eq 6)
            {
                $Restart = 15
                ForEach ($Count In (1 .. $Restart))
                {
                    Write-Progress -Id 1 -Activity "Restarting $($Env:COMPUTERNAME) to complete the reset" -Status "Restarting in $Restart seconds, $($Restart - $Count) seconds left" -PercentComplete (($Count / $Restart) * 100)
                    Start-Sleep -Seconds 1
                }
                Restart-Computer
            }
        }
        End
        {
            [void][Runtime.InteropServices.Marshal]::ReleaseComObject($WShell)
            $ErrorActionPreference = $DefaultErrorAction
        }
    }
     
  2. Hi, how am i suppose to run this?
    i saved it as .ps1 and its not seem to work.
     
  3. GodHand

    GodHand MDL Senior Member

    Jul 15, 2016
    492
    666
    10
    It works perfectly fine. It's a function, so open PowerShell ISE, copy the contents of the .ps1 into the script pane, hit F5 then type the Start-WindowsCleanup or Reset-WindowsUpdate...whichever you want to run.

    Of you can copy and paste all of its contents in a regular elevated PowerShell console shell and type the function command.
     
  4. GodHand

    GodHand MDL Senior Member

    Jul 15, 2016
    492
    666
    10
    [​IMG]

    After hitting F5 in PowerShell ISE, you can see it automatically detects the command and the parameters available to it.

    [​IMG]

    And just hit enter to run it:

    [​IMG]
     
  5. Thanks.. :worthy: just tried Windows cleanup, its really great, i guess i'll just replace the ccleaner with this one. I assume it'll work with win 7 too. btw if its possible please consider to make one click cleaner method for it for the convenience.
     
  6. abbodi1406

    abbodi1406 MDL KB0000001

    Feb 19, 2011
    10,378
    41,556
    340
    I know it's possible to use "dot sourcing" to execute functions from ps1 script
    Code:
    . .\script.ps1
    or
    . C:\Downloads\script.ps1
    Code:
    Start-WindowsCleanup
    i recommend not to remove all SoftwareDistribution contents, that will reset WU
    Download folder and maybe *.log is enough

    thanks :)
     
  7. GodHand

    GodHand MDL Senior Member

    Jul 15, 2016
    492
    666
    10
    I personally prefer resetting Windows Update, as it returns all update programs back to their default settings; however, anyone using the function can simply remove that removal command or replace it with something else :)

    And yes you can dot source the script and run the function. Two dots sources the script so you can follow up with the function command itself in the console if you want to pass additional parameters. You would use a single dot to run a script, not a function.[​IMG]