[ Team LiB ] |
14.2 WMI Scripting with VBScript and PerlThe learning curve to develop WMI scripts is relatively short if you have any experience with a scripting language. 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, including DNS. And fortunately, by understanding just a few guidelines, you can convert VBScript code to Perl and vice versa. 14.2.1 Referencing an ObjectTo reference objects in WMI, you use a UNC-style path name. Here is an example of how to reference the C: drive on the host terminator: \\terminator\root\CIMv2:Win32_LogicalDisk.DeviceID="C:" The first part of the path (\\terminator\) 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 dot (.) for the computer name. The second part (root\CIMv2) is the namespace the object resides in. Each WMI provider uses a namespace to store its associated objects. The third part (Win32_LogicalDisk) is the class of the object to reference. The fourth part is the key/value pairs representing an object of that class. Generically, the path can be described as follows: \\ComputerName\NameSpace:ClassName.KeyName="KeyValue"[,KeyName2="KeyValue2" . . . ] Now that we know how to reference WMI objects, let's instantiate an object using VBScript's GetObject function. In order for GetObject to understand we are referencing WMI objects, we have to include one additional piece of information: the moniker. If you've done any Active Directory scripting before, you're probably familiar with the LDAP: and WinNT: monikers used in ADSI. For WMI, we need to use the winmgmts: moniker: set objDisk = GetObject("winmgmts:\\terminator" & _ "\root\CIMv2:" & _ "Win32_LogicalDisk.DeviceID='C:'") To accomplish the same thing in Perl, we need to use the Win32::OLE module. (The sidebar details differences between VBScript and Perl.) Here is the same code written in Perl: use Win32::OLE; $objDisk = Win32::OLE->GetObject("winmgmts:\\\\terminator" . "\\root\\CIMv2:" . "Win32_LogicalDisk.DeviceID='C:'");
14.2.2 Enumerating Objects of a Particular ClassNow let's enumerate all logical disks on a machine. 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. An example should 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 "" next 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 loop. Since we used a for loop in the last example, we'll show the equivalent code in Perl: use Win32::OLE 'in'; my $strComputer = "."; my $objWMI = Win32::OLE->GetObject("winmgmts:\\\\$strComputer\\root\\cimv2"); my $objDisks = $objWMI->InstancesOf("Win32_LogicalDisk"); for my $objDisk (in $objDisks) { print "DeviceID: ", $objDisk->DeviceID,"\n"; print "FileSystem: ", $objDisk->FileSystem ,"\n"; print "FreeSpace: ", $objDisk->FreeSpace,"\n"; print "Name: ", $objDisk->Name,"\n"; print "Size: ", $objDisk->Size,"\n"; print "\n"; } As you can see, the Perl code is very similar to the VBScript code. One thing to note is that we had to import the in function on the first line, which was later used in the for loop to iterate over the $objDisks collection. VBScript provides this function natively within the language whereas Perl does not. Having the capability to easily obtain the instances of a certain type of class is very powerful. As you can imagine, you could adapt the code to retrieve a list of all CPUs, services, processes, etc., 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 print. We can instead retrieve all properties of the Win32_LogicalDisk class using the Properties_ method on each object as shown here: 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" else ' 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) next else ' If the property was not NULL or an array, we print it wscript.echo " " & objProp.Name & " : " & objProp.Value end if end if next WScript.Echo "" next 14.2.3 Searching with WQLSo 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 complete control over locating objects. With WQL, we can even perform the same function as the InstancesOf method we used earlier. The following query retrieves 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, huh? Now let's put WQL to use. First, we 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 next 14.2.4 Authentication with WMISo 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 = "terminator.movie.edu" set objWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") But let's say that the person calling the script does not have any privileges on terminator. We must now use the following: strComputer = "terminator.movie.edu" 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.[1] We 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 a quick introduction into WMI scripting. We'll cover additional tasks, such as invoking an action or modifying properties of an object, as we walk through specific DNS examples later in the chapter. Now, back to the good stuff . . . |
[ Team LiB ] |