DEV Community

OpsKit
OpsKit

Posted on

Disk cleanup with PowerShell: an age-gated script that shows its work before deleting anything

Temp folder cleanup is the kind of task everybody does by hand once, gets uncomfortable with, and then automates badly. The uncomfortable part is the three-week-old .part download sitting in someone's temp folder, or the log file that's still being written to — scripts that just do Remove-Item $env:TEMP\* don't care. They'll eat it.

Here's the cleanup script I actually run, and the three rules that make it safe enough to schedule.

Rule 1: never delete by age alone, always by last-touched age

The failure mode of naive cleanup isn't deleting too much — it's deleting something live. A file's creation date tells you nothing; a file being written to right now still has a creation date from three weeks ago if the folder was restored from backup.

What matters is last write time: if a file hasn't been touched in 3+ days, nothing has it open, nothing is appending to it, and nobody is actively using it. The script's -MinFileAgeDays parameter (default 3) filters everything through that check before a single file is considered:

$MinFileAge = (Get-Date).AddDays(-$MinFileAgeDays)
$targets | Where-Object { $_.LastWriteTime -lt $MinFileAge }
Enter fullscreen mode Exit fullscreen mode

Raise it to 7 or 14 on busy servers. On a terminal server with users in session all day, that one number is the difference between a maintenance window and an incident.

Rule 2: -WhatIf has to mean something

PowerShell's SupportsShouldProcess attribute gives you -WhatIf and -Confirm for free, but only if you route every destructive call through ShouldProcess. The script does:

if ($PSCmdlet.ShouldProcess($file.FullName, "Remove (age $([math]::Round($age)) days)")) {
    Remove-Item -LiteralPath $file.FullName -Force -ErrorAction SilentlyContinue
    $freed += $file.Length
}
Enter fullscreen mode Exit fullscreen mode

Run it once with -WhatIf and you get a complete preview — full paths, ages, counts — with zero deletions. It's a dry run you can paste into a change ticket as evidence of what would happen.

Rule 3: the script reports, it doesn't just delete

A cleanup that leaves no trace is a cleanup nobody can audit. Each run appends a line to %ProgramData%\OpsKit\disk-cleanup.csv:

Timestamp, Host, FreedMB
2026-10-08 03:14:02, WEB01, 812.4
Enter fullscreen mode Exit fullscreen mode

Five weeks later, when someone asks why disk space jumped on Tuesday, the answer is one Import-Csv away. It also gives the script a natural heartbeat — an empty CSV section means the scheduled task stopped running, which you notice before the disk fills up.

What it actually cleans

  • User temp folders ($env:LOCALAPPDATA\Temp) for every profile on the box — not just the currently logged-in one
  • System temp ($env:windir\Temp)
  • Windows Update download cache ($env:windir\SoftwareDistribution\Download) — usually the biggest single win after a patch cycle, and safe to clear when the update service isn't mid-install
  • Old logs in paths you configure, age-gated the same way
.\disk-cleanup.ps1 -WhatIf                  # preview, delete nothing
.\disk-cleanup.ps1 -MinFileAgeDays 7        # only files idle a week
.\disk-cleanup.ps1 -MinFileAgeDays 3 -KeepDays 30  # logs: age-gate 3, retention 30
Enter fullscreen mode Exit fullscreen mode

Try it before you trust it

disk-cleanup.ps1 is one of two scripts I keep MIT-licensed and free in the powershell-admin-scripts repo — source visible, no account, no telemetry. The companion there is ssl-expiry-check.ps1, which catches expiring certificates before your users do.

The full kit — backup rotation, service watchdog, event-log alerting, inventory export, share and password audits, patch reporting, and the user-offboarding checklist — is $7 on Gumroad with a 30-day refund. If offboarding is the only thing you need, the standalone script is $2.

Related: How to get an email when a Windows scheduled task fails - the watchdog I run over exactly these kinds of jobs (TaskWatch, free mini sample).

Top comments (0)