Checking server health by logging in and eyeballing Task Manager doesn't scale past one or two machines. This script checks disk space, memory, and a list of critical services, and only sends an email when something's actually wrong — not a daily "everything's fine" notification you'll learn to ignore within a week.
Before you start
You'll need PowerShell 5.1+ (built into Windows Server) and SMTP credentials for sending mail — an app password if you're using Microsoft 365 or Gmail, or your internal relay if you have one.
Step 1: Write the check
Each check appends to an $issues array only when something crosses a threshold. Nothing gets added when things are fine, which is what keeps the email quiet on a normal day.
# health-check.ps1
$diskThreshold = 15 # percent free
$memThreshold = 10 # percent free
$services = @("W3SVC", "MSSQLSERVER", "Spooler")
$issues = @()
Get-PSDrive -PSProvider FileSystem | ForEach-Object {
$freePct = ($_.Free / ($_.Free + $_.Used)) * 100
if ($freePct -lt $diskThreshold) {
$issues += "Drive $($_.Name): $([math]::Round($freePct,1))% free"
}
}
$os = Get-CimInstance Win32_OperatingSystem
$memPct = ($os.FreePhysicalMemory / $os.TotalVisibleMemorySize) * 100
if ($memPct -lt $memThreshold) {
$issues += "Memory: $([math]::Round($memPct,1))% free"
}
foreach ($svc in $services) {
$status = Get-Service -Name $svc -ErrorAction SilentlyContinue
if (-not $status -or $status.Status -ne "Running") {
$issues += "Service $svc is not running"
}
}
Step 2: Only email when there's something to say
if ($issues.Count -gt 0) {
Send-MailMessage -To "you@solutionscraft.com" -From "server-alerts@yourdomain.com" `
-Subject "Server health check: $($issues.Count) issue(s)" `
-Body ($issues -join "`n") `
-SmtpServer "smtp.yourprovider.com" -Port 587 -UseSsl `
-Credential (Get-StoredCredential -Target "smtp-alerts")
}
Tip: Don't hardcode the SMTP password in the script.
Get-StoredCredential(from theCredentialManagermodule) pulls it from Windows Credential Manager instead, so the script stays safe to commit or share.
Step 3: Run it on a schedule
Register it as a scheduled task so it runs without you:
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-File C:\Scripts\health-check.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 8am
Register-ScheduledTask -TaskName "ServerHealthCheck" -Action $action -Trigger $trigger
What's next
If you're monitoring more than a couple of servers, this is also a good candidate for an n8n Execute Command node instead of Task Scheduler — you get a run history and retry logic for free, the same tradeoff covered in the n8n workflow tutorial.
Top comments (0)