Restart Windows services on remote servers with email alerts
Restart one or more Windows services on remote servers, ping-checked, with success/failure email alerts. Pipeline-driven, -WhatIf safe, and fixes the en-dash/curly-quote bugs in the original.
by Emanuel De Almeida
A small but genuinely useful piece of plumbing: restart a named service on one or more remote servers, check the box is actually reachable first, and get an email when it is done (or when it breaks). Handy for scheduled "bounce this flaky service at 3am" jobs or quick remote remediation.
What it does
For each target it pings the server, restarts the named service(s), and emails a per-action report. It takes pipeline input, so you can feed it a list of server/service pairs and let it work the list. Email is optional, omit the mail parameters and it just restarts and logs.
What I rebuilt
The source copy had two silent bugs from a bad paste that stop it dead in PowerShell:
if ($intPingError –eq 0)used an en-dash (–) instead of the operator hyphen, which is a parse error.- Several
Write-Hostlines used curly quotes ("...") instead of straight quotes, another parse error.
Both are fixed here. On top of that:
- Renamed to approved verbs (
Restart-RemoteService,Send-Notification) instead ofFuncRestartService/FuncMail. - Added `[CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]`, so
-WhatIfpreviews every restart and scheduled runs pass-Confirm:$false. - Pipeline input and multiple services per server, replacing the hard-coded call list.
- Real error handling (
$($_.Exception.Message)instead of dumping the whole$Errorhistory),returninstead ofbreakto exit cleanly,Test-Connectioninstead of hand-rolledSystem.Net.NetworkInformation.Ping, and the mail objects are disposed.
Notes
Needs rights to control services on the remote host (Get-Service / Restart-Service use RPC/DCOM on PowerShell 5.1) and an SMTP server for the optional alerts. Run with -WhatIf first. Adapted from a script on SQLServerCentral; test before production.
The script
#Requires -Version 5.1
<#
.SYNOPSIS
Restarts one or more Windows services on one or more remote servers, pinging
first and emailing the outcome (or any failure). Honours -WhatIf / -Confirm.
.DESCRIPTION
For each target the script tests connectivity, restarts the named service(s),
and sends an email report on success or failure. It accepts pipeline input,
so you can feed it a list of server/service pairs and let it work through
them. Email is optional: omit the mail parameters to just restart and log.
.PARAMETER ComputerName
The remote server. Pipeline-bindable (alias: Server).
.PARAMETER ServiceName
One or more service names to restart on that server (alias: Service).
.PARAMETER To / From / SmtpServer
Supply all three to receive an email report per action.
.EXAMPLE
Restart-RemoteService -ComputerName SQL01 -ServiceName MSSQLSERVER -To it@example.com -From noreply@example.com -SmtpServer smtp.example.local
.EXAMPLE
# Dry run a batch from the pipeline
@(
[pscustomobject]@{ ComputerName = 'SQL01'; ServiceName = 'MSSQLSERVER' }
[pscustomobject]@{ ComputerName = 'WEB01'; ServiceName = @('W3SVC', 'WAS') }
) | Restart-RemoteService -To it@example.com -From noreply@example.com -SmtpServer smtp.example.local -WhatIf
.NOTES
Author : Emanuel De Almeida - https://www.navanem.com
Adapted from a script on SQLServerCentral (script 87230). Test before production.
#>
function Send-Notification {
param(
[string[]] $To,
[string] $From,
[string] $Subject,
[string] $Body,
[string] $SmtpServer
)
if (-not ($To -and $From -and $SmtpServer)) { return } # email is optional
$msg = New-Object Net.Mail.MailMessage
$smtp = New-Object Net.Mail.SmtpClient($SmtpServer)
try {
$msg.From = $From
foreach ($addr in $To) { $msg.To.Add($addr) }
$msg.Subject = $Subject
$msg.IsBodyHtml = $true
$msg.Body = $Body
$smtp.Send($msg)
}
catch {
Write-Warning "Could not send notification: $($_.Exception.Message)"
}
finally {
$msg.Dispose()
$smtp.Dispose()
}
}
function Restart-RemoteService {
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param(
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[Alias('Server')]
[string] $ComputerName,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[Alias('Service')]
[string[]] $ServiceName,
[string[]] $To,
[string] $From,
[string] $SmtpServer
)
process {
$mail = @{ To = $To; From = $From; SmtpServer = $SmtpServer }
if (-not (Test-Connection -ComputerName $ComputerName -Count 2 -Quiet)) {
Write-Warning "$ComputerName is not responding to ping; skipping."
Send-Notification @mail -Subject "Server: $ComputerName - Status" -Body "$ComputerName is not responding to ping. Please investigate."
return
}
foreach ($svc in $ServiceName) {
if (-not $PSCmdlet.ShouldProcess("$ComputerName\$svc", 'Restart service')) { continue }
try {
$service = Get-Service -ComputerName $ComputerName -Name $svc -ErrorAction Stop
Restart-Service -InputObject $service -Force -ErrorAction Stop
Write-Output "Restarted $ComputerName\$svc."
Send-Notification @mail -Subject "Server: $ComputerName - Restart" -Body "$ComputerName\$svc has been restarted."
}
catch {
Write-Error "Failed to restart $ComputerName\$svc : $($_.Exception.Message)"
Send-Notification @mail -Subject "Server: $ComputerName - Error" -Body "$ComputerName\$svc restart failed. Details: $($_.Exception.Message)"
}
}
}
}
# ── Edit and uncomment, or dot-source this file (. .\Restart-RemoteService.ps1) and call the function. ──
# Restart-RemoteService -ComputerName 'Server1' -ServiceName 'ServiceName1' -To 'it@example.com' -From 'noreply@example.com' -SmtpServer 'smtp.example.local'
# Restart-RemoteService -ComputerName 'Server2' -ServiceName 'ServiceName2' -To 'it@example.com' -From 'noreply@example.com' -SmtpServer 'smtp.example.local'
Review before running. Test in a non-production environment first.