PowerShell Script to Delete Files Older Than 30 Days

To delete files older than 30 days with PowerShell, list the files with Get-ChildItem, keep the ones whose LastWriteTime is before a cutoff date, and pipe them to Remove-Item. Run it with -WhatIf first:

$cutoff = ([datetime]'2026-09-28').AddDays(-30)

Get-ChildItem -Path C:\psfaqs\Cleanup -File -Recurse |
    Where-Object LastWriteTime -LT $cutoff |
    Remove-Item -WhatIf

Output:

What if: Performing the operation "Remove File" on target "C:\psfaqs\Cleanup\app-2026-07-01.log".
What if: Performing the operation "Remove File" on target "C:\psfaqs\Cleanup\app-2026-08-15.log".
What if: Performing the operation "Remove File" on target "C:\psfaqs\Cleanup\export-2026-07-20.csv".
What if: Performing the operation "Remove File" on target "C:\psfaqs\Cleanup\Archive\2026-06\old.zip".
PowerShell delete files older than 30 days preview with WhatIf
Four files older than August 29, 2026 would be deleted (PowerShell 7)

In a real script, set the cutoff with (Get-Date).AddDays(-30). I used a fixed date and a sample folder so the output stays the same, and I ran every example in PowerShell 7.6 and Windows PowerShell 5.1.

Delete the files and report what was freed

Once the preview looks right, remove -WhatIf. Measuring the files first lets you log how much space you got back:

$cutoff = ([datetime]'2026-09-28').AddDays(-30)
$old = Get-ChildItem -Path C:\psfaqs\Cleanup -File -Recurse -Force | Where-Object LastWriteTime -LT $cutoff

$freed = ($old | Measure-Object -Property Length -Sum).Sum
$old | Remove-Item -Force -ErrorAction Continue -ErrorVariable failed

"Deleted $($old.Count - $failed.Count) files, freed {0:N1} MB, errors: $($failed.Count)" -f ($freed / 1MB)
"Left behind: $((Get-ChildItem -Path C:\psfaqs\Cleanup -File -Recurse -Force).Name -join ', ')"

Output:

Deleted 5 files, freed 4.0 MB, errors: 0
Left behind: app-2026-09-25.log, today.log

-ErrorVariable collects files that couldn’t be deleted, like ones another program has open, while the rest still go. Writing to a log file shows how to keep a record.

Hidden and read-only files need -Force

Two things quietly leave old files behind. Get-ChildItem skips hidden files, and Remove-Item refuses read-only ones:

$cutoff = ([datetime]'2026-09-28').AddDays(-30)

"Without -Force, Get-ChildItem sees: $((Get-ChildItem -Path C:\psfaqs\Cleanup -File | Where-Object LastWriteTime -LT $cutoff).Name -join ', ')"
try { Remove-Item -Path C:\psfaqs\Cleanup\export-2026-07-20.csv -ErrorAction Stop }
catch { 'Remove-Item without -Force failed on the read-only export-2026-07-20.csv' }

Output:

Without -Force, Get-ChildItem sees: app-2026-07-01.log, app-2026-08-15.log, export-2026-07-20.csv
Remove-Item without -Force failed on the read-only export-2026-07-20.csv
PowerShell delete old files skips hidden files and fails on read-only files
The hidden .cache.tmp isn’t listed, and the read-only CSV can’t be deleted (Windows PowerShell 5.1)

Add -Force to both commands, as in the delete example above. Remove-Item covers -Force and -Recurse in detail.

Delete only certain file types

Use -Filter to limit the cleanup to one extension, like old log files:

$cutoff = ([datetime]'2026-09-28').AddDays(-30)

Get-ChildItem -Path C:\psfaqs\Cleanup -Filter *.log -File -Recurse |
    Where-Object LastWriteTime -LT $cutoff |
    ForEach-Object { "Would delete $($_.Name)" }

Output:

Would delete app-2026-07-01.log
Would delete app-2026-08-15.log

-Filter is faster than -Include and works without extra wildcards in the path.

Remove empty folders afterward

Deleting files can leave empty folders. Remove them deepest first, so a parent becomes empty before you check it:

$cutoff = ([datetime]'2026-09-28').AddDays(-30)
Get-ChildItem -Path C:\psfaqs\Cleanup -File -Recurse -Force | Where-Object LastWriteTime -LT $cutoff | Remove-Item -Force

Get-ChildItem -Path C:\psfaqs\Cleanup -Directory -Recurse |
    Sort-Object -Property { $_.FullName.Length } -Descending |
    Where-Object { -not (Get-ChildItem -Path $_.FullName -Force) } |
    ForEach-Object { Remove-Item -Path $_.FullName; "Removed empty folder $($_.FullName.Replace('C:\psfaqs\Cleanup\', ''))" }

Output:

Removed empty folder Archive\2026-06
Removed empty folder Archive
PowerShell remove empty folders after deleting old files
Archive\2026-06 was removed before Archive (PowerShell 7)

Don’t delete folders by their own date. A folder’s LastWriteTime only changes when files are added or removed, so an old date doesn’t mean old contents.

A reusable cleanup function

Wrap the steps in a function with parameters, and SupportsShouldProcess gives it -WhatIf for free:

function Remove-OldFile {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)] [string]$Path,
        [int]$Days = 30,
        [string]$Filter = '*'
    )
    $cutoff = (Get-Date).Date.AddDays(-$Days)
    Get-ChildItem -Path $Path -Filter $Filter -File -Recurse -Force |
        Where-Object LastWriteTime -LT $cutoff |
        Remove-Item -Force
}

Remove-OldFile -Path C:\psfaqs\Cleanup -Days 60 -Filter *.log -WhatIf

Output (run on September 28, 2026):

What if: Performing the operation "Remove File" on target "C:\psfaqs\Cleanup\app-2026-07-01.log".

Finding files by date covers the date filters in more depth.

Run the cleanup on a schedule

Save the function and a call to it as C:\Scripts\cleanup.ps1, then register a daily task from an elevated PowerShell window:

$action  = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\cleanup.ps1'
$trigger = New-ScheduledTaskTrigger -Daily -At '2:00 AM'
Register-ScheduledTask -TaskName 'Delete old logs' -Action $action -Trigger $trigger -User 'NT AUTHORITY\SYSTEM' -RunLevel Highest

Test the script by hand with -WhatIf before you schedule it. Microsoft’s Register-ScheduledTask documentation covers other triggers, and the Remove-Item reference lists every parameter.

Frequently Asked Questions