Last Update: Sep 04, 2024 | Published: Mar 09, 2016
You might be familiar with the concept about trying to prove a negative, which is to say, it’s very hard. For an IT pro, this might come up more often than you think. That’s the question I found in an online PowerShell forum asked, “How can I show if something exists or not?” There were two scenarios that he was working with, so let’s use them as learning opportunities.
First, given a list of servers, he want to check for the existence of a service and indicate via a Boolean value if the service exists or not using PowerShell. For my demonstration, I’m going to use Get-CimInstance instead of Get-Service, because I’m trying to get in the habit of not using legacy protocols.
I also have a variable that contains 11 server names that I know are running and that I can access. I’ll forego error handling to keep this as simple as possible. So let’s say I want to report on which servers have the Spooler service installed. It is simple enough to list those that do.
get-ciminstance win32_service -filter "name = 'spooler'" -ComputerName $servers | Select Name,Status,PSComputername
I can’t change the filter to <> ‘spooler’ because that will then give me every other service. One approach would be to break this down into several steps using ForEach-Object so I can process each computer separately.
$servers | foreach { $obj = [pscustomobject]@{ Computername = $_.ToUpper() Service = "Spooler" Verified = "Unknown" } $s = get-ciminstance win32_service -filter "name = 'spooler'" -ComputerName $_ if ($s) { $obj.Verified = $True } else { $obj.verified = $False } #write the object to the pipeline $obj }
In the ForEach loop I’m creating a custom object with three properties. Next, I attempt to get the service. If the command succeeds, $s will have a value, which means I can set Verified to True. Otherwise, I set it to False.
Here are my results:
Now, I know I didn’t want to use Get-Service, but in this particular case, it might be more useful because it will throw an exception if the service is not found.
This means I can catch the exception, although again I will need to process each server one at a time.
$servers | foreach { $computername = $_ #Write-Host "Processing $computername" -ForegroundColor green Try { $s = Get-Service -Name spooler -ComputerName $computername -ErrorAction Stop $Verified = $True } Catch { $Verified = $False } Finally { [pscustomobject]@{ Computername = $Computername Service = "Spooler" Verified = $Verified } } }
In this example, I’m using a Try/Catch block. Note that the ErrorAction is set to Stop for Get-Service. If that is not set, then code in the Catch block will never execute. This is also one of the rare times I use the Finally scriptblock. Code here runs regardless of whether there is an error or not. In this case, I’m using the fact that there’s an error to indicate the service was not found. Remember, I’ve already taken steps to make sure the server is running and that I have access to it.
Finally, as a compromise I could wrap the Get-Service command inside Invoke-Command. I get the benefits of using Get-Service, but the command runs over the single remoting port and essentially in parallel, so it’s also much faster.
Invoke-Command -scriptblock { Try { $s = Get-Service -Name spooler -ErrorAction Stop $Verified = $True } Catch { $Verified = $False } Finally { [pscustomobject]@{ Service = "Spooler" Verified = $Verified } } } -computer $servers | Select PSComputername,Service,Verified | sort Verified
Notice that I simplified the code inside the scriptblock to skip getting the computername. I get that from Invoke-Command.
I suppose the real takeaway is that isn’t necessarily one best way to prove a negative. It might depend on what results you need to capture and how a related cmdlet might handle the situation. If you can catch exceptions, that might be the best route.
There’s a bit more to this topic I want to cover and we’ll look at another scenario in a future PowerShell Problem Solver article.