PowerShell Service Monitoring: Alert on Stopped Services

Your website went down at 11 PM because IIS stopped and no one noticed until users started calling in the morning. A proper PowerShell monitor services script checks critical services on a schedule, restarts stopped ones automatically, and sends an email alert so someone is always in the loop. This post builds a complete service monitoring solution with multi-server support, email notifications, event log integration, and Task Scheduler setup.
Check Critical Service Status
Start with a list of services you care about and check their status. Get-Service returns the current running state for each:
$criticalServices = @('W3SVC', 'MSSQLSERVER', 'wuauserv', 'Spooler')
$status = foreach ($svc in $criticalServices) {
$s = Get-Service -Name $svc -ErrorAction SilentlyContinue
if ($s) {
[PSCustomObject]@{
Name = $s.Name
DisplayName = $s.DisplayName
Status = $s.Status
StartType = $s.StartType
}
} else {
[PSCustomObject]@{
Name = $svc
DisplayName = "NOT FOUND"
Status = "Unknown"
StartType = "Unknown"
}
}
}
$status | Format-Table -AutoSize
Name DisplayName Status StartType
---- ----------- ------ ---------
W3SVC World Wide Web Publishing Running Automatic
MSSQLSERVER SQL Server (MSSQLSERVER) Stopped Automatic
wuauserv Windows Update Running Manual
Spooler Print Spooler Running Automatic
Restart Stopped Services Automatically
When a monitored service is stopped and its start type is Automatic, attempt a restart. Wait briefly to confirm the restart succeeded before reporting:
$stoppedServices = $status | Where-Object { $_.Status -eq 'Stopped' -and $_.StartType -eq 'Automatic' }
foreach ($svc in $stoppedServices) {
try {
Start-Service -Name $svc.Name -ErrorAction Stop
Start-Sleep -Seconds 5
$refreshed = Get-Service -Name $svc.Name
if ($refreshed.Status -eq 'Running') {
Write-Host "Restarted $($svc.DisplayName) successfully" -ForegroundColor Green
} else {
Write-Warning "$($svc.DisplayName) restarted but still not Running — status: $($refreshed.Status)"
}
}
catch {
Write-Warning "Failed to restart $($svc.DisplayName): $($_.Exception.Message)"
}
}
Send Alert Email on Stop
Build an email body with the list of stopped services and send it. Use HTML formatting so the message is readable in any email client:
function Send-ServiceAlert {
param(
[string[]]$StoppedServiceNames,
[string]$ComputerName = $env:COMPUTERNAME
)
$body = @"
<html><body>
<h3>Service Alert: $ComputerName — $(Get-Date)</h3>
<p>The following critical services were found stopped:</p>
<ul>$(($StoppedServiceNames | ForEach-Object { "<li>$_</li>" }) -join '')</ul>
<p>Automatic restart was attempted where applicable.</p>
</body></html>
"@
$mailParams = @{
From = '[email protected]'
To = '[email protected]'
Subject = "SERVICE ALERT: $ComputerName at $(Get-Date -Format 'HH:mm')"
Body = $body
BodyAsHtml = $true
SmtpServer = 'smtp.corp.com'
}
Send-MailMessage @mailParams
}
$stopped = $status | Where-Object Status -eq Stopped
if ($stopped) {
Send-ServiceAlert -StoppedServiceNames $stopped.DisplayName
}
Monitor Multiple Servers
Extend the script to check services across a list of servers using Invoke-Command:
$servers = Get-Content "C:\Scripts\servers.txt"
$checkServices = @('W3SVC', 'MSSQLSERVER', 'Spooler')
$allResults = Invoke-Command -ComputerName $servers -ThrottleLimit 15 -ScriptBlock {
param($services)
foreach ($svc in $services) {
$s = Get-Service -Name $svc -ErrorAction SilentlyContinue
[PSCustomObject]@{
Server = $env:COMPUTERNAME
Service = $svc
Status = if ($s) { $s.Status } else { 'Not Found' }
}
}
} -ArgumentList (, $checkServices)
$allResults | Where-Object Status -ne 'Running' | Format-Table -AutoSize
Log Service Events
Write service status changes to the Windows Application event log using Write-EventLog, giving your monitoring team a persistent audit trail:
$logName = 'Application'
$source = 'PSServiceMonitor'
if (-not [System.Diagnostics.EventLog]::SourceExists($source)) {
New-EventLog -LogName $logName -Source $source
}
foreach ($svc in $stoppedServices) {
Write-EventLog -LogName $logName -Source $source -EventId 1001 `
-EntryType Warning -Message "Service '$($svc.DisplayName)' was found stopped on $env:COMPUTERNAME at $(Get-Date)"
}
Schedule with Task Scheduler
Register the monitoring script to run every five minutes using the Task Scheduler cmdlets:
$action = New-ScheduledTaskAction -Execute 'pwsh.exe' `
-Argument '-NonInteractive -File "C:\Scripts\Monitor-Services.ps1"'
$trigger = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Minutes 5) -Once -At (Get-Date)
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 4)
Register-ScheduledTask -TaskName 'ServiceMonitor' -Action $action `
-Trigger $trigger -Settings $settings -RunLevel Highest -Force
Common Errors and Fixes
- Auto-restart can loop if service crashes immediately. If a service keeps crashing, your script will attempt restart on every check cycle, generating repeated alerts. Add a counter or time check: only attempt restart once per hour for the same service, then alert and stop trying.
- Email credentials stored insecurely in script. Never hard-code SMTP credentials in a script. Use an anonymous internal relay, Windows credential manager, or an encrypted credential file loaded at runtime instead.
Related Cmdlets / See Also
Wrapping Up
A solid service monitoring script combines status checks, automatic restart attempts, email alerting, and event log entries into a scheduled task that runs continuously in the background. Build it once, schedule it every five minutes, and you will know about stopped services before your users do.


