The other day I came across a PowerShell question on StackOverflow about testing if a service was running on a group of machines.This sparked an idea for a tool to "ping" a service, in much the same way we ping a computer to see if it is up and running, or at least reachable. It is not difficult to use Get-Service, or even WMI, to connect to a service on a remote machine. But I thought it might be nice to have a few extras so I came up with Ping-Service.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
I suppose I could have called it Test-Service, but I liked the idea of pinging a service. Let me layout the core part then I'll touch on a few parts.
Function Ping-Service {
[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter the name of a service")]
[ValidateNotNullorEmpty()]
[string]$Name,
[Parameter(Position=1,ValuefromPipeline=$True)]
[ValidateNotNullorEmpty()]
[string[]]$Computername=$env:computername,
[switch]$Quiet
)
Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"
Write-Verbose "Pinging service $name"
}
Process {
Foreach ($computer in $computername) {
Write-Verbose "Testing computer $computer"
#get service from each computer
$measure=Measure-Command {$service=Get-Service -Name $Name -ComputerName $Computer -ErrorAction SilentlyContinue -ErrorVariable ev}
$end=Get-Date
if ($Quiet -and $service.status -eq "Running") {
#if quiet only return True if service is running
Write-Output $True
}
elseif ($Quiet) {
#status is something other than Running
Write-Output $False
}
elseif ($ev) {
$msg="Error with {0} service on {1}. {2}" -f $Name,$Computer,$ev[0].Exception.message
Write-Verbose $msg
Remove-Variable ev
New-Object -TypeName PSObject -Property @{
Time=(Get-Date -displayhint Time);
Computername=$Computer;
Name=$name;
Displayname=$null;
Status="Unknown";
Response=$measure.TotalMilliseconds;
Reply=$False
} | Select Time,Computername,Name,Displayname,Status,Response,Reply
}
else {
#otherwise, return a custom object
$service | Select @{Name="Time";Expression={(Get-Date -displayhint Time)}},
@{Name="Computername";Expression={$_.Machinename}},
Name,Displayname,Status,
@{Name="Response";Expression={$measure.TotalMilliseconds}},
@{Name="Reply";Expression={
if ($_.Status -eq "Running") {$True} else {$False}
}}
}
} #foreach
}
End {
Write-Verbose "Ending $($myinvocation.mycommand)"
}
} #function
The function takes service and computer names as parameters. You have specify a service name but the computername defaults to the local computer. By default the function writes a custom object to the pipeline which I'll get to in a moment. But you can also use -Quiet which will return True if the service is running and false for anything else, including errors connecting to the service or computer. This allows you to use the command in an If statement.
PS C:\> if (ping-service wuauserv -comp Quark -quiet) {"ok"} else {"down"}
You can pipe computernames to the function but you have to test for the same service on each. I figured this was the most likely usage scenario. I decided to use Get-Service instead of Get-WMIObject. True, WMI can return a bit more information, but I've always found WMI has a bit more overhead and Get-Service runs a little quicker.
$service=Get-Service -Name $Name -ComputerName $Computer -ErrorAction SilentlyContinue -ErrorVariable ev
Normally, I would do something like is in a Try/Catch block. But instead I want the function to keep going which is why I set the erroraction parameter to SilentlyContinue. But, if an exception occurred, I can store it in $ev. Now I have something I can test for later in the function.
elseif ($ev) {
$msg="Error with {0} service on {1}. {2}" -f $Name,$Computer,$ev[0].Exception.message
Write-Verbose $msg
Assuming I get a response and I didn't specify -Quiet, then a custom object is written to the pipeline with service information, a time span that shows how long the Get-Service command took to complete and a boolean indicating if there was a "reply".
Time : 2/7/2012 10:32:05 AM
Computername : jdhit-dc01
Name : wuauserv
DisplayName : Automatic Updates
Status : Running
Response : 4.4942
Reply : True
The function allows me to run expressions like this:
PS C:\> "serenity","jdhit-dc01","quark" | ping-service wuauserv | Where {!$_.Reply} | Select Computername,Status,Reply
Computername Status Reply
------------ ------ -----
quark Unknown False
Download Ping-Service and let me know what you think.
Super Cool Stuff!
Im not sure i really agree with this one. The reply is more so that WMI is working or that the service exists. not if the service is functioning correctly. also the response time is again more so the speed of Network or WMI/System, not really the service itself. I think this one is a little misleading. I like the use of ErrorVariable and the clean up (remove-variable) you dont see people use that as often as they should.
Those are fair criticisms. Perhaps I’m taking poetic license, if such a thing is possible here, a bit too far. At the core all I’m really doing is testing if the service is running on a computer that I can connect to. Using -Quiet should probably be the default and the function name should be Test-RunningService. Everything else is window dressing. Perhaps I should have saved this for a Friday Fun post. But thank you for taking the time to share your thoughts.
lol to be fair, i consider you a very smart person and take it as a challange to one up you, rarely am i able to 🙂
this is a perfect fun friday post. you’ve pulled in a lot of interesting things that people should be aware of and put in to practice. Thanks!