Hip, Hip, Array: Retrieving Multi-Valued WMI Properties Using Windows PowerShell 

 

 

If you’ve ever written a WMI script then you’ve probably encountered the dreaded property-stored-as-an-array issue. And if you’ve failed to handle this problem properly, well, don’t feel bad: in the original Scriptomaticthe Scripting Guys forgot all about multi-valued properties. As a result, Scriptomatic1.0 didn’t always return all the data available to you (a problem that was fixed in Scriptomatic 2.0 ).

 

Live and learn, huh?

 

If you aren’t sure what we’re taking about then consider this simple little VBScript script, one designed to return the MAC address and IP address for each IP-enabled network adapter installed on a computer:

 

strComputer = "."

Set objWMIService= GetObject (" winmgmts:\\" & strComputer& "\root\cimv2")
Set colItems= objWMIService.ExecQuery_
    ("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled= True")

For Each objItemin colItems
    Wscript.Echo objItem.MACAddress
    Wscript.Echo objItem.IPAddress
Next

 

Looks harmless enough, doesn’t it? But here’s what happens when you run the script:

 

00:0D :56:F8:1C:3F
C:\scripts\x.vbs(9, 5) Microsoft VBScript runtime error: Type mismatch

 

Yikes. As you can see, the script starts off by echoing back the MAC address. That’s good. But then it blows up with a “Type mismatch” error when it tries to echo back the IP address. That’s bad.

 

The problem is that a network adapter can actually have more than one IP address; therefore, IP addresses are stored as an array. (That’s true even if a network adapter doesn’t have anyIP addresses.) Because the IPAddressproperty is an array, you can’t directly echo back the value of that property; instead, you need to set up a For Each loop and loop through the collection of values. Here’s a revised script that uses a For Each loop to correctly handle the IPAddressproperty:

 

strComputer = "."

Set objWMIService= GetObject (" winmgmts:\\" & strComputer& "\root\cimv2")
Set colItems= objWMIService.ExecQuery_
    ("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled= True")

For Each objItemin colItems
    Wscript.Echo objItem.MACAddress
    For Each strAddressin objItem.IPAddress
        Wscript.Echo strAddress
    Next
Next

 

And here’s the output we get when we run this script on a computer with a single IP-enabled network adapter:

 

00:0D :56:F8:1C:3F
192.168.244.186

 

Much better.

 

If you write your scripts in VBScript, well, problem solved: just make sure you use a For Each loop any time you encounter a multi-valued property such as IPAddress. But what if you’re getting started with the new Windows PowerShell ? If that’s the case, then you might not know how to set up a For Eachloop to loop through a multi-valued property. Is there a similar solution for Windows PowerShell scripters, one just as easy as using a For Each loop?

 

To answer that question, let’s take a look at a Windows PowerShell script that doesn’tuse a For Each loop to iterate values in the IPAddressproperty:

 

$ strComputer= "."

$ colItems= get- wmiobject-class "Win32_NetworkAdapterConfiguration" -namespace "root\CIMV2" `
- computername$ strComputer-filter " IPEnabled= true"

foreach ($ objItemin $ colItems) {
      write-host " MACAddress: " $ objItem.MACAddress
      write-host " IPAddress: " $ objItem.IPAddress
      write-host
}

 

Note. In Windows PowerShell the grave accent character (`) is used to indicate a line break. In that respect it is equivalent to the underscore character (_) in WSH and VBScript.

 

Here’s the output we get when we run the script:

 

00:0D :56:F8:1C:3F
192.168.244.186

 

From the error message we can clearly see that – wait a second: the script worked, even without a For Each loop! As it turns out Windows PowerShell, unlike VBScript, isn’t fazed by a property that stores values as an array; instead, Windows PowerShell handles the situation itself, without any additional coding on your part. And that’s a nicer touch than you might think. Admittedly, working with an array property isn’t particularly hard: after all, it’s just a matter of setting up a For Eachloop to loop through all the values. What ishard is knowingwhich properties store values as arrays and which ones don’t; if you mistakenly assume a multi-valued property is a plain old single-valued property your script will fail to return important data. Take this script, for example:

 

On Error Resume Next

strComputer = "."

Set objWMIService= GetObject (" winmgmts:\\" & strComputer& "\root\cimv2")
Set colItems= objWMIService.ExecQuery_
    ("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled= True")

For Each objItemin colItems
    Wscript.Echo objItem.MACAddress
    Wscript.Echo objItem.IPAddress
Next

 

This script won’t return any values for the IPAddressproperty; however, because of the On Error Resume Nextstatement it won’t return an error message, either. Because there are no error messages you might assume that the script is fine and that your network adapters just don’t have any IP addresses assigned to them. That’s probably not true, so this assumption could cause problems for you later on. (For example, you might spend a lot of time trying to figure out whyyour network adapters don’t have any IP addresses assigned to them.)

 

But that’s a problem you won’t have to worry about with Windows PowerShell; Windows PowerShell will correctly handle both single-valued and multi-valued properties, without you having to identify and code for the multi-valued ones. And that’s pretty cool: as you might have guessed, the Scripting Guys are always in favor of anything that does our work for us.