Monthly Archive

CIM

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)"
 
$file.Copy($newfile)

}

 

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

}

 

OR

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:

 

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/02/registry-cmdlets-manage-the-registry.aspx

 

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/03/registry-cmdlets-first-steps-with-cdxml.aspx

 

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/04/registry-cmdlets-advanced-cdxml.aspx

 

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/05/registry-cmdlets-using-advanced-cdxml.aspx

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 http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/02/registry-cmdlets-manage-the-registry.aspx

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)

error.

 

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 {
[CmdletBinding()]
param (
[string]$classname
)

$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
}

$props

}

 

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

etc

 

 

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

Name                          Read Write
----                          ---- -----
Caption                       True False
Description                   True False
InstallDate                   True False
<truncated>

ErrorMethodology              True False
NumberOfBlocks               False False
Purpose                       True False
<truncated>
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

Releasedate
-----------
20140512000000.000000+000

 

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

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

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}}

ReleaseDate
-----------
       2014

 

This conversion is already done for you with the CIM cmdlets

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

ReleaseDate
-----------
12/05/2014 01:00:00

 

Again if you just want the year

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

CimInstanceProperties

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:

http://blogs.technet.com/b/askperf/archive/2014/08/07/troubleshooting-wmi-series-coming.aspx

 

The first article is about common symptoms and errors:

http://blogs.technet.com/b/askperf/archive/2014/08/08/wmi-common-symptoms-and-errors.aspx

 

followed by an article on dealing with WMI repository corruption:

http://blogs.technet.com/b/askperf/archive/2014/08/08/wmi-repository-corruption-or-not.aspx

 

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