WMI Enhancements in Windows PowerShell 2.0 CTP

The information in this article was written against the Community Technology Preview (CTP) of Windows PowerShell 2.0. This information is subject to change in future releases of Windows PowerShell 2.0.

On This Page

WMI Enhancements in Windows PowerShell 2.0 But What About the New WMI Cmdlets? Set-WMIInstance Invoke-WMIMethod Remove-WMIObject

WMI Enhancements in Windows PowerShell 2.0

Truth be told, the Windows Management Interface (WMI) capabilities built into Windows PowerShell 1.0 are pretty good; after all, PowerShell makes it very easy to retrieve WMI data. Windows PowerShell enables you to run methods and change property values, and PowerShell cmdlets such as Sort-Object and Group-Object make it a breeze to sort and group data returned by a WQL query, tasks that are difficult, at best, using VBScript.

In all fairness, however, it’s also true that the PowerShell 1.0 Get-WMIObject cmdlet has its weaknesses. For example, suppose you want to retrieve some simple information from an IIS 6.0 server. No problem; all you have to do is issue a command like this, right?

Get-WMIObject -class iiscomputer -namespace "root\microsoftiisv2" -computer atl-iis-001

Right.

No, wait: not right. Look what happens when you issue the preceding command:

Get-WmiObject : Access denied
At line:1 char:14
+ get-wmiobject <<<<  -class iiscomputer -namespace "root\microsoftiisv2" -computer atl-iis-001

Access denied? What the –

Yes, we know: irritating, isn’t it? And don’t bother verifying that you have the proper administrative credentials; you can be System Administrator to the World and you’ll get the same message. That’s because, for security reasons, you can’t connect to any of the IIS WMI classes without first setting the AuthenticationLevel to PacketPrivacy (a DCOM setting that, among other things, encrypts all the data traveling between your computer and the IIS server). That’s a hard-and-fast rule, and the only thing you can do about it is, well, go along with it and set the AuthenticationLevel to PacketPrivacy.

In VBScript that’s a trivial task; for example, here’s a sample VBScript WMI connection string that runs under the proper AuthenticationLevel:

Set objWMIService = GetObject _
    ("winmgmts:{authenticationLevel=pktPrivacy}\\" _
        & strComputer & "\root\microsoftiisv2")

And, of course, in Windows PowerShell 1.0 you … let’s see here, you just … all you have to do is … hmmm ….

As it turns out, you can’t change the WMI AuthenticationLevel in PowerShell 1.0; you also can’t change the ImpersonationLevel or enable all privileges, two other tasks occasionally required when working with WMI. Is that a problem? Well, if you need to manage IIS 6.0, it’s a big problem.

But guess what? In Windows PowerShell 2.0 all those problems go away. In PowerShell 2.0, the Get-WMIObject cmdlet (along with its peer cmdlets Invoke-WMIMethod; Remove-WMIObject; and Set-WMIInstance) includes new parameters for AuthenticationLevel and ImpersonationLevel, as well as a parameter (EnableAllPrivileges) that enables all privileges. Need to use Windows PowerShell 2.0 to get information from an IIS 6.0 server? Well, why didn’t you say so:

Get-WMIObject -class iiscomputer -namespace "root\microsoftiisv2" -computer atl-iis-001 -authentication 6

If that command looks familiar, it should: except for one addition, it’s exactly like the first WMI command we showed you. The difference is the -authentication parameter tacked onto the end:

-authentication 6

In case you’re wondering, the 6 means “PacketPrivacy.” Here are the other values available to you:

Value

Setting

-1

Unchanged

0

Default

1

None (no authentication is performed)

2

Connect (authentication is performed only when the client establishes a relationship with the application)

3

Call (authentication is performed only at the beginning of each call when the application receives the request)

4

Packet (authentication is performed on all data received from the client)

5

PacketIntegrity (all the data transferred between client and application is authenticated and verified)

6

PacketPrivacy (The properties of the other authentication levels are used, and all data is encrypted)

And what do we get back when we run this modified command? This:

__GENUS             : 2
__CLASS             : IIsComputer
__SUPERCLASS        : CIM_ApplicationSystem
__DYNASTY           : CIM_ManagedSystemElement
__RELPATH           : IIsComputer.Name="LM"
__PROPERTY_COUNT    : 10
__DERIVATION        : {CIM_ApplicationSystem, CIM_System, CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER            : ATL-IIS-001
__NAMESPACE         : root\microsoftiisv2
__PATH              : \\ATL-IIS-001\root\microsoftiisv2:IIsComputer.Name="LM"
Caption             :
CreationClassName   :
Description         :
InstallDate         :
Name                : LM
NameFormat          :
PrimaryOwnerContact :
PrimaryOwnerName    :
Roles               :
Status              :

Maybe not the most impressive or useful set of data, but that’s not the point. The point is that now that we can set the AuthenticationLevel, we can manage IIS 6.0 using Windows PowerShell 2.0.

Note. Yes, we can get “cleaner” output simply by piping the returned data to Select-Object and picking out only the properties of interest:

Get-WMIObject -class iiscomputer -namespace "root\microsoftiisv2" -computer atl-iis-001 -authentication 6 | Select-Object Name

Run that command and we end up with output that looks like this:

Name
----
LM

Is that better? We thought so, too.

But What About the New WMI Cmdlets?

Good question: what about the new WMI cmdlets, or, more specifically, what about these new cmdlets:

  • Invoke-WMIMethod

  • Remove-WMIObject

  • Set-WMIInstance

The truth is, these cmdlets don’t really add any new capabilities; what they do, however, is make it easier to carry out common WMI scripting tasks. As we’re about to find out.

Set-WMIInstance

The Set-WMIInstance cmdlet makes it easier and more straightforward to change the value of a read-write property in WMI. Suppose you wanted to change the WMI logging level. In Windows PowerShell 1.0, you’d use code similar to this:

$a = Get-WMIObject Win32_WMISetting -computername atl-fs-001
$a.LoggingLevel = 2
$a.Put()

That’s not too bad, although the Put method (which actually writes the changes to the object) can be a little tricky. That’s due to the fact that WMI’s scripting API actually uses a method named Put_; as a PowerShell script writer it wouldn’t be unreasonable to believe that you’re also using the WMI scripting API. As it turns out, however, you’re not; instead, PowerShell uses the .NET Framework class System.Management. And, in the .NET Framework, the method is named Put rather than Put_.

Note. Yes, it would be much better if the two methods had the same name. For better or worse, however, they don’t.

So how does Set-WMIInstance help? Well, for one thing, you don’t have to mess around with the Put method (or even the Put_ method). Instead, you can change the LoggingLevel using a single command:

Set-WMIInstance -class Win32_WMISetting `
-argument @{LoggingLevel=2} -computername atl-fs-001

See how easy that is? We simply call the Set-WMIInstance cmdlet, followed by three parameters:

-class. The –class parameter specifies the WMI class containing the property value to be changed. Because the Win32_WMISetting class is in the root\cimv2 namespace (the default namespace) we can get away with specifying only the class name. If the class was in a different namespace (e.g., root\MicrosoftIISv2) then we’d need to include the –namespace parameter, just like we did when we retrieved information from an IIS server earlier in this article.

And yes, now that you mention it, all the new WMI cmdlets also include the –Authentication, -Impersonation, and –EnableAllPrivileges parameters.

-argument. The –argument parameter holds two values: the name of the property to be changed, and the new value for that property. These name-value pairs must be passed to –argument in the form of a “hash table” (similar to a Dictionary object). That’s the reason for the syntax @{name=value}; the @ sign followed by a pair of curly braces tells PowerShell to construct a hash table. And, needless to say, the name-value pair LoggingLevel=2 simply says, “Change the value of the LoggingLevel property to 2.

Now, here’s something really cool: by specifying multiple name-value pairs you can change more than one property value with a single command. For example, suppose you want to change the LoggingLevel to 2 and the BackupInterval to 60 minutes. How would you do that? Like this:

Set-WMIInstance -class Win32_WMISetting `
-argument @{LoggingLevel=2;BackupInterval=60} -computername atl-fs-01

As you can see, all we had to do was include both name-value pairs (LoggingLevel=2 and BackupInterval=60) within our hash table, separating the two pairs by a semicolon. Want to change the default namespace to root\default as well? Then put all three name-value pairs in the hash table:

-argument @{LoggingLevel=2;BackupInterval=60;ASPScriptDefaultNamespace="root\cimv2"}

Etc., etc. And, again, no need to call the Put method to write the changes to the object; Set-WMIInstance takes care of that for you.

-computername. Last, but far from least, we have the –computername parameter. This is the spot where you specify the name of the computer on which the change (or changes) should be made. In our sample command, we’re making the change on a computer with the name atl-fs-001; alternatively, we could have specified the computer by IP address (192.1681.1.1) or by fully-qualified domain name (atl-fs-001.fabrikam.com). And sure, we can use a single command to change the LoggingLevel on more than one computer; all we have to do is specify multiple computer names as part of the –computername parameter, taking care to separate those names using commas:

Set-WMIInstance -class Win32_WMISetting `
-argument @{LoggingLevel=2} -computername atl-fs-001,atl-fs-001,atl-fs-003

Here’s a cool little trick. Suppose we have a text file (C:\Scripts\Computers.txt) that lists the names of all our computers. Need to change the LoggingLevel on all your machines? Well, you could type all those computer names as part of the –computername parameter. Or, you could run this command instead:

Set-WMIInstance -class Win32_WMISetting `
-argument @{LoggingLevel=2} -computername (Get-Content C:\Scripts\Computers.txt)

What’s so different about this command? Just one thing, really: instead of hard-coding computer names into the command we’re using the Get-Content cmdlet to read those names in from the file Computers.txt. Set-WMIInstance (like its fellow WMI cmdlets) will then operate against each of the computer names retrieved from that file.

Oh, and if you want to run the command against the local machine as well, you can use this syntax:

-computername = .,atl-fs-001,atl-fs-001,atl-fs-003

As you probably know, the dot (.) is WMI shorthand for the local computer. If you want to run this command only against the local computer then you can simply leave out the –computername parameter altogether. If no computer name is specified, then the WMI cmdlets automatically run against the local computer (and only the local computer). In other words, this command changes the logging level only on the local computer:

Set-WMIInstance -class Win32_WMISetting -argument @{LoggingLevel=2}

That’s pretty much all there is to Set-WMIInstance. And that’s the whole point: the idea is to make it a no-brainer to change read-write properties.

Invoke-WMIMethod

The Invoke-WMIMethod cmdlet is built upon a similar philosophy: make it as easy as possible to execute a WMI method. Let’s take a peek at a sample command that takes a printer named TestPrinter and renames it NewPrinterName:

Invoke-WMIMethod -path "Win32_Printer.DeviceID='TestPrinter'" -name RenamePrinter -argumentList "NewPrinterName" -computer atl-fs-001

Again, there’s not much to this command; all we do is call Invoke-WMIMethod followed by four parameters:

-path. In general, WMI supports two different types of methods: instance methods (methods that are called on specific instances of a class) and static methods (methods that are called on the class itself). In this case we’re calling an instance method: we want to rename a specific instance of the Win32_Printer class. (That is, we only want to rename the printer TestPrinter). Because of that, we use the –path parameter, specifying: 1) the WMI class name (Win32_Printer), and 2) a property name and value (DeviceID='TestPrinter') that enables the script to pinpoint the instance (or instances) we’re interested in.

What if we wanted to run a static method, a method that operates on a class as a whole? In that case we’d still use the –path parameter; however, all we’d specify is the class name, without targeting any specific instances of that class. For example:

Invoke-WMIMethod -path win32_process -name create -argumentList calc.exe

-name. This is the name of the WMI method we want to invoke. Note that we specify the name as used by WMI; don’t include parentheses after the name. Including parentheses after a method name is a standard practice in Windows PowerShell, but here we aren’t directly invoking the method; Invoke-WMIMethod will do that for us. Therefore, we only want to specify the name of the method that Invoke-WMIMethod should execute.

Incidentally, the method specified here must be supported by the class in question. You can use the Delete_ method to delete a printer using Invoke-WMIMethod; that’s because the Win32_Printer class supports the Delete_ method. However, you can’t use the Delete_ method to delete a service. Why not? You got it: because the Win32_Service class doesn’t support the Delete_ method.

-argumentList. This is simply the method parameters. We want to give our printer the name NewPrinterName, so we assign "NewPrinterName" to the -argumentList parameter

-computer. Again, simply the name (or names) of the computers you want this command to work against.

From time-to-time you will encounter a WMI method that requires multiple parameter values. For example, the Create method, a Win32_Share method for creating a new network share, requires the following values, which must be passed in order:

  • Access

  • Description

  • Maximum allowed connections

  • Share name

  • Password

  • Local path

  • Share type

Note. If you are familiar with WMI’s scripting API then you might have noticed that the parameters used by the Invoke-WMIMethod cmdlet are not passed in the same order as they’re passed when you use the Create method and the Scripting API. In this case, Invoke-WMIMethod uses parameters in the order they appear in Wbemtest.exe. Is this true of all WMI methods that use multiple parameters? We’re looking into this right now to verify that. Nevertheless, using the parameter order specified in Wbemtest is a good place to start when working with methods that require multiple parameters.

So how do you check the parameter order in Wbemtest? To begin with, type wbemtest in the Run dialog box. After connecting to the proper namespace, open the class in question, click the method name, and then click Edit Method. In the Method Editor dialog box click Edit Input Arguments. In the Object Editor dialog box, check Hide System Properties and then view the method parameters. It’s a bit of a hassle but, fortunately, most WMI methods require only a single parameter (if that).

So how do you deal with a method that requires multiple parameter values? Well, for starters, you assign those values, in order, to an array:

$a = $Null, "Finance Department share", 100, "FinanceShare", "", "C:\Finance", 0

And then you run this command, passing the array variable to Invoke-WMIMethod’s -argumentList parameter:

Invoke-WMIMethod -class Win32_Share -name Create -argumentList $a

Remove-WMIObject

Remove-WMIObject is an interesting little cmdlet: it enables you to remove instances of WMI objects using a standard syntax. What does that mean? Well, for example, in WMI you can:

  • Delete processes by using the Win32_Process class and the Terminate method

  • Delete a printer connection by using the Delete_ method.

  • Delete a folder by using the Delete method.

That’s three different WMI objects that can be removed, but each one uses a different method. That’s not necessarily hard, but it can be a bit tricky to keep track of which method deletes which type of object.

Now, let’s take a look at how we can accomplish those same tasks using Remove-WMIObject:

$a = Get-WMIObject -query "Select * From Win32_Process Where Name='notepad.exe'"
$a | Remove-WMIObject



$a = Get-WMIObject -query "Select * From Win32_Printer Where DeviceID='TestPrinter'"
$a | Remove-WMIObject



$a = Get-WMIObject -query "Select * From Win32_Directory Where Name ='C:\\Test'"
$a | Remove-WMIObject

As you can see, in all three cases we took the exact same approach: we simply used Get-WMIObject to retrieve an object reference ($a) to the items of interest, then we piped the variable $a to Remove-WMIObject. Remove-WMIObject took it from there.

And yes, that was awfully nice of Remove-WMIObject to take care of all that for us, wasn’t it?