Microsoft 365


Active Directory


Windows Server


If you are a large enterprise, don't miss our IT cost-cutting webinar!



Compound Filtering with WMI and PowerShell

Jeff Hicks


During the course of the last few articles, we’ve been looking at how to use PowerShell and WMI to query disk information for the %SYSTEMDRIVE% even when you don’t know what that value might be. In this article, I want to introduce a new requirement, which will offer the opportunity to explore some new concepts. If you are just joining us, take a few minutes to get caught up or you won’t understand everything in this article.

When I left you, I had some PowerShell code to query a list of computers using Get-WMIObject. To improve performance, we did a quick ping test to only use WMI for computers that we verified online. For today’s article, I have 11 computers in my list and some of them are unavailable. The new requirement is that need to know which servers have 5 GB or less of free space on the system drive. I don’t need to see any other servers.
If you are new to PowerShell, you might be inclined to modify the last code example and pipe results to Where-Object.

Filtering for freespace (Image Credit: Jeff Hicks)

Filtering for freespace (Image Credit: Jeff Hicks)

You can see that it works, and it took almost 20 seconds to execute in my network. Because I am writing an object to the pipeline with a custom property, FreeGB, I need to use that property in the Where-Object filtering scriptblock. When you use Where-Object like this, it is an example of late filtering. In other words, no filtering is done until the initial collection of objects, in this case disk objects, is complete. There's nothing necessarily wrong with late filtering. Sometimes that is your only option. But if you have an opportunity to filter at the point of collection you typically see some performance gains. This is referred to as early filtering. We are already filtering the Win32_LogicalDisk class by its deviceID to limit the query to the system drive. We can easily add another criteria to the filter using the AND operator. I know it gets confusing but remember that we use the legacy operators in a WMI query not the PowerShell ones. Here's a simple proof of concept.
Testing early filtering (Image Credit: Jeff Hicks)

Testing early filtering (Image Credit: Jeff Hicks)

If the system drive's freespace is greater than 5 GB, then nothing will be written to the pipeline, so all I get are those servers that meet my criteria. Note that because the freespace value is in bytes, I need to specify the comparison value in bytes. I got that number by simply typing 5 GB and a prompt and copying that value. But you can save yourself a step with a subexpression.
With this knowledge, let's revise our code to apply an early filter.
I get the same results as I did with late filtering, but this only took about 17 seconds. Remember some of that time is taken up by pinging each computer as I go along. If I limit my command to computers that I've already verified are online, this gets better.
Using $pinged instead of piping $computers to Test-Connection ran 6.2 seconds using late filtering and around 4.5 seconds using early filtering. Although I also moved the Select-Object expression back inside and that seemed to improve times as well.
$pinged | foreach {
#define a named variable for the computername so that it can be used the Catch
$computer = $_
Try {
  $os = Get-WmiObject win32_OperatingSystem -computername $computer -ErrorAction Stop
  #continue if there were no errors
  Get-WMIObject Win32_Logicaldisk -filter "deviceid='$($os.systemdrive)' AND freespace <=$(5GB)" -ComputerName $computer |
  Select PSComputername,DeviceID,
  @{Name="SizeGB";Expression={$_.Size/1GB -as [int]}},
} #Try
Catch {
  #$_ is the error object
  Write-Warning "Failed to get OperatingSystem information from $computer. $($_.Exception.Message)"
} | Format-Table –AutoSize

There are several mitigating factors surrounding performance with this solution. First, I’m making two WMI connections to the remote computer. I am also limited by the DCOM and RPC network connection to each computer in $pinged. Next time we’ll see what options we have to make this even better. In the meantime, I strongly encourage you to get in the habit of filtering as early in your PowerShell expression as you can. Look for parameters that will help limit what is written to the pipeline to only what you need. In addition to the obvious filter parameter, look for parameters like Name, Include and Exclude as be sure to read cmdlet help to learn how to use them effectively.

More in PowerShell


How To Set Environment Variables With PowerShell

Dec 5, 2022 | Sukesh Mudrakola


How to Use a PowerShell Foreach Loop

Oct 24, 2022 | Ivan Mirchev


How to Use a PowerShell Array

Oct 19, 2022 | Michael Reinders


Filtering with PowerShell Where-Object: Easy Examples

Oct 10, 2022 | Michael Reinders


What is PowerShell and How to Get Started With It?

Sep 7, 2022 | Mike Kanakos


Use a PowerShell Substring to Search Inside a String

Aug 11, 2022 | Jeff Hicks

Most popular on petri

Article saved!

Access saved content from your profile page. View Saved