Monthly Archive


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.

Copy a file with WMI

A question came up on the forum about copying files with CIM (WMI). I normally use Copy-Item rather than CIM as its easier. The questioner was using CIM_LogicalFile when I’ve normally used CIM_DataFile so I decided to take a look at the class. In reality the two classes are very similar and CIM-datafile could be substituted for CIM_LogicalFile in the code that follows.


The obvious starting point is to use the Copy method on the CIM_LogicalFile class


$files = Get-WmiObject -Class CIM_LogicalFile -Filter "Path = '\\Test\\' AND Extension = 'txt'"

foreach ($file in $files) {
$newfile = "C:\Test2\$($file.FileName).$($file.Extension)"



Couple of points to note. In the Path part of the filter you have to escape the \ delimiter.  Extension doesn’t include the ‘.’

You have to give the full path – including file name - to the loaction to which you want to copy the file. In this case you don’t have to escape the \ delimiter. Consistency is a wonderful thing and usually absent from WMI.


You can also use Invoke-WmiMethod


$files = Get-WmiObject -Class CIM_LogicalFile -Filter "Path = '\\Test\\' AND Extension = 'txt'"

foreach ($file in $files) {
$newfile = "C:\Test2\$($file.FileName).$($file.Extension)"
Invoke-WmiMethod -InputObject $file  -Name Copy -ArgumentList $newfile




use the new CIM cmdlets


$files = Get-CimInstance -ClassName CIM_LogicalFile -Filter "Path = '\\Test\\' AND Extension = 'txt'"

foreach ($file in $files) {
$newfile = "C:\Test2\$($file.FileName).$($file.Extension)"
Invoke-CimMethod -InputObject $file  -MethodName Copy -Arguments @{Filename = $newfile}



In this case you have to give the argument name for the method as well as its value. You can discover the method parameters using Get-CimClass


$class = Get-CimClass CIM_LogicalFile

£> $class.CimClassMethods["Copy"].Parameters

Scripting Guy CDXML series finished

My CDXML series on the Scripting Guy blog finished today.  The 4 articles are:

Scripting Guy CDXML series

Today starts a four part series I’ve written for the Scripting Guy blog on using CDXML to create a module to work with the registry.  Don’t know what CDXML is – you will when you’ve read the series


The first post is at

Testing for a hotfix

KB3000850 – the November roll up for Windows 2012 R2 contains some very useful updates.

I’ve installed it on some machines in my lab but not all. The update is huge so I’m installing it manually rather than through WSUS.


I need to test a remote machine to determine if the update  is installed.

If it is installed you get a this back

£> Get-HotFix -Id KB3000850 -ComputerName w12r2dsc

Source        Description             HotFixID         InstalledBy      
------          -----------                --------          -----------      
W12R2DSC      Update           KB3000850      MANTICORE\Richard


But if its not installed you get this

£> Get-HotFix -Id KB3000850 -ComputerName w12r2od01
Get-HotFix : Cannot find the requested hotfix on the 'w12r2od01' computer. Verify the input and run the command again.
At line:1 char:1
+ Get-HotFix -Id KB3000850 -ComputerName w12r2od01
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (:) [Get-HotFix], ArgumentException
    + FullyQualifiedErrorId : GetHotFixNoEntriesFound,Microsoft.PowerShell.Commands.GetHotFixCommand

Get-Hotfix actually uses the Win32_QuickFixEngineering CIM class so you need to have DCOM open on the remote system otherwise you get a

Get-HotFix : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)



You need to wrap the call to Get-Hotfix in a try catch. You only need to know if the update is installed so creating a specialised output object manages that for you

Get-VM |
where State -eq 'Running' |
foreach {
$props = @{
   Machine = $($psitem.Name)
   Present = $false
try {
   $hf = Get-HotFix -Id KB3000850 -ComputerName $psitem.Name -ErrorAction Stop
   $props.Present = $true
catch {
   $props.Present = $false
New-Object -TypeName PSObject -Property $props


Substitute any other method of getting a list of computer names, for my call to Get-VM, to match your environment.

WMI — identifying writable properties

One common mistake I see is people trying to set the value of a read only property on a WMI class.  There isn’t a quick way to see if a property is writable. Get-CimClass can be used but you have to dig into the Qualifiers for each property.


You can use this function to determine the read\write settings on all of the properties of a WMI class

function get-cimreadwriteproperties {
param (

$props = @()

$class = Get-CimClass -ClassName $classname
$class.CimClassProperties |
foreach {
  $prop = [ordered]@{
    Name = $psitem.Name
    Read = $false
    Write = $false
  $psitem |
  select -ExpandProperty Qualifiers |
  foreach {
    if ($_.Name.ToLower() -eq 'read') {
      $prop.Read = $true
    if ($_.Name.ToLower() -eq 'write') {
      $prop.Write = $true

  $props += New-Object -TypeName PSObject -Property $prop




Take the class name as a parameter and use Get-CimClass. Iterate through the properties and foreach create an output object. Test each qualifier to determine if read or write and set out to true. Add to array and output.


The output looks like this


£> get-cimreadwriteproperties -classname Win32_bios | ft -AutoSize

Name                  Read Write
----                  ---- -----
Caption               True False
Description           True False
InstallDate           True False
Name                  True False
Status                True False
BuildNumber           True False




£> get-cimreadwriteproperties -classname Win32_LogicalDisk | ft -AutoSize

Name                          Read Write
----                          ---- -----
Caption                       True False
Description                   True False
InstallDate                   True False

ErrorMethodology              True False
NumberOfBlocks               False False
Purpose                       True False
VolumeDirty                   True False
VolumeName                    True  True
VolumeSerialNumber            True False