Managing Systems with PowerShell and WMI


Using WMI in administrative scripts is a common practice. WMI is an extremely powerful way to manage just about every aspect of a system including hardware, software, and configuration. It can also be used to remotely manage systems.

PowerShell has the Get-Wmiobject cmdlet that acts as an interface to WMI. This cmdlet lets you access any WMI class in any WMI namespace. For our purposes, we'll stick to the default namespace of Root\Cimv2 and the Win32 classes since you'll use these classes in 95% of your scripts.

At its simplest, all you have to do is specify a class and run Get-Wmiobject in order for the cmdlet to find all instances of that class and return a page of information:

 PS C:\> get-wmiobject win32_share Name                       Path                       Description ----                       ----                       ----------- E$                         E:\                        Default share IPC$                                                  Remote IPC downloads$                 e:\downloads ADMIN$                     E:\WINDOWS                 Remote Admin C$                         C:\                        Default share PS C:\> 

In this example we asked PowerShell to display WMI information about all instances of the Win32_Share class on the local computer. With this particular class, there are other properties that by default are not displayed. Depending on the class, you might get different results. For example, the following expression displays a long list of properties and values:

 PS C:\> get-wmiobject win32_Processor 

You're probably saying, "That's great, but how do I find out what Win32 classes are available?" All you have to do is ask. The Get-Wmiobject cmdlet has a -List parameter you can invoke. Open a PowerShell prompt and try this:

 PS C:\> get-wmiobject -list | where {$_.name -like "win32*"} 

You should get a long, two column list of all the available Win32 classes. You can query any of these classes using Get-Wmiobject.

The next question most of you are asking is, "How can I find out the properties for a given class?" As we discussed previously, you can use Get-Member to list all the properties for a WMI object. Take a look at the following script:

ListWMIProperties.ps1

image from book
 #ListWMIProperties.ps1 $class=Read-Host "Enter a Win32 WMI Class that you are interested in" $var=get-WMIObject -class $class -namespace "root\Cimv2" $properties=$var | get-member -membertype Property Write-Host "Properties for "$Class.ToUpper() foreach ($property in $properties) {$property.name} 
image from book

This script prompts you for a Win32 WMI class and defines a variable using Get-Wmiobject for that class. We define a second variable that is the result of piping the first variable to Get-Member. All that's left is to loop through $properties and list each one. Here's what you get when you run the script:

 Enter a Win32 WMI Class that you are interested in: win32_logical Properties for WIN32_LOGICALDISK Access Availability BlockSize Caption Compressed ConfigManagerErrorCode ConfigManagerUserConfig CreationClassName Description DeviceID DriveType ErrorCleared ErrorDescription ErrorMethodology FileSystem FreeSpace InstallDate LastErrorCode MaximumComponentLength MediaType Name NumberOfBlocks PNPDeviceID ... PS C:\> 

Usually there are only a handful of properties in which you are interested. In this case, you can use the -Property parameter to specify which properties you want to display:

 PS C:\> get-wmiobject -class win32_processor ` >>-property name,caption,L2CacheSize >> __GENUS          : 2 __CLASS          : Win32_Processor __SUPERCLASS     : __DYNASTY        : __RELPATH        : __PROPERTY_COUNT : 3 __DERIVATION     : {} __SERVER         : __NAMESPACE      : __PATH           : Caption          : x86 Family 6 Model 9 Stepping 5 L2CacheSize      : 1024 Name             :         Intel(R) Pentium(R) M processor 1600MHz PS C:\> 

Here we've asked for the Win32_Processor class, and specifically the name, caption, and L2CacheSize properties. Unfortunately, the cmdlet also insists on displaying system properties such as __Genus.

Since you don't care about those properties most of the time, a neater approach is something like this:

 PS C:\> get-wmiobject -class win32_processor | ` >> select-object name,caption,L2CacheSize | ` >> format-list >> name        :         Intel(R) Pentium(R) M processor 1600MHz caption     : x86 Family 6 Model 9 Stepping 5 L2CacheSize : 1024 PS C:\> 

Here we're calling the same cmdlet, except we use Select-Object to pick the properties in which we're interested.

As you learn to work with WMI, it's helpful to look at what information is populated on a system. This is a great way to learn the different properties and what values you can expect. The more you work with WMI you'll realize that not every property is always populated. There's no reason to spend time querying empty properties. With this in mind, we've put together a helpful script that enumerates all the properties for all the instances of a particular WMI class. However, the script only display properties with a value and it won't display any of the system class properties like __Genus:

ListWMIValues.ps1

image from book
 #ListWMIValues.ps1 $class=Read-Host "Enter a Win32 WMI Class that you are interested in" $var=get-WMIObject -class $class -namespace "root\CimV2" $properties=$var | get-member -membertype Property Write-Host -foregroundcolor "Yellow" "Properties for "$Class.ToUpper() # if more than one instance was returned then $var will be an array # and we need to loop through it $i=0 if ($var.Count -ge 2) {  do {   foreach ($property in $properties) {   #only display values that aren't null and don't display system   #properties that start with __    if ($var[$i].($property.name) -ne $Null -AND ` $property.name -notlike "__*") {    write-Host -foregroundcolor "Green" ` $property.name"="$var[$i].($property.name)    }   }   Write-Host "***********************************" #divider between instances  $i++  }while($i -le ($var.count-1)) } # else $var has only one instance else {   foreach ($property in $properties) {    if ($var.($property.name) -ne $Null -AND ` $property.name -notlike "__*") {    write-Host -foregroundcolor "Green" ` $property.name"="$var.($property.name)    }   } } 
image from book

This script is based on our earlier ListWMIProperties script. Once we have the variable with all the properties, we iterate through all the instances of the specified WMI class. If the property value isn't null and the property name is not like __*, then the property name and its value are displayed. We've even thrown in a little color to spice up the display.

Now that you know about WMI classes and properties, we'll take a closer look at what you do with it. While you can make some system configuration changes with WMI, most of the WMI properties are read-only, which makes for terrific management reports. The Get-Wmiobject cmdlet even has two parameters that make this easy to do on your network.

You can use -Computername to specify a remote computer that you want to connect to and -Credential to specify alternate credentials. However, you can't use alternate credentials when querying the local system. Here's an example:

 PS C:\> get-wmiobject win32_operatingsystem ` >>-computer dc01 -credential (get-credential) >> cmdlet get-credential at command pipeline position 1 Supply values for the following parameters: Credential SystemDirectory : C:\WINDOWS\system32 Organization    : SAPIEN Press BuildNumber     : 3790 RegisteredUser  : Staff SerialNumber    : 69713-640-3403486-45904 Version         : 5.2.3790 PS C:\> 

This example connects to computer DC01 and gets information on the Win32_Operatingsystem WMI object. For alternate credentials, we call the Get-Credential cmdlet that presents a standard Windows authentication box.

VBScript Alert

If you have a library of WMI scripts written in VBScript, don't delete them yet! With a little tweaking, you can take the essential WMI queries from your VBScripts and put them into PowerShell scripts. Use the Where cmdlet for your WMI queries. For example, you may have a WMI query like:

 Select deviceID,drivetype,size,freespace from Win32_LogicalDisk where drivetype='3' 

In PowerShell this can be rewritten as:

 Get-Wmiobject win32_logicaldisk | Select deviceid,drivetype,size,freespace | where {drivetype -eq 3} 

Once you have the core query, you can tweak your PowerShell script and use the filtering, formatting, sorting, and exporting options that are available in PowerShell. If you don't have a script library, there are many, many WMI scripts available on the Internet that you can leverage and turn into PowerShell scripts.

The Get-Wmiobject cmdlet does not allow you to specify multiple classes. So, if you want information from different Win32 classes, you'll have to call the cmdlet several times and store information in variables:

WMIReport.ps1

image from book
 #WMIReport.ps1 $OS=Get-WmiObject -class win32_operatingsystem ` | Select-Object Caption,CSDVersion #select fixed drives only by specifying a drive type of 3 $Drives=Get-WmiObject -class win32_logicaldisk | ` where {$_.DriveType -eq 3} Write-Host "Operating System:" $OS.Caption $OS.CSDVersion #Write-Host `n Write-Host "Drive Summary:"  write-Host "Drive"`t"Size (MB)"`t"FreeSpace (MB)" foreach ($d in $Drives) {  $free="{0:N2}" -f ($d.FreeSpace/1048576)  $size="{0:N0}" -f ($d.size/1048576)  Write-Host $d.deviceID `t $size `t $free } 
image from book

This script reports system information from two WMI Win32 classes. We first define a variable to hold operating system information, specifically the Caption and CSDVersion, which is the service pack. The second class, Win32_LogicalDisk, is captured in $Drives. Since we're only interested in fixed drives, we use the Where cmdlet to filter by drive type.

Once we have this information we can display it any way we choose. Here's what you might see when the script runs:

 Operating System: Microsoft(R) Windows(R) Server 2003, Enterprise Edition Service Pack 1 Drive Summary: Drive   Size (MB)       FreeSpace (MB) C:       8,095   2,417.88 E:       15,006          4,696.01 PS C:\> 

If you prefer a simpler approach to working with WMI and have some experience working with it, you may prefer to use PowerShell's WMI type:

 PS C:\> [WMI]$srv="root\cimv2:win32_computersystem.Name='Godot'" PS C:\> $srv Domain              : WORKGROUP Manufacturer        : Dell Computer Corporation Model               : Latitude D800 Name                : Godot PrimaryOwnerName    : Administrator TotalPhysicalMemory : 1609805824 

By creating a new object of type WMI, we can directly access the WMI instance. PowerShell returns a pre-determined subset of information when you call object as we did above. However, you have access to all the WMI properties which you can see by piping the object to Get-Member:

 PS C:\> $srv|Get-Member 

If we want additional information all we have to do is check the object's property:

 PS C:\> $srv.Status OK PS C:\> $srv.Roles LM_Workstation LM_Server SQLServer Print NT PS C:\> 

While you can't specify alternate credentials, you can specify a remote system like this:

 PS C:\> [WMI]$srv="\\DC01\root\cimv2:win32_computersystem.Name='DC01'" 

To use the WMI type, you must create a reference to a specific instance of a WMI object. For example, you can't create a WMI object to return all instances of Win32_LogicalDisk:

 PS C:\> [WMI]$disk="root\cimv2:win32_logicaldisk" Cannot convert value "root\cimv2:win32_logicaldisk" to type "System.Management.ManagementObject". Error: "Specified argument was out of the range of valid values. Parameter name: path" At line:1 char:11 + [WMI]$disk= <<<< "root\cimv2:win32_logicaldisk" PS C:\> 

Instead, you must specify a single instance by using the WMI object's primary key and querying for a certain value:

 PS C:\> [WMI]$disk="root\cimv2:win32_logicaldisk.DeviceID='C:'" PS C:\> $disk DeviceID     : C: DriveType    : 3 ProviderName : FreeSpace    : 2797834240 Size         : 15726702592 VolumeName   : Server2003 PS C:\> 

That's great, but how do you figure out an object's primary key? The easiest way is to run WBEMTEST and query for a Win32 class. The results are the format you need to use with the WMI type in PowerShell.

Finally, PowerShell also has a WMISearcher type. This allows you to submit a query to WMI and return a collection of objects.

 PS C:\> $d = [WmiSearcher]"Select * from Win32_Logicaldisk ` >>where drivetype = 3' >> PS C:\> $d.get() DeviceID     : C: DriveType    : 3 ProviderName : FreeSpace    : 3007844352 Size         : 15726702592 VolumeName   : Server2003 DeviceID     : E: DriveType    : 3 ProviderName : FreeSpace    : 3738099712 Size         : 24280993792 VolumeName   : XP 

The object, $d, is a collection of all Win32_LogicalDisk instances where drive type is equal to 3. To access to collection we call the Get method. This technique is very helpful when you want to find dynamic information that might include multiple instances of a given WMI class.

 PS C:\> $s=[WMISearcher]"Select * from win32_Service ` >> where StartMode='Disabled'" >> PS C:\> $s.Get()|format-table -autosize ExitCode Name                           ProcessId StartMode State Stat -------- ----                           --------- --------- -----   --     1077 Alerter                                0 Disabled  Stopped OK     1077 ALG                                    0 Disabled  Stopped OK     1077 ClipSrv                                0 Disabled  Stopped OK     1077 FastUserSwitchingCompatibility         0 Disabled  Stopped OK     1077 HidServ                                0 Disabled  Stopped OK     1077 Irmon                                  0 Disabled  Stopped OK     1077 Messenger                              0 Disabled  Stopped OK     1077 msvsmon80                              0 Disabled  Stopped OK     1077 NetDDE                                 0 Disabled  Stopped OK     1077 NetDDEdsdm                             0 Disabled  Stopped OK     1077 RemoteAccess                           0 Disabled  Stopped OK     1077 RemoteRegistry                         0 Disabled  Stopped OK     1077 SCardSvr                               0 Disabled  Stopped OK     1077 SharedAccess                           0 Disabled  Stopped OK     1077 TermService                            0 Disabled  Stopped OK     1077 TlntSvr                                0 Disabled  Stopped OK     1077 WMDM PMSP Service                      0 Disabled  Stopped OK PS C:\> 

You cannot use the WMISearcher type to query a remote system. If you need to do that, then you'll have to continue using the Get-Wmiobject cmdlet.

So when should you use Get-WmiObject and when should you use the WMI type? If you know exactly the WMI object and instance you want to work with, and don't need to specify alternate credentials, then WMI type is the best and fastest approach. Otherwise, use the Get-WmiObject cmdlet.

Read More About It

If you need a primer or even a refresher on WMI, pick up a copy of Managing Windows with VBScript and WMI by Don Jones (Addison-Wesley). If you really want to dig into WMI, get a copy of Windows Management Instrumentation by Matthew Lavy and Ashley Meggitt (New Riders).



Windows PowerShell. TFM
Internet Forensics
ISBN: 982131445
EAN: 2147483647
Year: 2004
Pages: 289

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net