Automate Your Windows Cleanup: A PowerShell Script That Saves Hours
Every few weeks, you notice your computer slowing down. Disk space is mysteriously disappearing. The culprit? Temporary files, browser caches, Windows update remnants, and download folder clutter that accumulate silently in the background.
The manual solution? Clicking through Disk Cleanup, clearing browser caches one by one, and manually sorting through downloads. It works, but it's tedious and easy to forget. Today, we're building a PowerShell script that does all of this automatically—and you'll never think about disk cleanup again.
What You'll Learn
- How to identify and target common disk space wasters on Windows
- Building a comprehensive cleanup script with safety checks
- Implementing logging to track what gets cleaned
- Scheduling the script to run automatically
- Customizing cleanup targets for your specific needs
Prerequisites
- Windows 10 or 11 (Windows Server works too)
- PowerShell 5.1 or higher (pre-installed on modern Windows)
- Administrator privileges for full cleanup capabilities
- Basic familiarity with running scripts
The Manual Pain
Here's what manual Windows cleanup typically looks like:
- Open Disk Cleanup utility, wait for it to scan
- Check all the boxes, click OK, wait for cleanup
- Open Chrome, Settings, Privacy, Clear browsing data
- Repeat for Firefox, Edge, or whatever browsers you use
- Navigate to Downloads folder, sort by date, delete old files
- Check the Recycle Bin, empty it
- Hunt for large files you forgot about
This process takes 15-30 minutes and needs to happen regularly. Most people don't do it until their disk is critically low. Let's fix that.
The Automated Solution
We'll build a script that:
- Cleans Windows temporary files and caches
- Clears browser caches for major browsers
- Removes old files from Downloads (with configurable age threshold)
- Empties the Recycle Bin
- Logs everything it does
- Runs safely without deleting anything important
Step 1: Setting Up the Foundation
First, let's create the basic structure with parameters and logging:
1# Define configurable parameters2$DaysToKeepDownloads = 303$DaysToKeepTempFiles = 74$LogPath = "$env:USERPROFILE\Documents\CleanupLogs"5$LogFile = Join-Path $LogPath "Cleanup_$(Get-Date -Format 'yyyy-MM-dd_HHmmss').log"67# Ensure log directory exists8if (-not (Test-Path $LogPath)) {9 New-Item -ItemType Directory -Path $LogPath -Force | Out-Null10}
This sets up our configuration. Downloads older than 30 days get flagged, temp files older than 7 days are removed, and everything is logged for your records.
Step 2: Creating a Logging Function
Every good automation script logs what it does. Here's our logging function:
1function Write-Log {2 param (3 [string]$Message,4 [ValidateSet("INFO", "WARN", "ERROR", "SUCCESS")]5 [string]$Level = "INFO"6 )78 $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"9 $logMessage = "[$timestamp] [$Level] $Message"1011 # Write to log file12 Add-Content -Path $LogFile -Value $logMessage1314 # Also display in console with color15 switch ($Level) {16 "INFO" { Write-Host $logMessage -ForegroundColor Cyan }17 "WARN" { Write-Host $logMessage -ForegroundColor Yellow }18 "ERROR" { Write-Host $logMessage -ForegroundColor Red }19 "SUCCESS" { Write-Host $logMessage -ForegroundColor Green }20 }21}
This gives us consistent logging to both a file and the console, with color-coded output so you can quickly see what happened.
Step 3: Cleaning Windows Temp Files
Windows accumulates temporary files in several locations. Let's clean them:
1function Clear-WindowsTempFiles {2 param (3 [int]$DaysOld = 74 )56 Write-Log "Starting Windows temp file cleanup (files older than $DaysOld days)..."78 $tempPaths = @(9 $env:TEMP,10 $env:TMP,11 "C:\Windows\Temp",12 "$env:LOCALAPPDATA\Temp"13 )1415 $totalSize = 016 $filesRemoved = 017 $cutoffDate = (Get-Date).AddDays(-$DaysOld)1819 foreach ($path in $tempPaths) {20 if (Test-Path $path) {21 Write-Log "Cleaning: $path"2223 try {24 $oldFiles = Get-ChildItem -Path $path -Recurse -File -ErrorAction SilentlyContinue |25 Where-Object { $_.LastWriteTime -lt $cutoffDate }2627 foreach ($file in $oldFiles) {28 try {29 $fileSize = $file.Length30 Remove-Item -Path $file.FullName -Force -ErrorAction Stop31 $totalSize += $fileSize32 $filesRemoved++33 }34 catch {35 # File in use, skip silently36 }37 }38 }39 catch {40 Write-Log "Could not fully clean $path - some files may be in use" -Level "WARN"41 }42 }43 }4445 $sizeMB = [math]::Round($totalSize / 1MB, 2)46 Write-Log "Temp cleanup complete: Removed $filesRemoved files ($sizeMB MB)" -Level "SUCCESS"4748 return @{49 FilesRemoved = $filesRemoved50 SizeFreed = $totalSize51 }52}
Notice we're catching errors silently for files in use—this is normal and expected. Windows often has temp files locked by running processes.
Step 4: Clearing Browser Caches
Browser caches are often the biggest space hogs. Here's how to clean the major browsers:
1function Clear-BrowserCaches {2 Write-Log "Starting browser cache cleanup..."34 $totalSize = 056 # Chrome cache paths7 $chromePaths = @(8 "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Cache",9 "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Code Cache",10 "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\GPUCache"11 )1213 # Firefox cache paths14 $firefoxProfile = Get-ChildItem "$env:LOCALAPPDATA\Mozilla\Firefox\Profiles" -Directory -ErrorAction SilentlyContinue |15 Select-Object -First 116 $firefoxPaths = @()17 if ($firefoxProfile) {18 $firefoxPaths = @(19 "$($firefoxProfile.FullName)\cache2",20 "$($firefoxProfile.FullName)\startupCache"21 )22 }2324 # Edge cache paths25 $edgePaths = @(26 "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\Cache",27 "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\Code Cache",28 "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\GPUCache"29 )3031 $allPaths = $chromePaths + $firefoxPaths + $edgePaths3233 foreach ($cachePath in $allPaths) {34 if (Test-Path $cachePath) {35 try {36 $cacheSize = (Get-ChildItem $cachePath -Recurse -ErrorAction SilentlyContinue |37 Measure-Object -Property Length -Sum).Sum3839 Remove-Item -Path "$cachePath\*" -Recurse -Force -ErrorAction SilentlyContinue40 $totalSize += $cacheSize4142 $browserName = if ($cachePath -like "*Chrome*") { "Chrome" }43 elseif ($cachePath -like "*Firefox*") { "Firefox" }44 elseif ($cachePath -like "*Edge*") { "Edge" }45 else { "Unknown" }4647 Write-Log "Cleared $browserName cache: $([math]::Round($cacheSize / 1MB, 2)) MB"48 }49 catch {50 Write-Log "Could not clear cache at $cachePath (browser may be running)" -Level "WARN"51 }52 }53 }5455 $sizeMB = [math]::Round($totalSize / 1MB, 2)56 Write-Log "Browser cache cleanup complete: Freed $sizeMB MB total" -Level "SUCCESS"5758 return @{ SizeFreed = $totalSize }59}
Important: Close your browsers before running this script for maximum effectiveness. Open browsers lock their cache files.
Step 5: Cleaning the Downloads Folder
The Downloads folder is where files go to be forgotten. Let's clean old downloads while being careful not to delete anything recent:
1function Clear-OldDownloads {2 param (3 [int]$DaysOld = 30,4 [switch]$WhatIf5 )67 Write-Log "Scanning Downloads folder for files older than $DaysOld days..."89 $downloadsPath = [Environment]::GetFolderPath("UserProfile") + "\Downloads"10 $cutoffDate = (Get-Date).AddDays(-$DaysOld)1112 if (-not (Test-Path $downloadsPath)) {13 Write-Log "Downloads folder not found at $downloadsPath" -Level "WARN"14 return @{ FilesRemoved = 0; SizeFreed = 0 }15 }1617 $oldFiles = Get-ChildItem -Path $downloadsPath -File -ErrorAction SilentlyContinue |18 Where-Object { $_.LastWriteTime -lt $cutoffDate }1920 $totalSize = 021 $filesRemoved = 02223 foreach ($file in $oldFiles) {24 if ($WhatIf) {25 Write-Log "Would remove: $($file.Name) ($('{0:N2}' -f ($file.Length / 1MB)) MB)" -Level "INFO"26 }27 else {28 try {29 $fileSize = $file.Length30 Remove-Item -Path $file.FullName -Force -ErrorAction Stop31 $totalSize += $fileSize32 $filesRemoved++33 Write-Log "Removed: $($file.Name)"34 }35 catch {36 Write-Log "Could not remove: $($file.Name) - $_" -Level "WARN"37 }38 }39 }4041 $sizeMB = [math]::Round($totalSize / 1MB, 2)4243 if ($WhatIf) {44 $potentialSize = ($oldFiles | Measure-Object -Property Length -Sum).Sum45 Write-Log "WhatIf: Would remove $($oldFiles.Count) files ($([math]::Round($potentialSize / 1MB, 2)) MB)" -Level "INFO"46 }47 else {48 Write-Log "Downloads cleanup complete: Removed $filesRemoved files ($sizeMB MB)" -Level "SUCCESS"49 }5051 return @{52 FilesRemoved = $filesRemoved53 SizeFreed = $totalSize54 }55}
Notice the -WhatIf parameter—this lets you preview what would be deleted without actually deleting anything. Always run with -WhatIf first on a new system!
Step 6: Emptying the Recycle Bin
Finally, let's empty the Recycle Bin to truly reclaim that space:
1function Clear-RecycleBin {2 Write-Log "Emptying Recycle Bin..."34 try {5 # Get Recycle Bin size before clearing6 $shell = New-Object -ComObject Shell.Application7 $recycleBin = $shell.NameSpace(0xA)8 $itemCount = $recycleBin.Items().Count910 if ($itemCount -eq 0) {11 Write-Log "Recycle Bin is already empty" -Level "INFO"12 return @{ ItemsRemoved = 0 }13 }1415 # Clear the Recycle Bin16 Clear-RecycleBin -Force -ErrorAction Stop1718 Write-Log "Recycle Bin emptied: $itemCount items removed" -Level "SUCCESS"19 return @{ ItemsRemoved = $itemCount }20 }21 catch {22 Write-Log "Could not empty Recycle Bin: $_" -Level "ERROR"23 return @{ ItemsRemoved = 0 }24 }25}
The Complete Script
Here's the full, production-ready script that brings it all together:
1<#2.SYNOPSIS3 Automated Windows cleanup script that removes temp files, browser caches,4 old downloads, and empties the Recycle Bin.56.DESCRIPTION7 This script performs comprehensive Windows cleanup including:8 - Windows temporary files and caches9 - Browser caches (Chrome, Firefox, Edge)10 - Old files in the Downloads folder11 - Recycle Bin contents1213 All actions are logged for review. The script includes safety features14 and WhatIf support for previewing changes.1516.PARAMETER DaysToKeepDownloads17 Files in Downloads older than this many days will be removed. Default: 301819.PARAMETER DaysToKeepTempFiles20 Temp files older than this many days will be removed. Default: 72122.PARAMETER SkipBrowserCache23 Skip browser cache cleanup (useful if browsers are running)2425.PARAMETER SkipDownloads26 Skip Downloads folder cleanup2728.PARAMETER SkipRecycleBin29 Skip Recycle Bin cleanup3031.PARAMETER WhatIf32 Preview what would be deleted without actually deleting3334.EXAMPLE35 .\WindowsCleanup.ps136 Runs full cleanup with default settings.3738.EXAMPLE39 .\WindowsCleanup.ps1 -WhatIf40 Preview what would be cleaned without deleting anything.4142.EXAMPLE43 .\WindowsCleanup.ps1 -DaysToKeepDownloads 14 -SkipBrowserCache44 Clean downloads older than 14 days, skip browser caches.4546.NOTES47 Author: Chris Anderson48 Date: 2025-11-0349 Version: 1.050 Requires: PowerShell 5.1 or higher51#>5253[CmdletBinding(SupportsShouldProcess)]54param (55 [Parameter(Mandatory = $false)]56 [ValidateRange(1, 365)]57 [int]$DaysToKeepDownloads = 30,5859 [Parameter(Mandatory = $false)]60 [ValidateRange(1, 90)]61 [int]$DaysToKeepTempFiles = 7,6263 [Parameter(Mandatory = $false)]64 [switch]$SkipBrowserCache,6566 [Parameter(Mandatory = $false)]67 [switch]$SkipDownloads,6869 [Parameter(Mandatory = $false)]70 [switch]$SkipRecycleBin71)7273# ============================================================================74# Configuration75# ============================================================================7677$LogPath = "$env:USERPROFILE\Documents\CleanupLogs"78$LogFile = Join-Path $LogPath "Cleanup_$(Get-Date -Format 'yyyy-MM-dd_HHmmss').log"7980# Ensure log directory exists81if (-not (Test-Path $LogPath)) {82 New-Item -ItemType Directory -Path $LogPath -Force | Out-Null83}8485# ============================================================================86# Functions87# ============================================================================8889function Write-Log {90 param (91 [string]$Message,92 [ValidateSet("INFO", "WARN", "ERROR", "SUCCESS")]93 [string]$Level = "INFO"94 )9596 $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"97 $logMessage = "[$timestamp] [$Level] $Message"9899 Add-Content -Path $LogFile -Value $logMessage100101 switch ($Level) {102 "INFO" { Write-Host $logMessage -ForegroundColor Cyan }103 "WARN" { Write-Host $logMessage -ForegroundColor Yellow }104 "ERROR" { Write-Host $logMessage -ForegroundColor Red }105 "SUCCESS" { Write-Host $logMessage -ForegroundColor Green }106 }107}108109function Clear-WindowsTempFiles {110 param ([int]$DaysOld = 7)111112 Write-Log "Starting Windows temp file cleanup (files older than $DaysOld days)..."113114 $tempPaths = @(115 $env:TEMP,116 $env:TMP,117 "C:\Windows\Temp",118 "$env:LOCALAPPDATA\Temp"119 )120121 $totalSize = 0122 $filesRemoved = 0123 $cutoffDate = (Get-Date).AddDays(-$DaysOld)124125 foreach ($path in $tempPaths) {126 if (Test-Path $path) {127 Write-Log "Cleaning: $path"128129 try {130 $oldFiles = Get-ChildItem -Path $path -Recurse -File -ErrorAction SilentlyContinue |131 Where-Object { $_.LastWriteTime -lt $cutoffDate }132133 foreach ($file in $oldFiles) {134 try {135 $fileSize = $file.Length136 Remove-Item -Path $file.FullName -Force -ErrorAction Stop137 $totalSize += $fileSize138 $filesRemoved++139 }140 catch {141 # File in use, skip silently142 }143 }144 }145 catch {146 Write-Log "Could not fully clean $path" -Level "WARN"147 }148 }149 }150151 $sizeMB = [math]::Round($totalSize / 1MB, 2)152 Write-Log "Temp cleanup complete: Removed $filesRemoved files ($sizeMB MB)" -Level "SUCCESS"153 return @{ FilesRemoved = $filesRemoved; SizeFreed = $totalSize }154}155156function Clear-BrowserCaches {157 Write-Log "Starting browser cache cleanup..."158159 $totalSize = 0160161 $chromePaths = @(162 "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Cache",163 "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Code Cache"164 )165166 $firefoxProfile = Get-ChildItem "$env:LOCALAPPDATA\Mozilla\Firefox\Profiles" -Directory -ErrorAction SilentlyContinue |167 Select-Object -First 1168 $firefoxPaths = @()169 if ($firefoxProfile) {170 $firefoxPaths = @("$($firefoxProfile.FullName)\cache2")171 }172173 $edgePaths = @(174 "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\Cache",175 "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\Code Cache"176 )177178 $allPaths = $chromePaths + $firefoxPaths + $edgePaths179180 foreach ($cachePath in $allPaths) {181 if (Test-Path $cachePath) {182 try {183 $cacheSize = (Get-ChildItem $cachePath -Recurse -ErrorAction SilentlyContinue |184 Measure-Object -Property Length -Sum).Sum185186 Remove-Item -Path "$cachePath\*" -Recurse -Force -ErrorAction SilentlyContinue187 $totalSize += $cacheSize188189 $browserName = if ($cachePath -like "*Chrome*") { "Chrome" }190 elseif ($cachePath -like "*Firefox*") { "Firefox" }191 else { "Edge" }192193 Write-Log "Cleared $browserName cache: $([math]::Round($cacheSize / 1MB, 2)) MB"194 }195 catch {196 Write-Log "Could not clear cache at $cachePath" -Level "WARN"197 }198 }199 }200201 Write-Log "Browser cleanup complete: Freed $([math]::Round($totalSize / 1MB, 2)) MB" -Level "SUCCESS"202 return @{ SizeFreed = $totalSize }203}204205function Clear-OldDownloads {206 param ([int]$DaysOld = 30)207208 Write-Log "Scanning Downloads for files older than $DaysOld days..."209210 $downloadsPath = [Environment]::GetFolderPath("UserProfile") + "\Downloads"211 $cutoffDate = (Get-Date).AddDays(-$DaysOld)212213 $oldFiles = Get-ChildItem -Path $downloadsPath -File -ErrorAction SilentlyContinue |214 Where-Object { $_.LastWriteTime -lt $cutoffDate }215216 $totalSize = 0217 $filesRemoved = 0218219 foreach ($file in $oldFiles) {220 try {221 $fileSize = $file.Length222 Remove-Item -Path $file.FullName -Force -ErrorAction Stop223 $totalSize += $fileSize224 $filesRemoved++225 }226 catch {227 Write-Log "Could not remove: $($file.Name)" -Level "WARN"228 }229 }230231 Write-Log "Downloads cleanup: Removed $filesRemoved files ($([math]::Round($totalSize / 1MB, 2)) MB)" -Level "SUCCESS"232 return @{ FilesRemoved = $filesRemoved; SizeFreed = $totalSize }233}234235function Clear-RecycleBinItems {236 Write-Log "Emptying Recycle Bin..."237238 try {239 Clear-RecycleBin -Force -ErrorAction Stop240 Write-Log "Recycle Bin emptied successfully" -Level "SUCCESS"241 }242 catch {243 Write-Log "Could not empty Recycle Bin: $_" -Level "WARN"244 }245}246247# ============================================================================248# Main Execution249# ============================================================================250251Write-Log "========================================" -Level "INFO"252Write-Log "Windows Cleanup Script Started" -Level "INFO"253Write-Log "========================================" -Level "INFO"254255$totalFreed = 0256257# Clean Windows temp files258$tempResult = Clear-WindowsTempFiles -DaysOld $DaysToKeepTempFiles259$totalFreed += $tempResult.SizeFreed260261# Clean browser caches262if (-not $SkipBrowserCache) {263 $browserResult = Clear-BrowserCaches264 $totalFreed += $browserResult.SizeFreed265}266else {267 Write-Log "Skipping browser cache cleanup (SkipBrowserCache specified)" -Level "INFO"268}269270# Clean old downloads271if (-not $SkipDownloads) {272 $downloadsResult = Clear-OldDownloads -DaysOld $DaysToKeepDownloads273 $totalFreed += $downloadsResult.SizeFreed274}275else {276 Write-Log "Skipping Downloads cleanup (SkipDownloads specified)" -Level "INFO"277}278279# Empty Recycle Bin280if (-not $SkipRecycleBin) {281 Clear-RecycleBinItems282}283else {284 Write-Log "Skipping Recycle Bin (SkipRecycleBin specified)" -Level "INFO"285}286287# Summary288Write-Log "========================================" -Level "INFO"289Write-Log "Cleanup Complete!" -Level "SUCCESS"290Write-Log "Total space freed: $([math]::Round($totalFreed / 1MB, 2)) MB" -Level "SUCCESS"291Write-Log "Log saved to: $LogFile" -Level "INFO"292Write-Log "========================================" -Level "INFO"
How to Run This Script
Method 1: Interactive Execution
1# Open PowerShell as Administrator and navigate to script location2cd C:\Scripts34# Run with default settings5.\WindowsCleanup.ps167# Preview what would be deleted8.\WindowsCleanup.ps1 -WhatIf910# Custom settings11.\WindowsCleanup.ps1 -DaysToKeepDownloads 14 -SkipBrowserCache
Method 2: Scheduled Task
- Open Task Scheduler (search "Task Scheduler" in Start)
- Click "Create Task"
- General tab: Name it "Weekly Windows Cleanup"
- Triggers tab: Add new trigger, Weekly, pick a day
- Actions tab: New action
- Program:
powershell.exe - Arguments:
-ExecutionPolicy Bypass -File "C:\Scripts\WindowsCleanup.ps1"
- Program:
- Check "Run with highest privileges"
Customization Options
| Variable | Default | Description |
|---|---|---|
| $DaysToKeepDownloads | 30 | Days before downloads are considered "old" |
| $DaysToKeepTempFiles | 7 | Days before temp files are removed |
| $SkipBrowserCache | $false | Skip browser cache cleanup |
| $SkipDownloads | $false | Skip Downloads folder cleanup |
| $SkipRecycleBin | $false | Skip Recycle Bin emptying |
Security Considerations
⚠️ Important security notes:
- This script only deletes files in known safe locations (temp folders, caches)
- Downloads cleanup uses age-based filtering—recent files are protected
- Always run with
-WhatIffirst on a new system to preview actions - Log files are created for audit trails
- The script doesn't require network access or external downloads
- Never run scripts from untrusted sources without reviewing the code
Common Issues & Solutions
| Issue | Cause | Solution |
|---|---|---|
| "Access Denied" errors | Files locked by processes | Close applications, run as Administrator |
| Browser cache not clearing | Browser is running | Close all browser windows first |
| Script won't run | Execution policy | Run: Set-ExecutionPolicy RemoteSigned -Scope CurrentUser |
| Downloads not being removed | Files too recent | Adjust -DaysToKeepDownloads parameter |
| No space freed | Already clean or nothing matched | Check log file for details |
Taking It Further
Once you've mastered this script, consider these enhancements:
- Email notifications: Send yourself a summary after each cleanup
- Additional cleanup targets: Windows Update cache, installer caches
- Disk space monitoring: Alert when free space drops below threshold
- Network deployment: Push to multiple machines via Group Policy
- Before/after reporting: Track space trends over time
Conclusion
You've just built a comprehensive Windows cleanup automation that handles the tedious task of disk maintenance. Set it to run weekly, and you'll never again be surprised by a "low disk space" warning.
The script is designed to be safe—it only targets known expendable files, logs everything it does, and supports preview mode. Feel free to customize the thresholds and targets for your specific needs.
Remember: the best automation is the kind you set up once and forget about. This cleanup script will quietly keep your system running smoothly in the background while you focus on work that actually matters.
Happy automating, and may your disk always have space to spare!
Sponsored Content
Interested in advertising? Reach automation professionals through our platform.
