SolutionsCraft
← Automation

Automation · 2 min read

Automate Windows Server Health Checks with PowerShell and Email Alerts

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 the CredentialManager module) 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.

Get new tutorials by email

No spam, unsubscribe anytime.