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
End of Article