Downloads
37596.zip

A trend seems to be growing in Redmond. Microsoft is taking administrative tools and command-line utilities once shackled to run on the local computer and transforming them into remote administration juggernauts. The catalyst behind the change is Windows Management Instrumentation (WMI), the underlying instrumentation on which the tools are built.

An often-overlooked fact about WMI is that from the beginning, Microsoft designed WMI to support remote administration scenarios. Any task that you can perform locally with a WMI-enabled tool, you can also perform remotely. To demonstrate WMI's remote administration agility, I want to walk you through several scenarios. First, I present a simple WMI script that performs an administrative task on the local computer. Next, I show you how to easily transform the script into a remote-savvy script. Finally, I show you how to run the script against all the computers on a remote IP subnet.

To get the most from this article, you need a basic knowledge of WMI and WMI scripting. If you need to build some skills, I encourage you to read Microsoft's "WMI Scripting Primer: Part 1" at http://msdn.microsoft.com/library/en-us/dnclinic/html/scripting06112002.asp.

Pick a Task, Any Task
The administrative task that I want to perform on the local computer is to check the Telnet service's Startup Type and Status. Now, before you say, "Please, not another services script," let me stress that the task is largely irrelevant. You can easily substitute this task with any of the hundreds of WMI scripts available at the Microsoft TechNet Script Center (http://www.microsoft.com/technet/scriptcenter). My objective is to show you how to take a common WMI script and scale it to service hundreds or thousands of remote computers.

Listing 1, page 54, shows the local version of the Telnet Server script, appropriately named TelnetCheck.vbs. The script begins by initializing the strComputer variable with a string that consists of a single dot, or period. In WMI scripting, the dot represents the local computer. Because this script runs on the local computer, I could have omitted the strComputer variable, but isolating the target computer variable (as Listing 1 shows) makes adapting the script to support remote computers easier.

The script uses VBScript's GetObject function to connect to the WMI service on the target computer. The string that the script passes to the GetObject function is a WMI connection string (aka a moniker) that consists of three components:

  • the mandatory WMI prefix, "winmgmts:"
  • security settings that tell WMI to impersonate the caller (i.e., use the security context of the person or process running the script to establish the connection)
  • a WMI object path that comprises the target computer name and the WMI namespace to connect to on the target computer

GetObject returns a reference to the WMI scripting library's SWbemServices object, which represents an authenticated connection to WMI on a local or remote computer. To learn more about the WMI connection string, see "WMI Monikers," May 2001, http://www.winnetmag.com, InstantDoc 20401.

After the connection is established, Listing 1 calls the SWbemServices ExecQuery method to retrieve the instance of the Win32_Service class that represents the Telnet service. ExecQuery returns a reference to an SWbemObjectSet collection—another automation object in the WMI scripting library. If Telnet is installed on the target computer, ExecQuery returns a collection containing exactly one item, which is assigned to the reference variable named colServices. If Telnet isn't installed, ExecQuery returns an empty collection. The script uses the SWbemObjectSet Count property to determine whether Telnet is installed on the target computer. If the Count property is equal to 0, the WScript Echo method echoes to the console a message stating that Telnet isn't installed. Otherwise, the Echo method echoes the Telnet service's Startup Type (the Win32_Service StartMode property) and Status (the Win32_Service State property).

To run the script in Listing 1, open a command prompt and type the following (assuming you saved the script to a directory named C:\scripts):

C:\scripts>cscript telnetcheck.vbs

If Telnet is installed on the local computer, you should see a message similar to the following echoed to the console:

ATL-LAB-01,Disabled,Stopped

Go Remote
As you might guess, modifying Listing 1 to support remote computers is as simple as changing the value assigned to the strComputer variable to the name of any WMI-enabled computer in your domain. For example, to run the script against a remote computer named ATL-WEB-01, simply change

strComputer = "."

to

strComputer = "atl-web-01"

Of course, modifying the script each time you want to target a different remote computer isn't practical. A better solution would be to provide the remote computer name to the script as a command-line argument, as Listing 2 shows. The only difference between Listings 1 and 2 is that the line initializing strComputer in Listing 1 has been replaced with the code at callout A in Listing 2. The If...Then...Else statement at callout A checks for the presence of a command-line argument. If you pass an argument to the script, strComputer is initialized with the argument's value. Otherwise, strComputer is initialized with a dot, which represents the local computer. The remainder of Listing 2 is identical to Listing 1.

The following command demonstrates how to run Listing 2 against a remote computer named ATL-WEB-01:

C:\scripts>cscript
   telnetcheck.vbs atl-web-01

The transparency of WMI remote access underscores one of WMI's most important features: Any task that you can perform locally, you can also perform remotely. WMI uses Distributed COM (DCOM), which uses remote procedure call (RPC) over a suitable network transport to access remote computers. Before you start feeling insecure about the ease with which WMI makes remote administration possible, let me point out that, by default, you must possess Administrator-equivalent credentials on the remote computer before you can use WMI to access it remotely. If you don't have such credentials and you try to run Listing 2 against a remote WMI-enabled computer, for example, GetObject will fail with a Permission denied error.

Here's a tip: To remote-enable the hundreds of WMI scripts available at the Script Center, simply replace the

strComputer = "."

line in the Microsoft script with the code at callout A in Listing 2. By doing so, you'll instantly create hundreds of scripts that can target remote computers; you simply need to specify the remote computer's name on the command line when you run the script.

Remotely Manage an Entire IP Subnet
You can use DNS-style names and IP addresses—in addition to NetBIOS names—
to identify remote computers. Suppose the computer named ATL-WEB-01 is assigned an IP address of 192.168.0.3 and resides in the atl.acme.com domain. Using the script that Listing 1 shows, you can use any of the following values to target the remote computer:

strComputer = "atl-web-01"
strComputer = "192.168.0.3"
strComputer = "atl-web-01.atl.acme.com"

WMI's recognition of DNS names and IP addresses opens the door for creative scripting solutions. The script in Listing 3, for example, demonstrates how to run a WMI script against an entire IP subnet.

Listing 3 begins by initializing the strIPSubnet variable with the network portion of the target subnet. (In my example, I use the private 192.168.0.0/24 class C network.) Next, the script creates a Windows Script Host (WSH) Shell object. The Shell object, which the variable objShell references, will call the Shell object's Exec method (I explain why in a moment). The script also enables VBScript's error-handling mechanism, On Error Resume Next, to trap the error that occurs if the script tries to connect to a non-WMI device or if the script tries to connect to a WMI-enabled computer without administrator credentials.

The real work begins with the For loop. During each loop iteration, the For loop increments a counter named strIPNode. The strIPNode counter represents the host portion of the target IP address. The first step inside the body of the For loop concatenates the current value of strIPNode with strIPSubnet and stores the resulting string in strComputer.

Next, the script uses the WSH Shell object's Exec method and the OS's ping.exe utility to ping the target computer. The reason I'm first pinging each computer has to do with the way WMI connections behave. Under typical conditions, WMI has a rather long connection timeout of 1 minute or longer. If a large number of computers in the target subnet are offline or unreachable, the script's performance suffers accordingly.

Suppose, for example, that half of the nodes in a typical class C subnet are unreachable. Without first running the Ping utility, the script will take several hours to run while WMI tries to connect to every IP address, only to time out on half of the addresses. If you have the script first ping each computer, you reduce the amount of time the script spends determining whether a computer is online and reachable.

I use the WSH Shell object's Exec method to run Ping. To control Ping's behavior, I reduce the number of echo requests that Ping sends to 2 (-n 2) and I set the reply timeout to 1000 milliseconds (-w 1000). By doing so, I reduce a 60-second or longer check (i.e., the WMI connection timeout) to a couple of seconds.

The Exec method returns a reference to a WshScriptExec object that the script can use to interact with and control the spawned process. I use the returned objScriptExec reference to capture Ping's output, which I then convert to lowercase and store in the strPingStdOut variable. Next, to determine whether Ping was successful, I use VBScript's InStr function to search for the substring "reply from" (followed by the target IP address) in Ping's output.

If Ping was successful, the code at callout A attempts to establish a WMI connection with the remote computer that the IP address stored in strComputer references. The GetObject call is identical to the GetObject call that you see in Listings 1 and 2.

Although I pinged the node to ensure that it's online, GetObject can fail if the node that the IP address in strComputer references isn't a WMI-enabled computer or if the security context under which the script is running doesn't have administrator access to the remote computer. The On Error Resume Next statement (enabled earlier) and the error-checking code immediately following the call to GetObject catch both errors.

The remainder of Listing 3's script—after the WMI connection is established—is identical to Listings 1 and 2. Following is a portion of the output that Listing 3 produces.

ATL-LAB-01: Disabled,Stopped
ATL-DC-01: Manual,Stopped
ATL-WEB-01: Disabled,Stopped
192.168.0.4: Permission denied
192.168.0.5: Telnet not installed.
192.168.0.6: Telnet not installed.
192.168.0.7: Host unreachable

If you're curious, the node with the IP address 192.168.0.4 is a Windows XP Home Edition computer, which doesn't permit remote WMI connections.

To run Listing 3 on a range of IP addresses in your network, you'll need to change the value assigned to strIPSubnet and the starting and ending values assigned to strIPNode in the For statement. You'll also need to ensure that you're running WSH 5.6, the version that introduced the Shell object's Exec method.

Put It to Work
You're probably wondering how you would adapt Listing 3 to run one of the WMI scripts in the Script Center. The good news is that the answer is simpler than you might think.

Suppose you want to remotely run the "Restart a Computer" script (which you'll find at http://www.microsoft.com/technet/scriptcenter/compmgmt/scrcm38.asp) against all the computers in a specified IP address range. You would follow these steps:

  1. Replace the code at callout A in Listing 3 with the GetObject call in Microsoft's script:
  2. Set objWMIService =
      GetObject("winmgmts:" & _
      "\{impersonationLevel=impersonate," & _
      "(Shutdown)\}!\\" & strComputer & _
      "\root\cimv2")
  3. Replace the code at callout B in Listing 3 with the ExecQuery call in Microsoft's script:
  4. Set colOperatingSystems = _
      objWMIService.ExecQuery _
      ("Select * from _
      Win32_OperatingSystem")
  5. Replace the code at callout C in Listing 3 with the For Each loop in Microsoft's script:
  6. For Each objOperatingSystem in _
      colOperatingSystems _
      ObjOperatingSystem.Reboot()
    Next

    Listing 4 shows the final script. A word of caution before you unleash Listing 4 on your network: The script will also restart the computer running the script if the computer is assigned an IP address in the range of the target subnet. If such is the case, you can add an additional decision statement (If...Then...Else) inside the body of the main For loop to force the script to skip the local computer. To turn a local computer script into an enterprise juggernaut, you can apply the previous steps to most of the WMI scripts in the Script Center.

    What About Security?
    WMI security is robust. Although I don't have the space to delve into the nitty-gritty details of WMI security, I'll try to put any immediate concerns to rest and follow up with details in a subsequent article.

    Whenever you use WMI, you pass through several security checkpoints. I've already mentioned the first: the WMI permissions checkpoint. WMI permissions are implemented at the namespace level (e.g., \root\cimv2) and checked when you establish a connection to WMI on a local or remote computer. You use the Microsoft Management Console (MMC) WMI Control snap-in to configure WMI permissions. By default, administrators are the only trustees granted the Remote Enable WMI permission.

    The second checkpoint is DCOM security. The default DCOM setting for WMI is to impersonate the caller, which is the person or process running the script. The third and final checkpoint is the OS's security subsystem. The basic rule is that WMI doesn't provide access to anything you don't already have access to.

    Finally, what about your Web browser? The good news is that all the WMI scripting objects (e.g., SWbemServices, SWbemObject) in the WMI scripting library are marked as unsafe for scripting. So, providing your users haven't lowered their Web browser's security settings, you're safe. However, if your users have lowered their browsers' security settings, you might consider implementing a system policy to manage your organization's security settings.