-
-
Save shvchk/4ad98cc847093ab035cf6502403b8aeb to your computer and use it in GitHub Desktop.
Clean-up system clutter and reclaim disk space
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Function Start-WindowsCleanup | |
{ | |
<# | |
.SYNOPSIS | |
`Start-WindowsCleanup` cleans-up a system clutter and reclaims disk space. | |
.DESCRIPTION | |
The `Start-WindowsCleanup` cmdlet performs the following clean-up tasks to reclaim disk space: | |
Clears the contents of common directories of the Windows file system for both the current running user and the global system that are used to store temporary, logging, backup, cache and dump files. | |
Runs the Windows Disk Clean-up utility automatically as a .NET process by setting the appropriate VolumeCache subkeys on the StateFlags registry property. | |
Removes all VolumeCache subkeys that were previously set by `Start-WindowsCleanup` after the Windows Disk Clean-up utility has completed. | |
Cleans-up the WinSxS Component Store by removing superseded component files and resetting the image base. | |
Logs all clean-up actions to a transcript that is saved in the C:\Windows\Temp directory. | |
.PARAMETER Include | |
Includes user-specific directories, outside of temporary and logging directories, in the clean-up process. The acceptable values for this parameter are: Downloads, RestorePoints, EventLogs, Defender, DuplicateDrivers, IconCache, ThumbnailCache, Chrome, Firefox, IE and Edge. | |
Downloads - Removes all content from all download folders and directories. | |
RestorePoints - Removes all system restore points. | |
EventLogs - Removes all event logs and event tracing log files. | |
Defender - Removes all Windows Defender scan history, log files and definition update backups. | |
DuplicateDrivers - Outputs a Gridview list of any outdated and duplicate drivers for selective removal. | |
IconCache - Resets the icon cache databases. | |
ThumbnailCache - Resets the thumbnail cache databases. | |
Chrome - Removes all cache, cookie, history and logging directories for the Google Chrome web browser. | |
Firefox - Removes all cache, cookie, history and logging directories for the Mozilla Firefox web browser. | |
IE - Removes all cache, cookie, history and logging directories for the Internet Explorer web browser. | |
Edge - Removes all cache, cookie, history and logging directories for the Microsoft Edge web browser. | |
More than one parameter value may be entered at a time. If, for example, you want to also remove all Restore Points, Event Logs and all content from the Chrome, Firefox and Internet Explorer user and system profile directories, the following values would be passed with the parameter: | |
-Include 'RestorePoints', 'EventLogs', 'Chrome', 'Firefox', 'IE' | |
.PARAMETER GUI | |
Outputs a Gridview GUI list of all of the values in the -Include parameter allowing for the selection of items to include in the removal process as opposed to manually entering them. | |
This switch can be used in place of the -Include parameter. | |
.PARAMETER ComponentStore | |
Starts a DISM job on the Component Store. This parameter accepts the following values: | |
Analyze - Analyze the Component Store to determine whether a clean-up of the Component Store is recommended or not. | |
Cleanup - Compresses all superseded components in the component store. | |
ResetBase - Rebases the image and removes all superseded components in the component store. | |
.PARAMETER Additional | |
Removes any user-specific file, folder or directory passed to the parameter when the function is called. This can be a single object or an array of multiple objects. | |
Any items that return an unauthorized access error will have their access control list permissions bypassed for removal. | |
.PARAMETER JSON | |
Uses the VolumeCaches.json file to set the Microsoft Windows Disk Clean-up utility clean-up components in the registry. | |
By default the dynamic string list created by `Start-WindowsCleanup` is used. | |
.EXAMPLE | |
PS C:\> Start-WindowsCleanup | |
This command will clean-up all distribution, logging and temporary content. | |
.EXAMPLE | |
PS C:\> Start-WindowsCleanup -GUI -ComponentStore ResetBase -JSON | |
This command will perform five primary clean-up tasks: | |
1 - Perform an image base reset on the Component Store. | |
2 - Output a Gridview GUI list with selectable content to include in the clean-up process. | |
3 - Remove all distribution, logging and temporary content. | |
4 - Remove any content selected from the Gridview GUI. | |
5 - Use the VolumeCaches.json file to set the registry properties for which components are cleaned-up by the Microsoft Windows Disk Clean-up utility. | |
.EXAMPLE | |
PS C:\> Start-WindowsCleanup -Include 'Defender', 'Downloads', 'RestorePoints', 'EventLogs', 'IconCache', 'ThumbnailCache', 'Chrome', 'FireFox', 'Edge' -Additional 'C:\My Notes', 'C:\Executable', 'D:\MapData' -ComponentStore 'Analyze', 'ResetBase' | |
This command will perform eleven primary clean-up tasks: | |
1 - Remove all Windows Defender scan history, log files and definition update backups. | |
2 - Remove all distribution, logging and temporary content. | |
3 - Remove any downloaded content. | |
4 - Remove all system restore points. | |
5 - Remove all event logs and event tracing log files. | |
6 - Reset the icon cache databases. | |
7 - Reset the thumbnail cache databases. | |
8 - Remove all cache, cookies, logging and temporary files and saved history for Google Chrome, Mozilla FireFox and Microsoft Edge. | |
9 - Remove all additionally added objects. | |
10 - Analyze the Component Store. | |
11 - If verified, perform an image base reset of the Component Store after it has been analyzed. | |
.NOTES | |
The integer value for the StateFlags registry property is randomly created each time the function is run and is not set to a static value. | |
For the -ComponentStore parameter, the 'Analyze' value can be used in conjunction with the 'Cleanup' or 'ResetBase' values (i.e. -ComponentStore 'Analyze', Cleanup'). This allows a Component Store clean-up to follow the analyzing of the Component Store if verified by the end-user. | |
For Windows 10 builds 18362 and above, using the ResetBase value with the -ComponentStore parameter will prompt additional verification before running due to a current bug in these builds that prevents future updates from installing if previous updates have been removed. | |
Performing a clean-up of the Component Store with a rebase of the image (ResetBase) will prevent the 'Reset This PC' function from working. | |
When performing a clean-up of the Component Store, it is not unusual for the progress to return less than 100% completion before continuing and does not mean the clean-up process did not complete. This commonly occurs when the Component Store does not require a clean-up but is cleaned-up anyways. | |
When removing outdated and duplicate drivers, ensure the current device driver is functioning properly as you will not be able to roll back the driver for that specific device. | |
If the removal of outdated and duplicate drivers has been included in the removal process, realize it can take some time for the list of drivers installed on the system to be compiled so just be patient. | |
.NOTES | |
12/06/2020 | |
- A new registry key gets added that contains both the function's last run time as well as the StateFlags integer value. Each time the function is run, it checks this registry key and ensures none of the previous StateFlags' integer values are still present. This will ensure any set StateFlags integer values are removed if the clean-up process is exited or closed before the Windows Disk Clean-up utility has completed. | |
- Included the removal of additional histories and recent documents. | |
- Additional primary function and helper function updates and improvements. | |
08/26/2020 | |
- The OS is now checked before removing shadow copies (restore points) to make sure it is not a version of Windows Server. | |
- Added additional default and Microsoft Edge removal paths. | |
- Added the clearing of the App Compat Cache and CD burning registry keys. | |
08/21/2020 | |
- The clean-up of the icon cache databases no longer requires a system reboot for them to be rebuilt. | |
- The Explorer process is now stopped before removals are processed and restarted after removals have completed. This is to ensure items that are in use by the running system can be removed without requiring a system reboot. | |
- Updated helper functions. | |
- Additional function improvements. | |
08/03/2020 | |
- Added the removal of Windows Defender scan history, log files and definition update backups to the -Include and -GUI parameters. | |
#> | |
[CmdletBinding(DefaultParameterSetName = 'Include', | |
ConfirmImpact = 'High', | |
SupportsShouldProcess = $true)] | |
Param | |
( | |
[Parameter(ParameterSetName = 'Include', | |
Mandatory = $true, | |
Position = 0, | |
HelpMessage = 'Includes the clean-up of Downloads, Restore Points, Event Logs, Defender, Duplicate Drivers, Icon Cache, Thumbnail Cache, Google Chrome, Mozilla Firefox, Internet Explorer and Microsoft Edge.')] | |
[ValidateSet('Downloads', 'RestorePoints', 'EventLogs', 'Defender', 'DuplicateDrivers', 'IconCache', 'ThumbnailCache', 'Chrome', 'Firefox', 'IE', 'Edge')] | |
[String[]]$Include, | |
[Parameter(ParameterSetName = 'GUI', | |
Mandatory = $true, | |
Position = 0, | |
HelpMessage = 'Outputs a Gridview GUI list of all of the values in the -Include parameter allowing for the selection of items to include in the removal process as opposed to manually entering them.')] | |
[Switch]$GUI, | |
[Parameter(Mandatory = $false, | |
HelpMessage = 'Removes all superseded components in the component store, and optionally performs a reset of the image base.')] | |
[ValidateSet('Analyze', 'Cleanup', 'ResetBase')] | |
[String[]]$ComponentStore, | |
[Parameter(Mandatory = $false, | |
HelpMessage = 'Removes any user-specific file, folder or directory passed to the parameter when the function is called. This can be a single object or an array of multiple objects.')] | |
[String[]]$Additional, | |
[Parameter(HelpMessage = 'Uses the VolumeCaches.json file to set the Microsoft Windows Disk Clean-up utility clean-up components in the registry.')] | |
[Switch]$JSON | |
) | |
Begin | |
{ | |
Set-StrictMode -Version Latest | |
# Add the PresentationFramework assembly for any verification pop-up message boxes. | |
Add-Type -AssemblyName PresentationFramework | |
# Check to make sure we are running with administrator permissions. | |
If (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "This script requires elevated access. Please relaunch Start-WindowsCleanup with administrator permissions."; Exit } | |
# Save the pre-processing variable names so they can be restored during post-processing. | |
$DefaultVariables = (Get-Variable).Name | |
# Create helper functions to aid in the clean-up process. | |
Function Remove-Items | |
{ | |
[CmdletBinding()] | |
Param | |
( | |
[Parameter(Mandatory = $true, | |
ValueFromPipeline = $true, | |
ValueFromPipelineByPropertyName = $true)] | |
[Alias('FullName', 'PSPath')] | |
[String[]]$Path, | |
[Switch]$Additional | |
) | |
Process | |
{ | |
ForEach ($Item In $Path) | |
{ | |
$Item = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Item) | |
If ($Additional.IsPresent) | |
{ | |
Try { Remove-Item -Path $Item -Recurse -Force -Verbose -ErrorAction Stop } | |
Catch [UnauthorizedAccessException] | |
{ | |
If ($null -eq $Item) { Continue } | |
$Retries = 5 | |
If ((Get-Item -Path $Item -Force -ErrorAction SilentlyContinue) -is [IO.DirectoryInfo]) { $TAKEOWN = ('TAKEOWN.EXE /F "{0}" /A /R /D Y' -f $Item) } | |
Else { $TAKEOWN = ('TAKEOWN.EXE /F "{0}" /A' -f $Item) } | |
Do | |
{ | |
$RET = $TAKEOWNRET = $ICACLSRET = $ATTRIBRET = $REMRET = $null | |
Write-Verbose ('Performing the operation "{0}"' -f $TAKEOWN) -Verbose | |
$TAKEOWNRET = Invoke-Expression $TAKEOWN | |
$ICACLS = ('ICACLS.EXE "{0}" /INHERITANCE:E /GRANT *S-1-5-32-544:F /T /Q /C /L' -f $Item) | |
Write-Verbose ('Performing the operation "{0}"' -f $ICACLS) -Verbose | |
$ICACLSRET = Invoke-Expression $ICACLS | |
$ATTRIB = ('ATTRIB.EXE -A "{0}" /S /D /L' -f $Item) | |
Write-Verbose ('Performing the operation "{0}"' -f $ATTRIB) -Verbose | |
$ATTRIBRET = Invoke-Expression $ATTRIB | |
Try { Remove-Item -Path $Item -Recurse -Force -Verbose -ErrorAction SilentlyContinue } | |
Catch { $REMRET = $PSItem.Exception.Message } | |
$RET = ($TAKEOWNRET + $ICACLSRET + $ATTRIBRET + $REMRET) | Select-String -Pattern 'Access is denied' | |
} | |
While ($null -ne $RET -or (--$Retries -le 0)) | |
} | |
Catch [Management.Automation.ItemNotFoundException] { } | |
} | |
Else | |
{ | |
Try | |
{ | |
If ((Get-Item -Path $Item -Force -ErrorAction SilentlyContinue) -is [IO.DirectoryInfo]) { Get-ChildItem -Path $Item -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -Verbose -ErrorAction SilentlyContinue } | |
Else { Remove-Item -Path $Item -Recurse -Force -Verbose -ErrorAction SilentlyContinue } | |
} | |
Catch [UnauthorizedAccessException], [Management.Automation.ItemNotFoundException] { } | |
} | |
} | |
} | |
} | |
Function Stop-Running | |
{ | |
[CmdletBinding()] | |
Param | |
( | |
[Parameter(Mandatory = $true, | |
ValueFromPipeline = $true)] | |
[String[]]$Name | |
) | |
Begin | |
{ | |
$Retries = 5 | |
} | |
Process | |
{ | |
ForEach ($Object In $Name) | |
{ | |
$Running = Get-Process | Where-Object -Property Name -Like *$Object* | |
If (!$Running) { $Running = Get-Service | Where-Object -Property Name -EQ $Object } | |
$Running | ForEach-Object -Process { | |
If ($PSItem -is [Diagnostics.Process]) | |
{ | |
If ($PSItem.Name -eq 'explorer') | |
{ | |
While ($PSItem.HasExited -eq $false) { Stop-Process -Name $PSItem.Name -Force -Verbose -ErrorAction SilentlyContinue; $PSItem.Refresh() } | |
} | |
Else | |
{ | |
While ($Retries -gt 0 -and $PSItem.Responding -eq $true) | |
{ | |
Stop-Process -Name $PSItem.Name -Force -Verbose -ErrorAction SilentlyContinue | |
Start-Sleep 1 | |
$PSItem.Refresh() | |
If ($PSItem.Responding -eq $true) { Start-Sleep 5 } | |
$Retries-- | |
} | |
} | |
} | |
ElseIf ($PSItem -is [ServiceProcess.ServiceController]) | |
{ | |
If ($PSItem.Status -ne 'Stopped') | |
{ | |
While ($Retries -gt 0 -and $PSItem.Status -ne 'Stopped') | |
{ | |
Stop-Service -Name $PSItem.Name -Force -Verbose -ErrorAction SilentlyContinue | |
Start-Sleep 1 | |
$PSItem.Refresh() | |
If ($PSItem.Status -eq 'Running') { Start-Sleep 5 } | |
$Retries-- | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
Function Start-ComponentCleanup | |
{ | |
[CmdletBinding()] | |
Param | |
( | |
[Switch]$Analyze, | |
[Switch]$Cleanup, | |
[Switch]$ResetBase | |
) | |
# Get the ResetBase registry property. If it does not exist, create it. | |
Try { $RebaseProperty = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\SideBySide\Configuration" -Name DisableResetbase -ErrorAction Stop } | |
Catch [Management.Automation.PSArgumentException] { $RebaseProperty = New-ItemProperty -LiteralPath "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\SideBySide\Configuration" -Name DisableResetBase -Value 1 -PropertyType DWord -Force -Verbose -ErrorAction SilentlyContinue } | |
# Assign the Windows 10 build number to a variable. | |
$BuildNumber = Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber | |
# If the -ComponentStore parameter is used, assign the name of the DISM log to a variable using Unix time. | |
$DISMLog = Join-Path -Path $Env:TEMP -ChildPath ('Dism{0}Image_{1}.log' -f ($PSBoundParameters.Keys | Select-Object -First 1), (Get-Date -Date (Get-Date).ToUniversalTime() -UFormat %s -Millisecond 0)) | |
# Assign the clean-up variables to null values. | |
$CleanupScriptBlock = $null | |
$CleanupString = $null | |
$DISMJob = $null | |
# Assign the default sleep duration to 5 seconds. | |
$SleepDuration = 5 | |
Switch ($PSBoundParameters.Keys) | |
{ | |
'Analyze' | |
{ | |
# Create a scriptblock for a Component Store analyzation. | |
$SleepDuration = 10 | |
$CleanupScriptBlock = { Param ($DISMLog) Dism.exe /Online /Cleanup-Image /AnalyzeComponentStore | Out-File -FilePath $DISMLog } | |
$CleanupString = 'Performing the operation "Dism.exe /Online /Cleanup-Image /AnalyzeComponentStore"' | |
Break | |
} | |
'Cleanup' | |
{ | |
# Create a scriptblock for a Component Store clean-up. | |
$RebaseProperty = Set-ItemProperty -Path $RebaseProperty.PSPath -Name DisableResetBase -Value 1 -Force -PassThru -Verbose -ErrorAction SilentlyContinue | |
$CleanupScriptBlock = { Param ($DISMLog) Dism.exe /Online /Cleanup-Image /StartComponentCleanup | Out-File -FilePath $DISMLog } | |
$CleanupString = 'Performing the operation "Dism.exe /Online /Cleanup-Image /StartComponentCleanup"' | |
Break | |
} | |
'ResetBase' | |
{ | |
# Create a scriptblock for a Component Store clean-up with image base reset. | |
If ($BuildNumber -ge 18362) | |
{ | |
# Before performing an image base reset on a Windows build equal to or greater than 18362, which can cause future updates from failing to install, request secondary verification. | |
$VerifyResetBase = [Windows.MessageBox]::Show('Are you sure you want to reset the image base for build [{0}]?' -f $BuildNumber, 'Verify Image Base Reset', 'YesNo', 'Question', 'No') | |
If ($VerifyResetBase -eq [Windows.MessageBoxResult]::Yes) | |
{ | |
$RebaseProperty = Set-ItemProperty -Path $RebaseProperty.PSPath -Name DisableResetBase -Value 0 -Force -PassThru -Verbose -ErrorAction SilentlyContinue | |
$CleanupScriptBlock = { Param ($DISMLog) Dism.exe /Online /Cleanup-Image /StartComponentCleanup /ResetBase | Out-File -FilePath $DISMLog } | |
$CleanupString = 'Performing the operation "Dism.exe /Online /Cleanup-Image /StartComponentCleanup /ResetBase"' | |
} | |
} | |
Else | |
{ | |
$RebaseProperty = Set-ItemProperty -Path $RebaseProperty.PSPath -Name DisableResetBase -Value 0 -Force -PassThru -Verbose -ErrorAction SilentlyContinue | |
$CleanupScriptBlock = { Param ($DISMLog) Dism.exe /Online /Cleanup-Image /StartComponentCleanup /ResetBase | Out-File -FilePath $DISMLog } | |
$CleanupString = 'Performing the operation "Dism.exe /Online /Cleanup-Image /StartComponentCleanup /ResetBase"' | |
} | |
Break | |
} | |
} | |
# Start a PowerShell DISM job using the Component Store clean-up scriptblock. | |
Write-Verbose $CleanupString -Verbose | |
$DISMJob = Start-Job -ScriptBlock $CleanupScriptBlock -ArgumentList $DISMLog -ErrorAction SilentlyContinue | |
Do | |
{ | |
Start-Sleep $SleepDuration | |
Get-Content -Path $DISMLog -Tail 3 | Select-String -Pattern % | Select-Object -Last 1 | |
} | |
While ((Get-Job -Id $DISMJob.Id).State -eq 'Running') | |
If ((Get-Job -Id $DISMJob.Id).State -eq 'Completed') | |
{ | |
If ($Analyze.IsPresent) | |
{ | |
If ((Get-Content -Path $DISMLog -Raw).Contains('Component Store Cleanup Recommended : No')) { $AnalyzeMessage = 'Component Store Clean-up not recommended. Clean-up anyway?' } | |
Else { $AnalyzeMessage = 'Component Store Clean-up recommended. Continue with clean-up?' } | |
$DISMJob | Remove-Job -ErrorAction SilentlyContinue | |
$DISMLog | Remove-Item -Force -ErrorAction SilentlyContinue | |
# Verify proceeding with a clean-up of the Component Store by returning the results of the Component Store analyzation. | |
$VerifyCleanup = [Windows.MessageBox]::Show($AnalyzeMessage, 'Clean-up Component Store', 'YesNo', 'Question', 'No') | |
If ($VerifyCleanup -eq [Windows.MessageBoxResult]::Yes) | |
{ | |
# If the Component Store clean-up has been verified, remove the 'Analyze' parameter and re-run the function with the other parameters passed to it. | |
$DISMLog | Remove-Item -Force -ErrorAction SilentlyContinue | |
[Void]$PSBoundParameters.Remove('Analyze') | |
Start-ComponentCleanup @PSBoundParameters | |
} | |
} | |
Else | |
{ | |
$DISMErrors = Get-Content -Path $DISMLog -Raw | Select-String -Pattern Error | |
If ($null -ne $DISMErrors) { $DISMErrors | Out-File -FilePath (Join-Path -Path $([Environment]::GetFolderPath('Desktop')) -ChildPath "DismCleanupErrors_$(Get-Date -Format 'yyyy-MM-dd-HHmmss').log") } | |
} | |
} | |
$DISMJob | Remove-Job -ErrorAction SilentlyContinue | |
$DISMLog | Remove-Item -Force -ErrorAction SilentlyContinue | |
Clear-Host | |
} | |
# If the -ComponentStore parameter is used and there are pending installations, request a system reboot. | |
If ($PSBoundParameters.ContainsKey('ComponentStore') -and (Test-Path -Path "$Env:SystemRoot\WinSxS\pending.xml")) | |
{ | |
Write-Warning "Cannot clean-up the Component Store while installations are pending. Please reboot the system and install any pending installations before cleaning-up the Component Store." | |
Break | |
} | |
# Assign the local and global paths to their own variables for easier path building. | |
$GlobalAppData = $Env:APPDATA | |
$LocalAppData = $Env:LOCALAPPDATA | |
$RootAppData = "$(Split-Path -Path $LocalAppData)\*" | |
# Create the object list of default items that will be cleaned-up. | |
$RemovalList = [Collections.Generic.List[Object]]@( | |
$Env:TEMP, | |
(Join-Path -Path $RootAppData -ChildPath Temp), | |
(Join-Path -Path $LocalAppData -ChildPath "IsolatedStorage\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Windows\Burn\Burn\desktop.ini"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Windows\WER"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Terminal Server Client\Cache"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Terminal Server Client\Cache"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Terminal Server Client\Cache"), | |
(Join-Path -Path $Env:ProgramData -ChildPath "Microsoft\Windows\RetailDemo"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "debug\WIA\*.log"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath drivers), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "INF\*.log*"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "Logs\CBS\*Persist*"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "Logs\DISM"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "Logs\dosvc\*.*"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "Logs\MeasuredBoot\*.log"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "Logs\NetSetup\*.*"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "Logs\SIH\*.*"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "Logs\WindowsBackup\*.etl"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath minidump), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "Panther\UnattendGC\*.log"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath Prefetch), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "security\logs\*.*"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath Temp), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "WinSxS\ManifestCache\*"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "*.log"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "*.dmp"), | |
(Join-Path -Path $Env:SystemDrive -ChildPath "*.dmp"), | |
(Join-Path -Path $Env:SystemDrive -ChildPath "File*.chk"), | |
(Join-Path -Path $Env:SystemDrive -ChildPath "Found.*\*.chk"), | |
(Join-Path -Path $Env:SystemDrive -ChildPath "LiveKernelReports\*.dmp"), | |
(Join-Path -Path $Env:SystemDrive -ChildPath swsetup), | |
(Join-Path -Path $Env:SystemDrive -ChildPath swtools), | |
(Join-Path -Path $Env:SystemDrive -ChildPath "Windows.old"), | |
(Join-Path -Path $Env:HOMEDRIVE -ChildPath "Config.Msi"), | |
(Join-Path -Path $Env:HOMEDRIVE -ChildPath "inetpub\logs\LogFiles"), | |
(Join-Path -Path $Env:HOMEDRIVE -ChildPath Intel), | |
(Join-Path -Path $Env:HOMEDRIVE -ChildPath PerfLogs) | |
) | |
# Check if the registry property for the last run session for `Start-WindowsCleanup` is present. If it is not, create it and assign it to a variable. | |
Try { $CleanupProperty = Get-ItemProperty -Path "HKLM:\SOFTWARE\WindowsCleanup" -ErrorAction Stop } | |
Catch [Management.Automation.ItemNotFoundException] { $CleanupProperty = New-Item -Path "HKLM:\SOFTWARE\WindowsCleanup" -ItemType Directory -Force | Get-ItemProperty } | |
If ($CleanupProperty) | |
{ | |
# Check if the registry property for the last run session for `Start-WindowsCleanup` has a StateFlags property value. If those property values are still present in the VolumeCaches, remove them. | |
If ($CleanupProperty.StateFlags) { Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches -ErrorAction SilentlyContinue | Get-ItemProperty | Where-Object { $CleanupProperty.StateFlags } | Remove-ItemProperty -Name ('StateFlags{0}' -f $CleanupProperty.StateFlags) -Force -ErrorAction SilentlyContinue } | |
} | |
# Generate the StateFlags integer string and set it to its registry property name. | |
$StateFlags = [Convert]::ToString($(Get-Random -Minimum 1 -Maximum 9999)) | |
$PropertyName = "StateFlags{0:D4}" -f $StateFlags | |
# Set the `Start-WindowsCleanup` registry properties to reflect the current run session and StateFlags property name. | |
Set-ItemProperty -Path "HKLM:\SOFTWARE\WindowsCleanup" -Name LastRun -Value $(Get-Date -UFormat "%m/%d/%Y %r") -Force -PassThru | Set-ItemProperty -Name StateFlags -Value $StateFlags -Force | |
# Assign the name of the transcript log to a variable using Unix time. | |
$Transcript = Join-Path -Path $Env:SystemRoot\Temp -ChildPath "Start-WindowsCleanup_$(Get-Date -Date (Get-Date).ToUniversalTime() -UFormat %s -Millisecond 0).log" | |
# Create an empty string list that will be populated with items to be excluded from clean-up processes. | |
$ExcludedList = [Collections.Generic.List[String]]@() | |
# If the -JSON switch has been used, assign any properties set to False to the excluded list. | |
If ($PSBoundParameters.ContainsKey('JSON') -and (Test-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath VolumeCaches.json))) | |
{ | |
Try { $VolumeCachesJSON = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath VolumeCaches.json) -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop } | |
Catch { Write-Warning $PSItem.ToString(); Break } | |
$ExcludedList.Add(($VolumeCachesJSON | Where-Object -Property Clean -EQ $false | Select-Object -ExpandProperty Component)) | |
} | |
Else { $VolumeCachesJSON = $null } | |
# Temporarily, add the downloads folder to the exclusion list. | |
If ($ExcludedList -notcontains 'DownloadsFolder') { $ExcludedList.Add('DownloadsFolder') } | |
# If the running system is Windows 10 build 18362 or greater, add update cleanup to the default excluded list. | |
If ((Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 18362 -and $ExcludedList -notcontains 'Update Cleanup') { $ExcludedList.Add('Update Cleanup') } | |
# Assign the Wuau service variable to a null value. | |
$WuauServ = $null | |
# Assign the AutoRestartShell registry property to a variable. | |
$AutoRestartShell = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoRestartShell -ErrorAction SilentlyContinue | |
# Save the paths of any opened directories before `Start-WindowsCleanup` begins processing so they can be automatically restored upon completion. | |
$OpenedDirectoryPaths = (New-Object -ComObject Shell.Application).Windows() | ForEach-Object -Process { $PSItem.Document.Folder.Self.Path } | |
# Assign the pre-cleanup storage state to a variable. | |
$PreClean = (Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object -Property DriveType -EQ 3 | Select-Object -Property @{ Name = 'Drive'; Expression = { ($PSItem.DeviceID) } }, | |
@{ Name = 'Size (GB)'; Expression = { '{0:N1}' -f ($PSItem.Size / 1GB) } }, | |
@{ Name = 'FreeSpace (GB)'; Expression = { '{0:N1}' -f ($PSItem.Freespace / 1GB) } }, | |
@{ Name = 'PercentFree'; Expression = { '{0:P1}' -f ($PSItem.FreeSpace / $PSItem.Size) } } | Format-Table -AutoSize | Out-String).Trim() | |
} | |
Process | |
{ | |
If (!$PSCmdlet.ShouldProcess($Env:COMPUTERNAME, 'Start-WindowsCleanup')) { Break } | |
Clear-Host | |
Start-Transcript -Path $Transcript | |
Switch ($PSCmdlet.ParameterSetName) | |
{ | |
'GUI' | |
{ | |
# If the GUI parameter set name is present, create an output Gridview list allowing for the selection of items to include in the removal process. | |
$IncludeList = [Ordered]@{ | |
Downloads = 'Removes all content from all download folders and directories.' | |
RestorePoints = 'Removes all system restore points.' | |
EventLogs = 'Removes all event logs and event tracing log files.' | |
Defender = 'Removes all Windows Defender scan history, log files and definition update backups.' | |
DuplicateDrivers = 'Outputs a Gridview list of any outdated and duplicate drivers for selective removal.' | |
IconCache = 'Resets all icon cache databases.' | |
ThumbnailCache = 'Resets all thumbnail cache databases.' | |
Chrome = 'Removes all cache, cookie, history and logging directories for the Google Chrome web browser.' | |
Firefox = 'Removes all cache, cookie, history and logging directories for the Mozilla Firefox web browser.' | |
IE = 'Removes all cache, cookie, history and logging directories for the Internet Explorer web browser.' | |
Edge = 'Removes all cache, cookie, history and logging directories for the Microsoft Edge web browser.' | |
} | |
$IncludeList = $IncludeList.Keys | Select-Object -Property @{ Label = 'Name'; Expression = { $PSItem } }, @{ Label = 'Description'; Expression = { $IncludeList[$PSItem] } } | Out-GridView -Title "Select items to include in the clean-up process." -PassThru | |
$IncludeList = $IncludeList.GetEnumerator().Name | |
Break | |
} | |
Default { $IncludeList = $Include; Break } | |
} | |
If ($PSBoundParameters.ContainsKey('ComponentStore') -and ([Environment]::OSVersion.Version).Major -gt 6) | |
{ | |
If ($ComponentStore -contains 'Analyze') | |
{ | |
If ($ComponentStore -contains 'Cleanup') { Start-ComponentCleanup -Analyze -Cleanup } | |
ElseIf ($ComponentStore -contains 'ResetBase') { Start-ComponentCleanup -Analyze -ResetBase } | |
} | |
Else | |
{ | |
If ($ComponentStore -eq 'Cleanup') { Start-ComponentCleanup -Cleanup } | |
ElseIf ($ComponentStore -eq 'ResetBase') { Start-ComponentCleanup -ResetBase } | |
} | |
} | |
If ($IncludeList) | |
{ | |
Switch ($IncludeList) | |
{ | |
'Downloads' | |
{ | |
# Before adding all downloaded content to the removal list, request secondary verification to insure the parameter value was not issued by accident. | |
$VerifyRemoveDownloads = [Windows.MessageBox]::Show('Are you sure you want to remove all downloads?', 'Verify Removal', 'YesNo', 'Question', 'No') | |
If ($VerifyRemoveDownloads -eq [Windows.MessageBoxResult]::Yes) { [Void]$ExcludedList.Remove('DownloadsFolder') } | |
# If the removal of downloads has been verified, add all download folders and directories to the removal list. | |
If ($ExcludedList -notcontains 'DownloadsFolder') | |
{ | |
$RemovalList.Add(( | |
(Join-Path -Path $RootAppData -ChildPath "Downloads\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Downloads\*"), | |
(Join-Path -Path $Env:SystemDrive -ChildPath "Users\Administrator\Downloads\*"), | |
(Join-Path -Path (Get-ItemPropertyValue -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" -Name "{374DE290-123F-4565-9164-39C4925E467B}" -ErrorAction SilentlyContinue) -ChildPath "\*") | |
)) | |
} | |
} | |
'RestorePoints' | |
{ | |
# Windows Server uses its integrated scheduled backup feature as shadow copies, removing them would actually delete the scheduled full disk backups that are created. | |
If ((Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -ExpandProperty Caption) -notlike "Microsoft Windows Server*") | |
{ | |
# Remove all system shadow copies if the -Include parameter with the 'RestorePoints' value is used and the running system is not a Windows Server. | |
If (Get-CimInstance -ClassName Win32_ShadowCopy) | |
{ | |
Get-CimInstance -ClassName Win32_ShadowCopy -Verbose:$false | ForEach-Object -Process { | |
Write-Verbose ('Performing the operation "Delete ShadowCopy" on target "{0}"' -f $PSItem.ID) -Verbose | |
$PSItem | Remove-CimInstance -Verbose:$false | |
} | |
} | |
} | |
} | |
'EventLogs' | |
{ | |
# Remove all event logs and event tracer log files if the -Include parameter with the 'EventLogs' value is used. | |
Get-WinEvent -ListLog * | Where-Object { $PSItem.IsEnabled -eq $true -and $PSItem.RecordCount -gt 0 } | ForEach-Object -Process { | |
Write-Verbose ('Performing the operation "ClearLog" on target "{0}"' -f $PSItem.LogName) -Verbose | |
[Diagnostics.Eventing.Reader.EventLogSession]::GlobalSession.ClearLog($PSItem.LogName) | |
} 2> $null | |
} | |
'Defender' | |
{ | |
# Remove all Windows Defender scan history, log files and definition update backups if the -Include parameter with the 'Defender' value is used. | |
$RemovalList.Add(( | |
(Join-Path -Path $Env:ProgramData -ChildPath "Microsoft\Windows Defender\Scans\History\Results\Resource\*"), | |
(Join-Path -Path $Env:ProgramData -ChildPath "Microsoft\Windows Defender\Scans\History\Service\*.log"), | |
(Join-Path -Path $Env:ProgramData -ChildPath "Microsoft\Windows Defender\Support\*.log"), | |
(Join-Path -Path $Env:ProgramData -ChildPath "Microsoft\Windows Defender\Definition Updates\Backup\*") | |
)) | |
} | |
'DuplicateDrivers' | |
{ | |
# Remove all outdated and duplicate drivers if the -Include parameter with the 'DuplicateDrivers' value is used. | |
Write-Verbose "Compiling a list of any outdated and duplicate system drivers." -Verbose | |
$AllDrivers = Get-WindowsDriver -Online -All | Where-Object -Property Driver -Like oem*inf | Select-Object -Property @{ Name = 'OriginalFileName'; Expression = { $PSItem.OriginalFileName | Split-Path -Leaf } }, Driver, ClassDescription, ProviderName, Date, Version | |
$DuplicateDrivers = $AllDrivers | Group-Object -Property OriginalFileName | Where-Object -Property Count -GT 1 | ForEach-Object -Process { $PSItem.Group | Sort-Object -Property Date -Descending | Select-Object -Skip 1 } | |
If ($DuplicateDrivers) | |
{ | |
$DuplicateDrivers | Out-GridView -Title 'Remove Duplicate Drivers' -PassThru | ForEach-Object -Process { | |
$Driver = $PSItem.Driver.Trim() | |
Write-Verbose ('Performing the action "Delete Driver" on target {0}' -f $Driver) -Verbose | |
Start-Process -FilePath PNPUTIL -ArgumentList ('/Delete-Driver {0} /Force' -f $Driver) -WindowStyle Hidden -Wait | |
} | |
} | |
} | |
'Chrome' | |
{ | |
# Add all Google Chrome cache, cookie, history and logging directories if the -Include parameter with the 'Chrome' value is used. | |
'chrome' | Stop-Running | |
$RemovalList.Add(( | |
(Join-Path -Path $RootAppData -ChildPath "Google\Chrome\User Data\Default\Cache*\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Google\Chrome\User Data\Default\Cache*\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Google\Chrome\User Data\Default\Cookies\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Google\Chrome\User Data\Default\Cookies\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Google\Chrome\User Data\Default\Media Cache\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Google\Chrome\User Data\Default\Media Cache\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Google\Chrome\User Data\Default\Cookies-Journal\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Google\Chrome\User Data\Default\Cookies-Journal\*") | |
)) | |
} | |
'Firefox' | |
{ | |
# Add all Mozilla Firefox cache, cookie, history and logging directories if the -Include parameter with the 'Firefox' value is used. | |
'firefox' | Stop-Running | |
$RemovalList.Add(( | |
(Join-Path -Path $RootAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\Cache*\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\Cache*\*"), | |
(Join-Path -Path $GlobalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\Cache*\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\jumpListCache\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\jumpListCache\*"), | |
(Join-Path -Path $GlobalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\jumpListCache\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\thumbnails\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\thumbnails\*"), | |
(Join-Path -Path $GlobalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\*sqlite*"), | |
(Join-Path -Path $RootAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\*.log"), | |
(Join-Path -Path $LocalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\*.log"), | |
(Join-Path -Path $GlobalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\*.log"), | |
(Join-Path -Path $RootAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\storage\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\storage\*"), | |
(Join-Path -Path $GlobalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\storage\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Mozilla\Firefox\Crash Reports\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Mozilla\Firefox\Crash Reports\*"), | |
(Join-Path -Path $GlobalAppData -ChildPath "Mozilla\Firefox\Crash Reports\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\startupCache\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\startupCache\*"), | |
(Join-Path -Path $GlobalAppData -ChildPath "Mozilla\Firefox\Profiles\*.default\datareporting\*") | |
)) | |
} | |
'IE' | |
{ | |
# Add all Internet Explorer cache, cookie, history and logging directories if the -Include parameter with the 'IE' value is used. | |
'iexplore' | Stop-Running | |
$RemovalList.Add(( | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Internet Explorer\*.log"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Internet Explorer\*.log"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Internet Explorer\*.txt"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Internet Explorer\*.txt"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Internet Explorer\CacheStorage\*.*"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Internet Explorer\CacheStorage\*.*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Windows\INetCache\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Windows\INetCache\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Windows\Temporary Internet Files\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Windows\Temporary Internet Files\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Windows\IECompatCache\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Windows\IECompatCache\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Windows\IECompatUaCache\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Windows\IECompatUaCache\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Windows\IEDownloadHistory\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Windows\IEDownloadHistory\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Windows\INetCookies\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Microsoft\Windows\INetCookies\*") | |
)) | |
} | |
'Edge' | |
{ | |
# Add Microsoft Edge HTML and Microsoft Edge Chromium cache, cookie, history and logging directories if the -Include parameter with the 'Edge' value is used. | |
'msedge', 'MicrosoftEdge*' | Stop-Running | |
If (Get-AppxPackage -Name Microsoft.MicrosoftEdge | Select-Object -ExpandProperty PackageFamilyName) | |
{ | |
$EdgePackageName = Get-AppxPackage -Name Microsoft.MicrosoftEdge | Select-Object -ExpandProperty PackageFamilyName | |
$RemovalList.Add(( | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\AC\#!00*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\AC\#!00*"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\AC\Temp\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\AC\Temp\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\AC\Microsoft\Cryptnet*Cache\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\AC\Microsoft\Cryptnet*Cache\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\Cookies\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\Cookies\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\UrlBlock\*.tmp"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\UrlBlock\*.tmp"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\User\Default\ImageStore\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\User\Default\ImageStore\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\User\Default\Recovery\Active\*.dat"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\User\Default\Recovery\Active\*.dat"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\User\Default\DataStore\Data\nouser1\*\DBStore\LogFiles\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\User\Default\DataStore\Data\nouser1\*\DBStore\LogFiles\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\User\Default\DataStore\Data\nouser1\*\Favorites\*.ico"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\AC\MicrosoftEdge\User\Default\DataStore\Data\nouser1\*\Favorites\*.ico"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\AppData\User\Default\Indexed DB\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\AppData\User\Default\Indexed DB\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\TempState\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\TempState\*"), | |
(Join-Path -Path $RootAppData -ChildPath "Packages\$EdgePackageName\LocalState\Favicons\PushNotificationGrouping\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Packages\$EdgePackageName\LocalState\Favicons\PushNotificationGrouping\*") | |
)) | |
} | |
If ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe" -ErrorAction Ignore) -or @((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction Ignore | Where-Object -Property DisplayName -EQ 'Microsoft Edge'), (Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction Ignore | Where-Object -Property DisplayName -EQ 'Microsoft Edge'))) | |
{ | |
$RemovalList.Add(( | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\*.pma"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\BrowserMetrics\*.pma"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\CrashPad\metadata"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\BudgetDatabase"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Cache\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Code Cache\js\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Code Cache\wasm\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Cookies"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\data_reduction_proxy_leveldb\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Extension State\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Favicons\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Feature Engagement Package\AvailabilityDB\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Feature Engagement Package\EventDB\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\File System\000\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\File System\Origins\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\IndexedDB\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Service Worker\CacheStorage\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Service Worker\Database\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Service Worker\ScriptCache\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Current Tabs"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Last Tabs"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\History"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\History Provider Cache"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\History-journal"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Network Action Predictor"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Top Sites"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Visited Links"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Login Data"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\CURRENT"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\LOCK"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\MANIFEST-*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\*.log"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\*.log"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\*\*.log"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\*\*log*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\*\MANIFEST-*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Shortcuts"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\QuotaManager"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Web Data"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Current Session"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Last Session"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Session Storage\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Site Characteristics Database\*"), | |
(Join-Path -Path $LocalAppData -ChildPath "Microsoft\Edge\User Data\Profile *\Sync Data\LevelDB\*"), | |
(Join-Path -Path $Env:ProgramData -ChildPath "Microsoft\EdgeUpdate\Log\*"), | |
(Join-Path -Path ${Env:ProgramFiles(x86)} -ChildPath "Microsoft\Edge\Application\SetupMetrics\*.pma"), | |
(Join-Path -Path ${Env:ProgramFiles(x86)} -ChildPath "Microsoft\EdgeUpdate\Download\*") | |
)) | |
} | |
} | |
} | |
If ($ExcludedList -notcontains 'Update Cleanup') | |
{ | |
# If 'Update Cleanup' has not been excluded, add the SoftwareDistribution and logging directories. | |
$RemovalList.Add(( | |
(Join-Path -Path $Env:SystemRoot -ChildPath "SoftwareDistribution\Download\*"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "SoftwareDistribution\DataStore\Logs\*.*"), | |
(Join-Path -Path $Env:SystemRoot -ChildPath "Logs\WindowsUpdate\*.*"), | |
(Join-Path -Path $Env:ProgramData -ChildPath "USOShared\Logs\*.*") | |
)) | |
} | |
If ($IncludeList.Contains('IconCache') -or $IncludeList.Contains('ThumbnailCache')) | |
{ | |
# Disable the AutoRestartShell to prevent Explorer from automatically restarting so any cache databases that are in use by the running system can be removed without requiring a system reboot. | |
$AutoRestartShell = Set-ItemProperty -Path $AutoRestartShell.PSPath -Name AutoRestartShell -Value 0 -Force -PassThru -Verbose -ErrorAction SilentlyContinue | |
'explorer' | Stop-Running | |
Switch ($IncludeList) | |
{ | |
'IconCache' | |
{ | |
If (@($LocalAppData, (Join-Path -Path $LocalAppData -ChildPath 'Microsoft\Windows\Explorer')) | Test-Path -Filter iconcache*.db -ErrorAction SilentlyContinue) | |
{ | |
# Reset the icon cache databases if the -Include parameter with the 'IconCache' value was used. | |
Invoke-Expression -Command ('IE4UINIT.EXE -SHOW') | |
@($LocalAppData, (Join-Path -Path $LocalAppData -ChildPath 'Microsoft\Windows\Explorer')) | Get-ChildItem -Filter iconcache*.db -Force -ErrorAction SilentlyContinue | Remove-Items | |
} | |
} | |
'ThumbnailCache' | |
{ | |
If ((Join-Path -Path $LocalAppData -ChildPath 'Microsoft\Windows\Explorer') | Test-Path -Filter thumbcache_*.db -ErrorAction SilentlyContinue) | |
{ | |
# Reset the thumbnail cache databases if the -Include parameter with the 'IconCache' value was used. | |
(Join-Path -Path $LocalAppData -ChildPath 'Microsoft\Windows\Explorer') | Get-ChildItem -Filter thumbcache_*.db -Force -ErrorAction SilentlyContinue | Remove-Items | |
} | |
} | |
} | |
} | |
} | |
If ($ExcludedList -notcontains 'Update Cleanup') | |
{ | |
# If 'Update Cleanup' has not been excluded and the Windows Update Servie is running, stop the Windows Update Service. | |
If ((Get-Service -Name wuauserv -ErrorAction SilentlyContinue).Status -eq 'Running') { $WuauServ = $true; 'wuauserv' | Stop-Running } | |
} | |
If ($AutoRestartShell.AutoRestartShell -ne 0) | |
{ | |
# Disable the AutoRestartShell to prevent Explorer from automatically restarting for the removal process so any items that may be in use by the system can be removed without a system reboot. | |
$AutoRestartShell = Set-ItemProperty -Path $AutoRestartShell.PSPath -Name AutoRestartShell -Value 0 -Force -PassThru -Verbose -ErrorAction SilentlyContinue | |
} | |
# Stop the Explorer process so any items that are currently in use can be removed without a reboot. | |
'explorer' | Stop-Running | |
# Remove all content from each path added to the array list. | |
$RemovalList | Remove-Items | |
If ($PSBoundParameters.ContainsKey('Additional')) | |
{ | |
# Remove any additional files, folders or directories that were passed with the -Additional parameter. | |
$Additional | Remove-Items -Additional | |
} | |
# Remove any junk folders from the Windows directory. | |
Get-ChildItem -Path $Env:SystemRoot -Directory -Force -ErrorAction SilentlyContinue | Where-Object { ($PSItem.Name -match "^\{\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\}$") -and ($PSItem.Name -notmatch "win|prog|res|rec|driv") } | Remove-Items | |
# Remove any junk folders from the root directory. | |
Get-ChildItem -Path $Env:SystemDrive -Directory -Force -ErrorAction SilentlyContinue | Where-Object { ($PSItem.Name -notmatch "win|prog|res|rec|driv") -and ($PSItem.Name -match "^[a-z0-9]{15,}$") -and ((("$($PSItem.Name)" -replace '[0-9]', '').Length * .9) -lt ("$($PSItem.Name)" -replace '[^0-9]', '').Length) } | Remove-Items | |
# Remove all Recycle Bin items from all system drives. This method is more thorough than using its shell object or the Clear-RecycleBin cmdlet. | |
Get-PSDrive -PSProvider FileSystem | ForEach-Object -Process { | |
$RecycleBin = Join-Path -Path $PSItem.Root -ChildPath '$Recycle.Bin\' | |
If (Test-Path -Path $RecycleBin) { Get-ChildItem -Path (Join-Path -Path $RecycleBin -ChildPath '*\$I*') -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object -Process { $($PSItem.FullName.Replace('$I', '$R')), $($PSItem.FullName) | Remove-Items } } | |
} | |
# Clear the CD burning cache. | |
Remove-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\CD Burning\StagingInfo" -Recurse -Force -Verbose -ErrorAction SilentlyContinue | |
# Clear the recent document history for WordPad. | |
Remove-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Applets\Wordpad\Recent File List" -Recurse -Force -Verbose -ErrorAction SilentlyContinue | |
# Clear the recently opened history for Paint. | |
Remove-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Applets\Paint\Recent File List" -Recurse -Force -Verbose -ErrorAction SilentlyContinue | |
# Clear the Run history. | |
Remove-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU" -Recurse -Force -Verbose -ErrorAction SilentlyContinue | |
# Clear the Search history. | |
Remove-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\WordWheelQuery" -Recurse -Force -Verbose -ErrorAction SilentlyContinue | |
# Clear the recent Documents list. | |
Remove-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs" -Recurse -Force -Verbose -ErrorAction SilentlyContinue | |
Join-Path -Path $GlobalAppData -ChildPath 'Microsoft\Windows\Recent\AutomaticDestinations\*' | Remove-Items | |
# Clear the last accessed registry key for RegEdit. | |
Remove-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit -Name LastKey -Force -Verbose -ErrorAction SilentlyContinue | |
# Clear the App Compat Cache. | |
Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache" -Name 'AppCompatCache', 'CacheMainSdb', 'SdbTime' -Force -Verbose -ErrorAction SilentlyContinue | |
# Remove all data in all data and hash cache files. | |
If (Get-Module -Name BranchCache) { Clear-BCCache -Force -Verbose -ErrorAction SilentlyContinue } | |
# Set the AutoRestartShell back to its default value. | |
$AutoRestartShell = Set-ItemProperty -Path $AutoRestartShell.PSPath -Name AutoRestartShell -Value 1 -Force -PassThru -Verbose -ErrorAction SilentlyContinue | |
# Restart the Explorer process. | |
Start-Process -FilePath explorer -Wait -Verbose -ErrorAction SilentlyContinue | |
If ($null -ne $VolumeCachesJSON) | |
{ | |
# If the -JSON switch is used and the VolumeCaches.json file is present, use it to set the Microsoft Windows Disk Clean-up utility clean-up components' StateFlag property in the registry. | |
$VolumeCachesList = $VolumeCachesJSON | Where-Object -Property Clean -EQ $true | Select-Object -ExpandProperty Component | |
$VolumeCacheProperties = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches -Name -ErrorAction SilentlyContinue | Where-Object { $PSItem -in $VolumeCachesList } -ErrorAction SilentlyContinue | Set-ItemProperty -Name $PropertyName -Value 2 -Force -Verbose -PassThru -ErrorAction SilentlyContinue | |
} | |
Else | |
{ | |
# Else use the string list created by the `Start-WindowsCleanup` function to set the Microsoft Windows Disk Clean-up utility clean-up components' StateFlag property in the registry. | |
$VolumeCacheProperties = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches -Exclude $ExcludedList -ErrorAction SilentlyContinue | Set-ItemProperty -Name $PropertyName -Value 2 -Force -Verbose -PassThru -ErrorAction SilentlyContinue | |
} | |
# Start the Microsoft Windows Disk Clean-up utility in advanced mode as a .NET process. | |
Try | |
{ | |
$ProcessInfo = New-Object -TypeName Diagnostics.ProcessStartInfo -ErrorAction Stop | |
$ProcessInfo.FileName = '{0}' -f "$Env:SystemRoot\System32\cleanmgr.exe" | |
$ProcessInfo.Arguments = '/SAGERUN:{0}' -f $StateFlags | |
$ProcessInfo.CreateNoWindow = $true | |
$Process = New-Object -TypeName Diagnostics.Process -ErrorAction Stop | |
$Process.StartInfo = $ProcessInfo | |
Write-Verbose "Running the Windows Disk Clean-up utility in advanced mode as a .NET process." -Verbose | |
[Void]$Process.Start() | |
$Process.WaitForExit() | |
} | |
Catch | |
{ | |
Write-Verbose "Running the Windows Disk Clean-up utility in advanced mode." -Verbose | |
$Process = Start-Process -FilePath cleanmgr -ArgumentList ('/SAGERUN:{0}' -f $StateFlags) -NoNewWindow -Wait -PassThru -ErrorAction SilentlyContinue | |
$Process.WaitForExit() | |
} | |
Finally | |
{ | |
If ($null -ne $Process) { $Process.Dispose() } | |
} | |
# Remove the Microsoft Windows Disk Clean-up utility clean-up components' StateFlag property from the registry. | |
$VolumeCacheProperties | Remove-ItemProperty -Name $PropertyName -Force -Verbose -ErrorAction SilentlyContinue | |
If ($RemovalList -match 'SoftwareDistribution' -and $null -ne $WuauServ) | |
{ | |
# Restart the Windows Update Service if it was originally running now that the 'SoftwareDistribution\Downloads' directory has been cleared. | |
Start-Service -Name wuauserv -Verbose -ErrorAction SilentlyContinue | |
} | |
# Restart the Explorer process. | |
'explorer' | Stop-Running | |
} | |
End | |
{ | |
If ($AutoRestartShell.AutoRestartShell -eq 0) | |
{ | |
# If the AutoRestartShell registry property is still set to 0, set it back to its default value. | |
Set-ItemProperty -Path $AutoRestartShell.PSPath -Name AutoRestartShell -Value 1 -Force -Verbose -ErrorAction SilentlyContinue | |
} | |
# Assign the post-cleanup storage state to a variable. | |
$PostClean = (Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object -Property DriveType -EQ 3 | Select-Object -Property @{ Name = 'Drive'; Expression = { ($PSItem.DeviceID) } }, | |
@{ Name = 'Size (GB)'; Expression = { '{0:N1}' -f ($PSItem.Size / 1GB) } }, | |
@{ Name = 'FreeSpace (GB)'; Expression = { '{0:N1}' -f ($PSItem.Freespace / 1GB) } }, | |
@{ Name = 'PercentFree'; Expression = { '{0:P1}' -f ($PSItem.FreeSpace / $PSItem.Size) } } | Format-Table -AutoSize | Out-String).Trim() | |
# Display the disk space reclaimed by the clean-up process. | |
@(("`n`n`tBefore Clean-up:`n{0}" -f $PreClean), ("`n`n`tAfter Clean-up:`n{0}`n" -f $PostClean)) | Write-Output | |
# Stop the transcript. | |
Stop-Transcript -ErrorAction Ignore | |
Write-Output '' | |
If ($OpenedDirectoryPaths) | |
{ | |
# Restore any opened directories that were open prior to the execution of `Start-WindowsCleanup`. | |
Invoke-Item -Path $OpenedDirectoryPaths -ErrorAction SilentlyContinue | |
} | |
# Clear any variables set by the `Start-WindowsCleanup` module and restore the default variables. | |
((Compare-Object -ReferenceObject (Get-Variable).Name -DifferenceObject $DefaultVariables).InputObject).ForEach{ Remove-Variable -Name $PSItem -ErrorAction Ignore } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"Component": "Active Setup Temp Folders", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "BranchCache", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Content Indexer Cleaner", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "D3D Shader Cache", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Delivery Optimization Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Device Driver Packages", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Diagnostic Data Viewer database files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Downloaded Program Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "DownloadsFolder", | |
"Clean": "False" | |
}, | |
{ | |
"Component": "GameNewsFiles", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "GameStatisticsFiles", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "GameUpdateFiles", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Internet Cache Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Language Pack", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Memory Dump Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Offline Pages Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Old ChkDsk Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Previous Installations", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Recycle Bin", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "RetailDemo Offline Content", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Service Pack Cleanup", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Setup Log Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "System error memory dump files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "System error minidump files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Temporary Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Temporary Setup Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Temporary Sync Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Thumbnail Cache", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Update Cleanup", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Upgrade Discarded Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "User file versions", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Windows Defender", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Windows Error Reporting Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Windows ESD installation files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "Windows Upgrade Log Files", | |
"Clean": "True" | |
}, | |
{ | |
"Component": "ZuneTranscodedFilesCache", | |
"Clean": "True" | |
} | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment