Thursday, November 17, 2011

Tips: Use Get-WMIObject – WMI PowerShell Tricks for a better managementof Windows Server

Brief Introduction
All the IT Professionals are familiar with Windows Servers and Windows Management Instrumentation or WMI. We can say this technology is a staple in Microsoft Operating systems since the days of Windows 2000. If you use WMI, you can identify all the types of management and system information from the BIOS to logical disks to the operating system. Earlier, we were using the VBScript to access this treasure trove. As Windows PowerShell arrived, it has become much easier and flexible to work with WMI.
PS S:\> get-wmiobject win32_operatingsystem | select InstallDate,Caption

InstallDate Caption

----------- -------

20110128161223.000000-300 Microsoft Windows 7 Ultimate

We can say the primary cmdlet is Get=WMIObject. Here, we have some tricks and will let you know about using the command. You may also use the mentioned WMI PowerShell tricks to get system information or to help develop your own WMI scripts.

PS S:\> get-wmiobject win32_operatingsystem |
>> select @{Name="Installed";
>> Expression={$_.ConvertToDateTime($_.InstallDate)}},
>> Caption

Installed Caption
--------- -------
1/28/2011 4:12:23 PM Microsoft Windows 7 Ultimate

WMI datetime
First, it is required to convert the WMI datetime strings like this: 20110128161223.000000-300. However, we were saying this was a bit of string parsing. Let’s have an example.
PS S:\> [wmi]$c="win32_logicaldisk.DeviceID='C:'"
PS S:\> $c

DeviceID : C:
DriveType : 3
ProviderName :
FreeSpace : 39053930496
Size : 487439986688
VolumeName :

If you’ve the user friendly install date, it will be much better. Fortunately the PowerShell team thought the same thing and every WMI class in PowerShell has a method called ConvertToDateTime(). You can pass a WMI datetime object as the method parameter to get a friendly datetime back. Using the hash table with Select-Object will be the easiest way to invoke.
PS S:\> [wmi]$c="\\quark\root\cimv2:win32_logicaldisk.DeviceID='C:'"
PS S:\> $c

DeviceID : C:
DriveType : 3
ProviderName :
FreeSpace : 131695562752
Size : 201504845824
VolumeName :

Here, a new property is created in the hash table. The name of the property is Installed. The value will come from the the Expression scriptblock which is invoking the ConvertToDateTime() method from the current object ($_) and converting the current object’s InstallDate property.

PS S:\> [wmiclass]$product="Win32_Product"

Usually, we can get all the instances from Get-WMIObject. However, if you are aware of the instance that you need, you can get it using the [WMI] type accelerator.
PS S:\> [wmiclass]$product="\\quark\root\cimv2:Win32_Product"

It means that you’re connecting to a root\cimv2 namespace class. The key you see while running a query in WBEMTest is the same one which you used in the part after the period. Therefore, you can say it’s a unique identifier. You can use the same technique with the remote computers by simply specifying the full WMI path.
PS S:\> $product.methods | select Name

PS S:\> $product.Properties | select Name


Though it doesn’t support alternate credentials, this is quick and easy.
What is WMIClass?
Unfortunately this is hardly practical for a long running query. Instead do something like this:
PS S:\> $props="Caption","Description","InstallDate","Version"
PS S:\> get-wmiobject win32_product -Property $props –asjob

WMIClass is another WMI accelerator we can use. If you wish to discover the available properties or methods, you can run a Get-WMIObject expression and pipe it to Get-Member.
As before, you can also query a remote computer.
This is very handy when querying a class this doesn’t exist locally. Now we can see methods and properties.
If you identify some properties, you can create a more effective PowerShell expression.
There are two tips in this command. First one is, you can speed up the WMI query a bit by using the –Property parameter. If don’t wish to have all the properties, you can specify the ones that you wish to have. As the Win32_Product class has many properties, you can get a faster response by limiting my query. At the same time, you can take the advantage of the –AsJob parameter to create a background job. Remember that this particular class is slow to query.
If you retrieve the results, you can also get the system properties like _CLASS which actually is not required. However, you can easily filter them by piping to Select-Object and specify the array of property names a second time.
PS S:\> receive-job 1 -keep | select $props

Let’s take this to another level and bring in splatting.
Discussion about Splatting
With the help of splatting, you can take a hash table of parameter values and pass it to the cmdlets. Let’s have a sample hash table.
PS S:\> $h=@{Class="win32_computersystem";
>> Property="Manufacturer","Model","Name","TotalPhysicalMemory";
>> Computername=$env:computername
>> }

The hash table keys are corresponded to the cmdlet parameter names. You can also pass the hash table by its name using the @ to indicate splatting. If you wish you can also re-use the hash table to filter out the WMI system properties.
PS S:\> get-wmiobject @h | select $

Manufacturer : TOSHIBA
Model : Qosmio X505
TotalPhysicalMemory : 8577855488

PS S:\> $h=@{Class="win32_service";
>> Property="Name","Displayname","StartMode","StartName","State","PathName";}
PS S:\> Get-Content computers.txt | foreach { get-wmiobject @h -comp $_}

PS S:\> get-wmiobject @h -comp (Get-content computers.txt)

PS S:\> invoke-command {Param([hashtable]$h) get-wmiobject @h} -arg $h -comp (get-content computers.txt)

B  y