Consider this scenario: Your company's configuration standards committee has determined that seven of the Windows XP services that are running by default should be disabled on each workstation on which Windows XP is installed. One of them, they believe, may be a security risk, while the others simply are not needed for company use and are needlessly using resources and posing a potential security risk.
The problem is that there are about 5,000 workstations running Windows XP on your network. You consider using a script to disable these services, rather than do that manually on each individual machine. But you realize that a standard batch file using Windows shell commands will not accomplish the goal. It's true that the "net stop" command will stop a running service, but that service will automatically start again the next time the computer is rebooted. What you need to do is change the start mode of the service from automatic to disabled, and no shell command will do that. At the same time, there is no object in Visual Basic Scripting Edition (VBS) with a method that will achieve this.
But you are not doomed to sitting down at 5,000 individual machines to turn off those services. It turns out that the job can easily be done using Windows Management Instrumentation (WMI). You can write a script that not only will disable each of the services on a local machine but will also disable the services on all 5,000 machines remotely, invoking the script only once.
I'll introduce you to WMI and show you more of the powerful things you can do with it. I'll also discuss accessing WMI using a VBS script and using it to retrieve information from managed resources on a computer.
What is WMI?
Windows Management Instrumentation is a technology built into Windows 2000, Windows XP, and Windows Server 2003 that provides direct access to all of the managed resources on a computer. Those resources include hardware, such as hard disks, network adaptors, video adaptors, BIOS and CPUs, as well as Windows components, such as services, processes, and the registry. You can use WMI to obtain information about those resources or to make configuration changes.
Using WMI, you can write a script that will, for instance:
- Show you a list of all services running on one or more computers, and their current state.
- Show you a list of installed NICs on every computer on your network.
- Tell you how much free disk space is on every computer on your network.
- Extract information from Event Logs and write it to a separate file.
- Retrieve BIOS information.
- Manage computer roles.
- Monitor print queues.
WMI is accessed through a Visual Basic script and the Windows Scripting Host. In my article "First steps in VBS scripting for administrators," I introduced the Windows Scripting Host and the concept of using objects, methods, and properties in Visual Basic scripting, as well as the concept of variables. To access WMI, you use the VBS method GetObject and assign it to a variable using the Set command, like this:
set objWMI = GetObject("winmgmts:\root\cimv2")
In this example, objWMI is a variable that references an object, in this case WMI. It can be named anything, but convention usually dictates that we preface it with an indicator of what type of variable it is. In this example, the prefix obj indicates that it is an object reference. GetObject is a VBS method. Recall that a method is something you can do with an object, such as delete or change, while a property is a value assigned to the object, such as name or description.
What falls in between the parentheses ("winmgmts:\root\cimv2") in the above example, however, is not strictly VBS and requires an introduction to some new concepts.
Monikers, namespaces, and classes
In the above example, winmgmts (which stands for Windows Management System) is called a moniker. A moniker is an intermediate object that allows a VBS script to create a reference to a COM object. WMI is accessed using the winmgmts moniker. In a future article, I'll discuss a moniker that's used to access Active Directory Service Interfaces (ADSI). When we use a moniker to access WMI, we say that we are binding to WMI.
The second part that you see in the parentheses in the example above, \root\cimv2, refers to a specific WMI namespace. Namespaces are grouped hierarchically, similar to the way folders are grouped in windows. Within each namespace is a collection of classes. Basically, each class corresponds to a managed resource. The class Win32_PhysicalMemory, for instance, refers to the installed RAM on a computer. The class Win32_NetworkAdaptor refers to a network interface card, and Win32_Service refers to services installed on a computer. Specific classes are found in specific namespaces, so it's important to know where they are located when binding to WMI.
There are some variations in namespaces, depending on the operating system, version of WMI, and installed software. Figure A shows the top-level namespace configuration on a default Windows XP Professional installation.
You will probably use the CIMV2 namespace more than any other, since it contains the most commonly used classes. Normally, CIMV2 is the default namespace, meaning that you do not have to use the namespace name in your script if you're referring to CIMV2. This is not to be confused with the namespace whose name is DEFAULT, which contains the classes used to manipulate the registry. You can change the default WMI namespace on a local computer, either with a WMI script or with a GUI interface. To use the GUI interface on a Windows XP Professional computer, right-click on My Computer, select Manage from the pop-up menu, and expand Services and Applications. Then, right-click on WMI Control and select Properties, as shown in Figure B.
On the Advanced tab in WMI Control (Figure C), you'll see what the current default WMI namespace is and have the opportunity to change it.
What do you do with it?
So far, we know that within a VBS script, we can bind to WMI and a specific WMI namespace using a moniker with the following line:
set objWMI = GetObject("winmgmts:\root\cimv2")
And we know that the reason we would bind to a WMI namespace is to gain access to a managed resource. There are, of course, hundreds of managed resources within a computer, such as:
Let's say you want to obtain a list of all the services installed on a computer. The script in Listing A would give you that.
To run this script, which you would name something like service.vbs, you would type the following at the command prompt (assuming the path is known):
And, of course, you could redirect the output to a text file, like this:
cscript service.vbs > services.txt
The script that I showed you in Listing A might seem a little imposing at first. However, not only is it fairly straightforward when we examine it, but it can also be easily changed to give us different information, as we'll see shortly.
The first line simply declares the variable objWMI, which is good scripting or programming practice. Much of the second line you already know—it simply binds to the WMI namespace root\cimv2.
Immediately following that, the line invokes the InstancesOf method of GetObject to iterate each instance of the class Win32_Service; in other words, to list each service installed. Note the use of quotation marks in this line, which are required. When you create the script, be sure to use a pure ASCII text editor such as Notepad, rather than a word processing program such as Word. By default, Word uses smart quotes, which your VBS script will not recognize.
The next line invokes the Echo method of the Windows Scripting Host to display the Name property of each instance of Win32_Service. The end result will be a list of every service installed on the computer.
Best of all, with a small change to this script, you could tell it to give you other information. For instance, try substituting "Win32_VideoContoller" for "Win32_Service." The script will display the name of your video card. The same would be true for "Win32_NetworkAdaptor." In other words, this simple script can serve as a template for a variety of WMI scripts.
The script shown in the example above will work only on the local computer. But you're not constrained to working only locally. You can just as easily run the script remotely from another workstation. All you have to do is include the name of the workstation in the WMI path, as you can see in Listing B.
Here, we defined a string variable called strComputer to use in the WMI path. The script can be modified to name any computer on the network. The only requirement is that you must have local administrator rights on the computer that is being queried.
On to bigger things
In part two of this series on WMI, we'll explore WMI in more detail, including where to learn about all of the WMI classes available, along with their properties and methods. We'll see how to modify the start mode of a service and also take a look at making the script a little more efficient with the WMI ExecQuery.
In part three, we'll explore using WMI in the enterprise, in which we can run a WMI script on a large number of computers by invoking it only once.