26.3 Getting Started with WMI Scripting

Once you have a basic understanding of the WMI architecture, scripting with WMI is easy. In fact, once you understand how to reference, enumerate and query objects of a particular class with WMI, it is straightforward to adapt the code to work with any managed component.

26.3.1 Referencing an Object

To reference objects in WMI, you use a UNC-style path name. An example of how to reference the C: drive on a computer looks like the following:


The format should be easy to follow. The first part of the path (\\dc1\) is a reference to the computer on which the object resides. To reference the computer on which the script is running, you can use a "." for the computer name. The second part (root\CIMv2) is the namespace the object resides in. The third part (Win32_LogicalDisk) is the class of the object to reference. The fourth part is the key/value pairs representing the object. Generically, the path can be shown as follows:


Now that we know how to reference WMI objects, let's go ahead and instantiate an object using VBScript's GetObject function. For GetObject to understand that we are referencing WMI objects, we have to include one additional piece of information: the moniker. Just as we've been using the LDAP: and WinNT: progIDs to reference Active Directory and SAM-based objects in ADSI, we need to use the winmgmts: moniker when we are dealing with WMI objects:

Set objDisk = GetObject("winmgmts:\\dc1\root\CIMv2:Win32_LogicalDisk.DeviceID='C:'")

Note that if you want to reference the C: logical drive on the local computer, you can leave off the computer name and namespace path. The GetObject call would then look like this:

Set objDisk = GetObject("winmgmts:Win32_LogicalDisk.DeviceID='C:'")

You can leave out the namespace path because root\CIMv2 is the default namespace. When accessing a provider that uses any other namespace, you need to include the namespace path. Also, if you are referencing a remote object, you need to include the namespace path even if it is root\CIMv2.

26.3.2 Enumerating Objects of a Particular Class

Now let's look at an example. We want to view all logical disks on a machine, not just a particular disk. To do so, we need to use the InstancesOf method on a WMI object pointing to the namespace of the provider that contains the class. Perhaps an example will make this clear:

strComputer = "."
Set objWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set objDisks = objWMI.InstancesOf("Win32_LogicalDisk")
for each objDisk in objDisks
    Wscript.Echo "DeviceID: " &  objDisk.DeviceID
    Wscript.Echo "FileSystem: " &  objDisk.FileSystem
    Wscript.Echo "FreeSpace: " & objDisk.FreeSpace
    Wscript.Echo "Name: " & objDisk.Name
    Wscript.Echo "Size: " & objDisk.Size
    WScript.Echo ""

Here we get a WMI object pointing to the root\CIMv2 namespace, after which we call the InstancesOf method and pass the Win32_LogicalDisk class. That method returns a collection of Win32_LogicalDisk objects which we then iterate over with a For Each loop.

As you can imagine, this is very powerful and allows you to easily retrieve a list of all the logical disks, services, or processes on a computer. The only issue with the last example is that we needed to know which property methods of the Win32_LogicalDisk class we wanted to see. We can instead retrieve all properties of the Win32_LogicalDisk class using the Properties_ method on each object.

strComputer = "."
strWMIClass = "Win32_LogicalDisk"
Set objWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set objDisks = objWMI.InstancesOf(strWMIClass)
for each objDisk in objDisks
   for each objProp in objDisk.Properties_
      ' Print out NULL if the property is blank
      if IsNull(objProp.Value) then
         Wscript.Echo " " & objProp.Name & " : NULL"
      ' If the value is an array, we need to iterate through each element
      ' of the array
         if objProp.IsArray = TRUE then
            For I = LBound(objProp.Value) to UBound(objProp.Value)
               wscript.echo " " & objProp.Name & " : " & objProp.Value(I)
       ' If the property was  not NULL or a an array, we will print it
            wscript.echo " " & objProp.Name & " : " & objProp.Value
         end if
      end if 
   WScript.Echo ""

26.3.3 Searching with WQL

So far we've shown how to instantiate specific objects, such as a logical drive, and also how to enumerate all the objects of a particular class using the InstancesOf method. Knowing how to do both of these functions will take us a long way with WMI, but we are missing one other important capability: the ability to find objects that meet certain criteria.

The creators of WMI found an elegant way to handle this problem. They implemented a subset of the Structured Query Language (SQL) known as the WMI Query Language (WQL). WQL greatly increases the power of WMI by giving the programmer ultimate control over locating objects.

With WQL, we can even perform the same function as the InstancesOf method we used earlier. The following query will retrieve all the Win32_LogicalDisk objects on the system:

"select * from Win32_LogicalDisk"

We can use any property available on Win32_LogicalDisk objects as criteria in our search. As an example, let's say we wanted to find all NTFS logical disks that have less than 100 MB of available space. The query would look like the following:

select * from Win32_LogicalDisk
where FreeSpace < 104857600
and   filesystem = 'NTFS'

Pretty easy, right? Now let's put WQL to use. We first need to get a WMI object to the namespace we want to query. After we've done that, we can call the ExecQuery method on that object and pass the WQL query to use. The next example uses the "less than 100 MB" query we just described to print out all logical disks on the local computer that match that criterion:

strComputer = "."
Set objWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set objDisks = objWMI.ExecQuery _
            ("select * from Win32_LogicalDisk " & _
             "where FreeSpace < 104857600 " & _
             "and filesystem = 'NTFS' ")
For each objDisk in objDisks
    Wscript.Echo "DeviceID: " & objDisk.DeviceID
    Wscript.Echo "Description: " & objDisk.Description
    Wscript.Echo "FileSystem: " & objDisk.FileSystem
    Wscript.Echo "FreeSpace: " & objDisk.FreeSpace

26.3.4 Authentication with WMI

So far, the examples we've shown assume that the caller of the script has the necessary rights to access the WMI information on the target machine. In most cases in which you are trying to automate a task, that may not be the case. Luckily, using alternate credentials in WMI is very straightforward.

Previously, to connect to a WMI namespace, we would have used the following:

strComputer = "dc1"
Set objWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

But let's say that the person calling the script does not have any privileges on the dc1 computer. We must now use the following:

strComputer = "dc1.mycorp.com"
strUserName = "administrator"
strPassword = "password"
Set objLocator = CreateObject("WbemScripting.SWbemLocator")
Set objWMI = objLocator.ConnectServer(strComputer, "root\cimv2", _
                                      strUserName, strPassword)

We've replaced the single call to GetObject with a call to CreateObject to instantiate a WbemScripting.SWbemLocator object. The SWbemLocator object has a method called ConnectServer, which allows us to specify the target machine, username, and password to authenticate with. You can then use the object returned from ConnectServer to get the instances of a class, perform a WQL search, or any other function.

This was quick introduction to WMI scripting. We will be covering additional tasks, such as invoking an action or modifying properties of an object, as we walk through specific examples later in the chapter.

    Part II: Designing an Active Directory Infrastructure
    Part III: Scripting Active Directory with ADSI, ADO, and WMI