Downloads
99222.zip

Windows Management Instrumentation (WMI) scripts that query multiple computers often take an inordinate amount of time to run due to computers being offline. However, you can dramatically speed up these types of scripts by using the PowerShell statement in Listing 1. Like many useful PowerShell statements, this statement is kind of long. That’s okay. As Larry Wall, the father of Perl, said back in 1990 when he was in a similar situation, “You want it in one line? Does it have to fit in 80 columns?”

A network query to an inaccessible remote machine can take up to a minute to time out on its own. As an example of how significant this is, consider the 180-system LAN I work with. Many of these systems are mobile or simply not turned on all the time, so the typical peak daytime count of connected systems is roughly 100. A WMI script that asks each system whether it has a particular patch installed only takes about 3 seconds per machine to run, or about 5 minutes for all the successful queries. However, the script actually attempts to connect to every single IP address on a private class C subnetwork (i.e., 254 nodes). This means that timeouts caused by the approximately 150 nonresponsive addresses add about 2 hours to the script’s run time—roughly 45 to 55 seconds for each offline system the script attempts to contact. With the statement in Listing 1, I can determine which machines are online and check for the patch only on those machines. As a result of this technique, the script executes in well under 10 minutes.

This technique is so useful, that I turned the statement in Listing 1 into a script for easy use. Before I tell you how to obtain and use this script, though, I want to walk you through how the statement works because it demonstrates many useful features in PowerShell.

The statement in Listing 1 uses Power- Shell’s Where-Object cmdlet (represented by the alias Where). The Where-Object cmdlet is a generic filter. Just like any other filter, it’s designed to remove items you don’t want.

The heart of any Where-Object filter is the code surrounded by braces \{ \}. Where-Object evaluates the code within the braces and attempts to turn any result of the code into a true or false statement. If the statement evaluates to true, Where-Object passes the current object down the pipeline. If the statement evaluates to false, Where-Object silently drops the current object.

Within the braces is the following command:

(Get-WMIObject Win32_PingStatus `
  -Filter "address='$_'")
This command uses the Get-WMIObject cmdlet, which you can use to access WMI classes and their data. In this case, the Win32_PingStatus class is being accessed.

You might be wondering about the $_ notation in the Get-WMIObject command. The current object in the pipeline is substituted everywhere you see $_ in braced code in PowerShell. So, for example, if the current object is srv01, the code “address=’$_’” becomes “address=’srv01’”. In this case, the Get-WMIObject command is the same as the WMI query

SELECT * FROM Win32_PingStatus _
  WHERE address='srv01'
No matter whether you use the Get- WMIObject or WMI query, Win32_Ping- Status returns an object. This object’s StatusCode property contains a numeric code that tells you whether the ping succeeded. If the property’s value is 0, the ping succeeded and you’ll be able to access that remote machine. If the property’s value is a non-zero value, you won’t be able to access the remote machine. (There are several possible non-zero values; all the non-zero values generally mean the remote machine is unavailable or has network connectivity issues. You can find the non-zero values documented at msdn2.microsoft.com/en-us/library/aa394350(VS.85).aspx.)

The Where-Object statement allows only those machines whose pings return a status code of 0 to pass through. Listing 2 demonstrates this. In this code, I piped four computer names—srv01, 192.168.1.254, www.penton.com, and localhost—into the Where-Object statement for testing purposes. Note that:

  • My local network doesn’t contain a computer named srv01.
  • I have a network appliance at 192.168.1.254.
  • The Penton Web server ignores public ping requests.
  • My desktop system responds to queries for localhost.

When I executed the code in Listing 2 in the PowerShell console on my machine, the output contained only 192.168.1.254 and localhost, as Figure 1 shows. Where-Object correctly dropped the computers that didn’t respond to the ping request.

As I mentioned previously, the Where- Object statement is so useful that I turned it into a script, Test-IPNode.ps1, for easy use. Listing 3 shows this script, which you can download by going to www.windowsitpro.com, entering 99222 in the InstantDoc ID box, clicking Go, then clicking the Download the Code Here button.

To use Test-IPNode.ps1, save the script somewhere in your Windows search path. If you save it elsewhere, you need to explicitly specify the path to the script to run it. To quickly ping the remote machines named srv01 and srv02 and only get back the nodes that respond, you would run it like this:

"srv01","srv02" | Test-IPNode

I want to thank James Lim for planting the idea that grew into Test-IPNode. ps1. As Jim noted in his Reader to Reader article “PowerShell Script Lets You Check Patches’ Status” (January 2008, InstantDoc ID 97609), it’s important to learn to apply PowerShell to current problems. By applying PowerShell, I was able to come up with a short, simple script that I can use to speed up virtually all tasks that query individual network nodes.

—Alex K. Angelopoulos, senior network engineer