Monthly Archive


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

WMI Associations


I saw a question regarding finding the Win32_NetworkAdapter instance using the matching Win32_NetworkAdapterConfiguration starting point.  This answers the “which adapter has an IP address of X” type question.


The Index property on a Win32_NetworkAdapterConfiguration instance has the same value as the DeviceId property on the corresponding Win32_NetworkAdapter.


An alternative is to use the ASSOCIATORS WQL keyword.


That approach get s a bit messy but looks like this:


$query = "ASSOCIATORS OF {Win32_NetworkAdapterConfiguration.Index='18'} WHERE RESULTCLASS = Win32_NetworkAdapter"
Get-WmiObject -Query $query


The CIM cmdlets get a bit better


$config = Get-CimInstance win32_networkadapterconfiguration -Filter "Index = 18"
Get-CimAssociatedInstance -InputObject $config -ResultClassName Win32_NetworkAdapter


Much simpler and you avoid the WQL

WMI and CIM dates

A question on the forum asked about extracting the year from the ReleaseDate property returned by Win32_BIOS


They were trying to do this

Get-CimInstance Win32_BIOS | Select-Object @{n="ReleaseDate";e={$_.ConvertToDateTime($_.ReleaseDate).year()}}


There are 2 problems with this approach – firstly the objects that Get-CimInstance produces don’t have the ConvertToDateTime method (its added by PowerShell to the objects produces by Get-WmiObject) and secondly on a DateTime object Year is a property not a method.


If you use the WMI cmdlet you see this

£> Get-WmiObject -Class Win32_Bios | select Releasedate



The date is in WMI format and needs to be converted.

£> Get-WmiObject Win32_BIOS | Select-Object @{n="ReleaseDate";e={$_.ConvertToDateTime($_.ReleaseDate)}}

12/05/2014 01:00:00


If you want just the year

£> Get-WmiObject Win32_BIOS | Select-Object @{n="ReleaseDate";e={($_.ConvertToDateTime($_.ReleaseDate)).Year}}



This conversion is already done for you with the CIM cmdlets

£> Get-CimInstance -CimSession $c -ClassName Win32_Bios | select ReleaseDate

12/05/2014 01:00:00


Again if you just want the year

£> ((Get-CimInstance -CimSession $c -ClassName Win32_Bios).ReleaseDate).Year


If you use Get-CimInstance and examine the object returnd – you’ll find some meta-data at the end of the object:

CimClass                                  : root/cimv2:Win32_OperatingSystem
CimInstanceProperties                     : {Caption, Description, InstallDate, Name...}
CimSystemProperties                       : Microsoft.Management.Infrastructure.CimSystemProperties


Expand CimInstanceProperties


£> Get-CimInstance Win32_OperatingSystem | select -ExpandProperty CimInstanceProperties | fl *


And you’ll see an entry like this for each property

Name            : Caption
Value           : Microsoft Windows 8.1 Pro
CimType         : String
Flags           : Property, ReadOnly, NotModified
IsValueModified : False


The CimType and Flags attributes are especially useful


For instance you can use them to discover which properties can be modified:


Get-CimInstance Win32_OperatingSystem |
select -ExpandProperty CimInstanceProperties |
foreach {
$flags =   ($psitem | select -ExpandProperty Flags)  -split ", "  

if ($flags -notcontains 'ReadOnly'){$psitem}


Remember that other properties may be modifiable via a method on the class.


You can find similar information via Get-CimClass


$class = Get-CimClass -ClassName Win32_OperatingSystem

$class.CimClassProperties |
foreach {
$flags =   ($psitem | select -ExpandProperty Flags)  -split ", "  

if ($flags -notcontains 'ReadOnly'){$psitem}


Get-CimClass gives you the qualifiers as well as the flags. Get-CimInstance gives you the value of the property if set.

Two options with slightly different results. All the better to investigate CIM classes

WMI troubleshooting

A WMI troubleshooting series has been started on the Ask the Performance Team Blog.

The overview article is:


The first article is about common symptoms and errors:


followed by an article on dealing with WMI repository corruption:


Very useful information in both articles. If the rest of the series is at this level you’ll want to book mark these posts

ServerManagerTasks module – – Get-SMServerFeature

Finding the Windows features installed on a remote machine can be an interesting task. You could use Get-WindowsFeature but that gives you a “graphical” display:

£> Get-WindowsFeature | where DisplayName -like '*DNS*'

Display Name         Name             Install State
------------         ----             -------------
[X] DNS Server       DNS              Installed
[X] DNS Server Tools RSAT-DNS-Server  Installed

Or you could use Get-SMServerFeature from the ServerManagerTasks module:

£> Get-SMServerFeature -BatchSize 1000  | where DisplayName -like '*DNS*' | sort Displayname | ft -a Displayname, State, Type, ConfigurationStatus

Displayname      State Type ConfigurationStatus
-----------      ----- ---- -------------------
DNS Server           1    0                   3
DNS Server Tools     1    2                   3

Which is CIM based and uses a new class: root/microsoft/windows/servermanager/MSFT_ServerFeature

Following the tradition firmly established since the introduction of WMI the data from this class is represented by integer values AND just for giggles its not documented. To be fair most of the WMI classes aren’t documented.


We need documentation for these classes


In an effort to work out which are the important values I compared the outputs from

Get-SMServerFeature -BatchSize 1000  | where DisplayName -like '*PowerShell*' | sort Displayname | ft -a Displayname, State, Type, ConfigurationStatus


Get-WindowsFeature | where DisplayName -like '*PowerShell*' | sort Displayname

The important property from  Get-SMServerFeature  seems to be State which takes a 0 or 1. O corresponds to Available in Get-WindowsFeature and 1 corresponds to Installed.

You can use the trick I show in PowerShell and WMI (

$state = DATA {ConvertFrom-StringData -StringData @'
0 = Available
1 = Installed

Get-SMServerFeature -BatchSize 1000  |
where DisplayName -like '*PowerShell*' |
sort Displayname |
select DisplayName, UniqueName,
@{N='State'; E={$state["$($_.State)"]}

$state = DATA {ConvertFrom-StringData -StringData @'
0 = Available
1 = Installed

Create a hash table for the integer values and their meaning and use that hash table in a calculated field to get the meaning from the integer value.

The output will look like this:

DisplayName : Windows PowerShell Web Access
UniqueName  : WindowsPowerShellWebAccess
State       : Available

Its now easy to compare the data between different machines.  Get-SMServerFeature has a CimSession parameter for working with remote machines