
Monitor Free Disk Space and Email an Alert with PowerShell
Before you run this
This script reads the free space on the fixed local disks of the machine it runs on and emails you when any of them drops below a percentage you set. It is a lightweight stand-in for a monitoring platform — good for a handful of servers, not a replacement for one across a fleet.
Privileges: reading disk space with Get-CimInstance Win32_LogicalDisk works fine as an ordinary user, so the script itself needs no elevation. Registering it as a scheduled task, and running it as SYSTEM or a service account, does need an elevated (Administrator) PowerShell session.
It changes nothing on disk. It only reads counters and sends mail. There is nothing destructive here and nothing to reverse except the scheduled task you create at the end, which I show how to remove.
Test it first. Read the script before you run it. Run it once by hand on a test or non-production server with the -Test switch (below), which forces a report email for every disk regardless of threshold, so you can confirm the mail path works before you rely on it. Don't schedule it until you've seen one message land in the inbox.
One honest caveat about mail: this guide uses Send-MailMessage, which is the well-established built-in way and still works in PowerShell 5.1 and 7. Microsoft marks it obsolete because it can't guarantee a secure connection to modern mail services, and does not plan to improve it. For internal relays it is fine. If you must send through a hardened or cloud provider, the current path is the Microsoft.Graph module or a library such as MailKit — I'm naming those rather than walking through them.
Assumptions
- Windows Server 2019 or 2022, Windows PowerShell 5.1 (the version shipped in the box).
- An internal SMTP relay (
smtp.example.com) that accepts mail from this server's IP without authentication — the common setup for internal alerting. I note where to add credentials if yours requires them. - You want a daily check. Adjust the trigger for hourly if you prefer.
The script
Save this as C:\Scripts\disk-space-alert.ps1. Edit the four mail settings at the top — they are obvious placeholders; replace example.com, the from/to addresses, and the relay name with your own.
#Requires -Version 5.1
# disk-space-alert.ps1
# Emails an alert for any fixed local disk below a free-space threshold.
param(
[int]$ThresholdPercent = 15, # alert when free space falls below this %
[switch]$Test # report ALL disks and always send (for testing)
)
# --- EDIT THESE ---
$smtpServer = "smtp.example.com" # your internal relay
$smtpPort = 25
$mailFrom = "diskalert@example.com"
$mailTo = "sysadmin@example.com"
# ------------------
# DriveType 3 = fixed local disk (excludes network, removable, and optical drives)
$disks = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3"
$report = foreach ($d in $disks) {
if (-not $d.Size -or $d.Size -eq 0) { continue } # skip disks with no reported size
$freePct = [math]::Round(($d.FreeSpace / $d.Size) * 100, 1)
[pscustomobject]@{
Drive = $d.DeviceID
FreeGB = [math]::Round($d.FreeSpace / 1GB, 1)
SizeGB = [math]::Round($d.Size / 1GB, 1)
FreePct = $freePct
}
}
# Which disks trip the alert
if ($Test) {
$low = $report
} else {
$low = $report | Where-Object { $_.FreePct -lt $ThresholdPercent }
}
if (-not $low) {
Write-Host "All fixed disks above $ThresholdPercent% free. No mail sent."
return
}
$hostName = $env:COMPUTERNAME
$body = ($low | Format-Table -AutoSize | Out-String)
$subject = "Low disk space on $hostName"
Send-MailMessage -SmtpServer $smtpServer -Port $smtpPort `
-From $mailFrom -To $mailTo `
-Subject $subject -Body $body
Two lines worth explaining: the DriveType=3 filter keeps this to real fixed disks, so a mounted network share or an empty DVD drive never triggers a false alert; and the $d.Size -eq 0 guard avoids a divide-by-zero on any disk that reports no size.
If your relay needs authentication or TLS
Send-MailMessage accepts -UseSsl and -Credential. Don't hard-code a password in the script. Store the credential once, as the account that will run the task, with DPAPI-protected XML:
# Run once, as the account the scheduled task will use
Get-Credential | Export-Clixml -Path C:\Scripts\smtp-cred.xml
Then near the top of the script, load it and pass it in:
$cred = Import-Clixml -Path C:\Scripts\smtp-cred.xml
# ...and add to the Send-MailMessage call:
# -UseSsl -Credential $cred
Note the DPAPI catch: Export-Clixml encrypts for the user and machine that created it. A file exported by your admin account cannot be read by SYSTEM. If you go the credential route, run the export as the exact service account the task uses (see scheduling below), not as yourself.
Test it by hand
Before scheduling anything, force a message:
C:\Scripts\disk-space-alert.ps1 -Test
With -Test it reports every disk and always sends, so you get an email even on a healthy server. Confirm it arrives and reads sensibly. Then try a realistic threshold — pick a number a real disk currently sits below to prove the filter fires, e.g. -ThresholdPercent 99.
Schedule it
From an elevated PowerShell session, register a daily task. Running as SYSTEM is the simplest choice when the relay needs no auth:
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\disk-space-alert.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 7:00am
Register-ScheduledTask -TaskName "Disk Space Alert" `
-Action $action -Trigger $trigger `
-User "SYSTEM" -RunLevel Highest `
-Description "Emails an alert when a fixed disk drops below the free-space threshold."
If you stored SMTP credentials, run the task as that specific service account instead of SYSTEM — add -User "DOMAIN\svc-diskalert" and you'll be prompted, or supply -Password. Check Microsoft Learn for Register-ScheduledTask for the exact credential parameters, as they differ for gMSA and standard accounts.
-Argument above uses -ExecutionPolicy Bypass for this one invocation only; it does not change the machine's policy.
Verify it worked
Confirm the task exists and check when it last ran and with what result:
Get-ScheduledTask -TaskName "Disk Space Alert"
Get-ScheduledTaskInfo -TaskName "Disk Space Alert"
Get-ScheduledTaskInfo shows LastRunTime and LastTaskResult — a result of 0 means the task exited cleanly. To force a real run and watch it end to end:
Start-ScheduledTask -TaskName "Disk Space Alert"
Then check the mailbox. If nothing arrives, run the script by hand with -Test again in the foreground and read any error from Send-MailMessage — that separates a mail problem from a scheduling problem.
Undo
To remove the schedule cleanly:
Unregister-ScheduledTask -TaskName "Disk Space Alert" -Confirm:$false
Then delete the files you created — C:\Scripts\disk-space-alert.ps1 and, if you made one, C:\Scripts\smtp-cred.xml. Since the script only reads counters and sends mail, there is nothing else to roll back; removing the task and the files returns the server to exactly where it started.
Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.
More about the author →Was this article helpful?
Tap a star — no sign-in needed.
Be the first to rate this article.
