Scripting Solutions with WSH and COM: Creating Simple and Useful Scripts with WMI
To automate and manipulate local and remote computers, you only need to know how to read and write Windows Management Instrumentation (WMI) monikers. This column shows you how to use monikers to quickly create useful WMI scripts.
July 24, 2000
Creating Simple and Useful Scripts with WMI
Last month, I showed you how to use Windows Management Instrumentation (WMI) to connect to local and remote computers and obtain information about the OS, printers, and services. This month, let's look at some quick, useful WMI scripts that automate and manipulate local and remote computers.
WMI can appear complex, but you don't have to understand WMI's complexity to use it. You need only to know how to read and write WMI monikers—the line of code that provides credentials and lets you connect to local and remote computers and control the WMI classes on a machine. Credentials perform at least two functions. First, they authenticate the user to connect to WMI and the namespace (e.g., rootcimv2) within it. Second, they impersonate you to the underlying APIs that the WMI provider calls so that only data you have permission to see is returned to you.
The moniker display name is a text string that represents a WMI class, an instance of a WMI class, or a namespace. The parts that make up a moniker are similar to those of a standard WMI object path. Monikers have the following parts:
The prefix winmgmts (mandatory and not case sensitive)
A security-settings component (optional)
A WMI object path (optional; prefixed by an exclamation point if the code includes a security-settings component)
WMI 1.5 is the default version on Windows 2000 and is available for earlier platforms. WMI 1.5 presumes that the security of a script is run in the security context of the user running the script. If you're running WMI scripts on earlier versions of WMI, you need to include the following code in your security-settings component:
{impersonationLevel=impersonate}
which specifically sets the access to be equivalent to the security of the account context in which the script is executed. For example, compare the following code with Listing 1 to see how you use it:
Set wmiDisk =GetObject _("WinMgmts:" & _"{impersonationLevel=" & _"impersonate}!Win32_LogicalDisk")
In this code, I use the "impersonation" specific security setting that I need when I want to use my default security credentials and connect to a specific object or class on a machine. Alternatively, I can pass a different set of username/password credentials over and even specify whether to use Kerberos or Windows NT LAN Manager (NTLM) authentication methods.
In my explanation of how to write most of the useful scripts you need, I show only the basics without the details. I'll introduce the theory behind the scripts over the course of future articles.
Connecting to Computers
First, you connect to a local computer that is running a WMI server (i.e., any computer with WMI installed) and use the following code to retrieve the default SWbemServices object:
Set wmiObject = GetObject _("WinMgmts:")
The code
Set wmiOnMyComputer = GetObject _("WinMgmts://myComputer")
lets you connect to a remote computer (myComputer) that is running a WMI server and retrieve the default SWbemServices object.
Connecting to a Specific Class
Let's say that I want to interrogate the hard disk of my local machine. In the WMI world, that means I need to retrieve the Win32_LogicalDisk class as an SWbemObject on the local machine. I enter the code in Listing 1. To retrieve the Win32_LogicalDisk class as an SWbemObject on a remote machine (myComputer), I use the default /root/cimv2 namespace for that machine in the code in Listing 2.
However, in Listing 1 and Listing 2, I'm not connecting to a WMI service for disks. Instead, I'm requesting the specific class object for "Win32_LogicalDisk", which describes the properties and methods that I could use if I were interrogating the information about a specific disk. Because I didn't specify the key value in the object path denoting which disk to connect to, if I were to print out the properties of this class, all the local properties would probably be null because this class doesn't have any default values. For this particular class, getting the class object isn't very valuable because you can't create new instances of it and it doesn't have any static methods. So, what I really need to do is to connect to a specific drive; then, I can use the entire gamut of properties and methods on the result.
To connect to a specific drive (C) on the local computer or to a specific drive (C) on a remote machine (myComputer), I first connect to the WMI server, then ask the WMI server to retrieve the relevant information about that disk into the object. I'm not communicating with a service for disks, as Listing 3 and Listing 4 show for the local machine and the remote machine, respectively.
Note that in some of the listings, I use the string strWMIMoniker to hold the moniker. I use this string to prevent the code from formatting badly across the printed page in the journal. If you prefer, you can place the entire moniker directly in the GetObject call.
Using a WQL Query
WMI's query capability is known as WMI Query Language. WQL is a powerful subset of the database query language SQL. In WMI, you can append WQL to a GetObject function and use a WMI moniker to pull selected items from a collection so that you can manipulate items as necessary. Here are a few examples of WQL statements:
Select * from Win32_ProcessSelect * from Win32_Process where Name='iexplore.exe'Select * from Win32_Service where Name='Moose' and state='stopped'
You integrate SQL ExecQuery statements like this:
strWMIMoniker = "WinMgmts:"strQuery = "select * from" _& "Win32_Process"Set wmiProcesses = GetObject _(strWMIMoniker).ExecQuery _(strQuery)
Simple Scripts You Can Use
With these basic techniques in mind, let's look at some useful snippets of code that you can drop into your scripts. Earlier in this article, I explained how to connect to the C drive of a remote computer; after you've connected to an object, manipulating it is easy.
Terminating all copies of Microsoft Internet Explorer (IE) on a remote machine. Let's say that I want to connect to a remote computer called ALISTAIR12 and terminate all the IE sessions that are running on that machine. First, I need to use a moniker to connect to the remote computer. Then, I use an ExecQuery statement to retrieve all processes in which the process name is iexplore.exe. Finally, I iterate through that collection and terminate the sessions one by one. As a bonus, I can also display the name and process ID of the IE sessions that I terminate. Listing 5 shows this sequence of statements.
However, I don't need the wmiProcesses variable here. I use it only in the loop and discard it afterward. Instead, I could put the entire code in one line and eliminate the variable entirely, as Listing 6 shows.
Manipulating services on a remote computer. Last month, I showed you one way to display all the services running on a computer. I can also use an ExecQuery statement to list services, as Listing 7 shows.
Now, let's say that I want to connect to a problem server (VICKY09) to find out which services have stopped on the server. In Listing 7, I show that you can use an ExecQuery statement with a SELECT clause to retrieve all the Win32_Service services from the remote WMI server on that machine. However, if I want to see only the services that have stopped, I need to use a WHERE clause that selects only those services in which the state is equal to 'stopped'. Listing 8 shows this code. Because this list of services is a collection, it has a count property, which I've used in the last line to test for no results and print an appropriate message about what that test reveals.
To take this script one step further, if I want to see just those services that are set to run automatically but for some reason aren't running, I need only change the SELECT clause in the ExecQuery statement, as Listing 9 shows.
If I want to stop a service on a remote machine, I need both the name of the service (e.g., "w3svc" for the Web service) and the name of the remote machine. When I have this information, the script is simple, as Listing 10 shows. In this code, I break up the long lines by using two string variables—a format that some people think is easier to read. I also use the VBScript InputBox functions to retrieve the two required inputs.
With the information I've given here, you can easily find all the services that started automatically and then stopped, and restart them. In theory, the script is as simple as the one in Listing 11. The caveat is that the code in Listing 11 works only for services that have no dependencies (i.e., services that depend on other services already having been started in order to start themselves).
In the next few months, I'll move away from WMI for a while. I'll take a two-part look at Scriptlets, formerly called Windows Script Components (WSC). Then, I'll look at Sapien's PrimalSCRIPT script editor.
About the Author
You May Also Like