PowerShell

PowerShell Problem Solver: Finding What’s Not There

powershell-hero-img

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

Listing servers with the spooler service
Listing servers with the spooler service (Image Credit: Jeff Hicks)

Sponsored Content

Passwords Haven’t Disappeared Yet

123456. Qwerty. Iloveyou. No, these are not exercises for people who are brand new to typing. Shockingly, they are among the most common passwords that end users choose in 2021. Research has found that the average business user must manually type out, or copy/paste, the credentials to 154 websites per month. We repeatedly got one question that surprised us: “Why would I ever trust a third party with control of my network?

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:

True/False results
True/False results (Image Credit: Jeff Hicks)

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.

A missing service exception
A missing service exception (Image Credit: Jeff Hicks)

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
  }
 }
}

Using Try/Catch/Finally results
Using Try/Catch/Finally results (Image Credit: Jeff Hicks)

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.

Sorted remote results
Sorted remote results (Image Credit: Jeff Hicks)

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.

Related Topics:

BECOME A PETRI MEMBER:

Don't have a login but want to join the conversation? Sign up for a Petri Account

Register
Comments (0)

Leave a Reply

Don't leave your business open to attack! Come learn how to protect your AD in this FREE masterclass!REGISTER NOW - Thursday, December 2, 2021 @ 1 pm ET

Active Directory (AD) is leveraged by over 90% of enterprises worldwide as the authentication and authorization hub of their IT infrastructure—but its inherent complexity leaves it prone to misconfigurations that can allow attackers to slip into your network and wreak havoc. 

Join this session with Microsoft MVP and MCT Sander Berkouwer, who will explore:

  • Whether you should upgrade your domain controllers to Windows Server
    2019 and beyond
  • Achieving mission impossible: updating DCs within 48 hours
  • How to disable legacy protocols and outdated compatibility options in
    Active Directory

Sponsored by: