Monthly Archive


WMI wildcards and filtering

A question on the forum asking about filtering WMI results raises a number of interesting points.


The user wanted to pass a computername and a filter term to pull product information from remote machines. I ended up with this

$computername = $env:COMPUTERNAME
$filter = 'Live'

$scriptblock = {
    Get-WmiObject -Class Win32_product -Filter "Name LIKE '%$filter%'" |
    Select  IdentifyingNumber, Name, LocalPackage }

Invoke-Command -ComputerName $computername -ScriptBlock $scriptblock -ArgumentList $filter


You can pass an argument into the scriptblock you use with invoke-command by using the –Argumentlist parameter.

More interesting is the –Filter parameter on Get-Wmi-Object

-Filter "Name LIKE '%$filter%'"


Notice that % is the wildcard not * as you’d use for a string.  Its always better to filter the results from Get-WmiObject using –Filter rather than a where-object after the call.


Of course you can just use the wmi or cim cmdlets directly for this problem which is even better

Get-WmiObject -Class Win32_Product -ComputerName $computername -Filter "Name LIKE '%$filter%'" | Select  IdentifyingNumber, Name, LocalPackage


Get-CimInstance -ClassName Win32_Product -ComputerName $computername -Filter "Name LIKE '%$filter%'" | Select  IdentifyingNumber, Name, LocalPackage

WMI linked classes

You will find that many WMI classes have links – some are associations built in to WMI (a subject for another time) while other classes can be linked based on property values. An example of the latter is the Win32_NetworkAdapter and Win32_NetworkAdapterConfiguration classes. The DeviceId on Win32_NetworkAdapter  matches Index on Win32_NetworkAdapterConfiguration .


The follwoing function (adapted from a question on the forum) shows one way this link can be used

function Get-NetworkInfo{
    param( [string]$ComputerName )

    $networkinfo = @()

    $networks = Get-WmiObject Win32_Networkadapter -Filter 'NetEnabled=True' -ComputerName $ComputerName
    $adapter = 0
    foreach($network in $networks){

        $id = $network.DeviceId
        $IPinfo = Get-WmiObject win32_networkadapterconfiguration -Filter "Index = $id" -ComputerName $ComputerName
        $winServers = 0
        $winServers = ($IPinfo.WinsPrimaryServer -join ','),($IPinfo.WinsSecondaryServer -join ',')

        $adapter += 1

        $props = @{
            'Adapter' = $adapter;
            'Manufacturer' = $network.Manufacturer;
            'Description' = $network.Description;
            'Connection' = $network.NetConnectionID;
            'SpeedGB' = [math]::Round($network.Speed / 1GB, 2)
            'IPAddress' = $IPinfo.IPAddress -join ','
            'Submask' = $IPinfo.IPSubnet -join ','
            'Gateway' = $IPinfo.DefaultIPGateway -join ','
            'DNSServers' = $IPinfo.DnsServerSearchOrder-join ','
            'WinServers' = $winServers -join ','
            'DomainSuffixes' = $IPinfo.DNSDomainSuffixSearchOrder -join ','
        $networkinfo += New-Object -TypeName psobject -Property $props
Get-NetworkInfo -ComputerName $env:COMPUTERNAME

NICs with IP addresses

A question on the forum asked about discovering those network adapters that had IP addresses configured. The user had tried

PS> Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPAddress IS NOT NULL"
Get-WmiObject : Invalid query "select * from Win32_NetworkAdapterConfiguration where IPAddress IS NOT NULL"
At line:1 char:1
+ Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPAdd ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-WmiObject], ManagementException
    + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand


Actually they’d tried the full WQL query. I don’t do that because its more typing than using the class name and a filter.


The problem is that IPAddress is a string array and WQL won’t let you query against an array of properties.


You have 2 choices

Get-WmiObject -Class Win32_NetworkAdapterConfiguration | where IPAddress


which is fine fro local machines but means you are filtering out results on the local machine when querying a remote machine – wasteful.


A better solution is to use the IPEnabled property. This is a boolean that is set to TRUE when an IP address (static or DHCP) has been set on a NIC. Your query then becomes

Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled = $true"


Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = $true"

Win32_ReliabilityRecords Class

The Win32_ReliabilityRecords class was introduced with Windows 7. It contains information from the Windows Event Log related to system reliability.


The most interesting properties are the Message and the TimeGenerated

£> Get-WmiObject -class win32_reliabilityRecords | select -First 1 | fl Message, TimeGenerated

Message       : Installation Successful: Windows successfully installed the following update: Definition Update for  Windows Defender - KB2267602 (Definition 1.207.1367.0)
TimeGenerated : 20150929170914.620000-000


Notice the usual delightful date format on TimeGenerated


Easiest way to resolve this and get sensibly formatted date is to use the CIM cmdlets

£> Get-CimInstance -ClassName win32_reliabilityRecords | select -First 1 | fl Message, TimeGenerated

Message       : Installation Successful: Windows successfully installed the following update: Definition Update for  Windows Defender - KB2267602 (Definition 1.207.1367.0)
TimeGenerated : 29/09/2015 18:09:14


Now you have a date you can read easily.

WMI dates

Dates as reported by WMI still seem to cause a lot of problems. If you use the WMI cmdlets

£> Get-WmiObject -Class Win32_OperatingSystem | select *date* | fl

InstallDate   : 20131205101649.000000+000
LocalDateTime : 20150728121320.002000+060


That format is year, month, day, hour, minute, second then fractions of a second after the decimal point with the final +nnn indicating an offset from Greenwich Mean Time  (UTC) for time zones and daylight saving time.


You can read the date presented by WMI but its not intuitive.


The PowerShell team added a ConvertToDateTime() to the object output by WMI so that you can easily perform date conversions

£> Get-WmiObject -Class Win32_OperatingSystem | select @{N='Install'; E={$_.ConvertToDateTime($_.Installdate)}}, @{N='Lo
calDate'; E={$_.ConvertToDateTime($_.LocalDateTime)}} | fl

Install   : 05/12/2013 10:16:49
LocalDate : 28/07/2015 12:16:26


Though my preferred solution these days is to use the CIM cmdlets as they convert the date for you without any extra effort

£> Get-CimInstance -ClassName Win32_OperatingSystem | select *date* | fl

InstallDate   : 05/12/2013 10:16:49
LocalDateTime : 28/07/2015 12:17:29

CIM filters

I was looking up Win32_SystemDriver on the MSDN site and noticed there was some PowerShell example code


Get-WmiObject -Class Win32_SystemDriver |
Where-Object -FilterScript {$_.State -eq "Running"} |
Where-Object -FilterScript {$_.StartMode -eq "Manual"} |
Format-Table -Property Name,DisplayName


A better way to write this would be:

Get-WmiObject -Class Win32_SystemDriver -Filter "State='Running' AND StartMode='Manual'" | Format-Table -Property Name, DisplayName –AutoSize




Get-CimInstance -ClassName Win32_SystemDriver -Filter "State='Running' AND StartMode='Manual'" | Format-Table -Property Name, DisplayName -AutoSize


Do the filtering in the CIM call – especially if you’re running this against a number of remote machines. That way you limit the network traffic you’re returning

WMF 5.0 April 2015 preview – – software inventory logging

A software inventory module is now included with the April 2015 WMF 5.0 preview

£> Get-Command -Module SoftwareInventoryLogging | select Name



Windows updates are always a good place to start poking into your systems

£> Get-Command Get-SilWindowsUpdate -Syntax

Get-SilWindowsUpdate [[-ID] <string[]>] [-CimSession <CimSession[]>]
[-ThrottleLimit <int>] [-AsJob] [<CommonParameters>]

£> Get-SilWindowsUpdate

ID          : KB3055381
InstallDate : 4/30/2015



The parameters for Get-SilWindowsUpdate look like those I’d expect from a CDXML module. Inspection of C:\Windows\System32\WindowsPowerShell\v1.0\modules\SoftwareInventoryLogging\

shows  a number of cdxml files



The WMF 5,0 release notes supply a link to further data of software inventory logging – interestingly its flagged as a Windows Server 2012 R2 page.

Trying the cmdlet against a Windows Server 2012 R2 system running WMF 4.0 (with the November 2014 roll up)

$cs = New-CimSession -ComputerName W12R2SUS
Get-SilWindowsUpdate -CimSession $cs

£> Get-SilWindowsUpdate -CimSession $cs

ID             : KB3006193
InstallDate    : 1/5/2015
PSComputerName : W12R2SUS



This means the class is on our Windows Server 2012 R2 box so we could use it directly

£> Get-CimInstance -Namespace root/InventoryLogging -ClassName  MsftSil_WindowsUpdate | Format-Table -a

ID                  InstallDate                                PSComputerName
--                   -----------                                 --------------
KB3006193 1/5/2015 12:00:00 AM
KB2894856 9/14/2014 12:00:00 AM



This module supplies a useful way to find out the software installed on our systems – I’ll be digging into this over a few more posts

Blocksize missing?

I recently had a question asking why the Bloacksize property on Win32_LogicalDisk is empty but is populated on Win32_Volume.

The thing is to remember the underlying thing that these 2 CIM classes represent.


A logical disk is a subdivision of an extended partition. An extended partition can hold one or more logical disks.


When you create a volume on a disk you use either a primary partition or a logical disk from an extended partition. Until you format the volume you can’t have a block size as that is determined by the parameters you use to perform the format.


The documentation for Win32_LogicalDisk states that block size is not populated for logical disks

OMI/CIM/WMI dictionary

Don Jones provides a very good summary of the similarities and differences between WMI, CIM and OMI

Recommended reading if you’re using these technologies

Query vs Filter

I’ve tended to advocate using the –Filter parameter rather than the –Query parameter with the CIM (and WMI) cmdlets but a recent post on the Windows Management Infrastructure blog has me questioning that decision.


Using Measure-Command I tried various pairs of commands – such as:


Measure-Command {Get-CimInstance -ClassName Win32_Directory -Filter "Name = 'C:\\Test2'"}


Measure-Command {Get-CimInstance -Query "SELECT * FROM Win32_Directory WHERE Name = 'C:\\Test2'"}


The results weren’t conclusive but it seems that at worst there is no significant difference between the approaches and at best using a query is significantly faster. 


At the moment my best advice would be use the –Filter parameter if you want to reduce typing but try –Query if speed becomes your main issue.