PowerShell Service Monitoring: Alert on Stopped Services

PowerShell Service Monitoring: Alert on Stopped Services

PowerShell Tips Editor 4 min read
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.

Send-Item -To