Monthly Archive


Monthly Archives: December 2015

Objects, properties and values

One thing that seems to keep causing confusion is using Select-Object to pick off one or more properties from a set of objects:

PS> Get-CimInstance -ClassName Win32_Share | select Path





The gap in the output is because the IPC$ share doesn’t have a path defined.


What you have is a ‘Selected’ version of the original object

PS> Get-CimInstance -ClassName Win32_Share | select Path | Get-Member

   TypeName: Selected.Microsoft.Management.Infrastructure.CimInstance

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
Path        NoteProperty string Path=C:\WINDOWS


Very often you’ll only want the value of the property. In which case you need to use the –ExpandProperty parameter on Select-Object

PS> Get-CimInstance -ClassName Win32_Share | select -ExpandProperty Path



Infrastructure as Code article

My Infrastructiure as Code article – part of an introducing DevOps series – has gone live

You can also link to it through where you’ll find the first in my series of article on PowerShell. Second one will be coming in the New Year

BinaryMiLog cmdlets

It’s not often I come across soemthing brand new in PowerShell but yesterday when I was investigating New-CimInstance I discovered 2 cmdlets in the CimCmdlets module I hadn’t noticed before. These are:




The cmdlets are used to export, or import, CIM instances as a binary encoded file.  Think of them as  Export-Clixml and Import-Clixml but for CIM instances.

Their usage is very simple:

Get-CimInstance -ClassName Win32_OperatingSystem |
Export-BinaryMiLog -Path testfile.bmil


This creates a   30 KB binary file – its definitely not human readable!

You don’t need to use a bmil extension (its the one in the help file) and you can use a CIM instance object instead of the pipeline

$os = Get-CimInstance -ClassName Win32_OperatingSystem
Export-BinaryMiLog -InputObject $os -Path testfile2.stuff


Getting the data back is performed by Import-BinaryMiLog

$os1 = Import-BinaryMiLog -Path .\testfile.bmil
$os2 = Import-BinaryMiLog -Path .\testfile2.stuff


The results appear to be a standard CIM object

Compare-Object -ReferenceObject $os -DifferenceObject $os1 -IncludeEqual
Compare-Object -ReferenceObject $os -DifferenceObject $os2 -IncludeEqual
Compare-Object -ReferenceObject $os1 -DifferenceObject $os2 –IncludeEqual


These cmdlets give you way to persist CIM objects to disk so that they can be referenced at a later date. If you need to test for changes to a system this could be a useful technique

New-CimInstance cmdlet and the–Key parameter

I was asked a question about the –Key parameter on New-CimInstance today. I wasn’t sure of the answer so I’ve done some experimentation.


I tend to avoid New-CimInstance if I can preferring to use the Create method on the CIM class – however not all CIM classes have a create method so need to fall back on New-CimInstance.


I started by looking at the documentation. The help file for New-CimInstance says:


Specifies the properties that are used as keys. CimSession and ComputerName cannot be used when Key is specified.


That then leads to the question how do I discover the Key or Keys of a CIM class.  You can’t use the –Qualifier parameter in Get-CimClass because that works at the class level and Key is a property qualifier.  Means you need to use some code

function Get-CimClassKey { 

    param (
        [string]$CIMnamespace = 'ROOT/cimv2',

    $class = Get-CimClass -Namespace $CIMnamespace -ClassName $CIMclass

    foreach ($property in $class.CimClassProperties) {
        $property | Select-Object -ExpandProperty Qualifiers |
        foreach {
            if ($_.Name -eq 'key'){



The Key property of a class HAS to be given a value when a new instance of the class is created.


The New-CimInstance help file shows an example using Win32_Environment.  Adapring the example:

PS> New-CimInstance -ClassName Win32_Environment @{Name='RStest1'; VariableValue='test1'; UserName='RSSURFACEPRO2\Richard'}

Name             UserName                                            VariableValue
----             --------                                            -------------
RStest1          RSSURFACEPRO2\Richard                               test1

Using our function to discover the Keys of Win32_Environment

PS> Get-CimClassKey -CIMclass Win32_Environment

Name               : Name
Value              :
CimType            : String
Flags              : Property, Key, NullValue
Qualifiers         : {read, key, MappingStrings, Override...}
ReferenceClassName :

Name               : UserName
Value              :
CimType            : String
Flags              : Property, Key, ReadOnly, NullValue
Qualifiers         : {key, MappingStrings, MaxLen, read}
ReferenceClassName :


Adding the –Key parameter

PS> New-CimInstance -ClassName Win32_Environment @{Name='RStest2'; VariableValue='test2'; UserName='RSSURFACEPRO2\Richard'} -Key 'Name', 'UserName'

Name             UserName                                            VariableValue
----             --------                                            -------------
RStest2          RSSURFACEPRO2\Richard                               test2


Using Win32_Environment you can use the Key parameter, or not, as long as you define values for the Name and Username properties.

Another example in the New-CimInstance help file uses the Win32_Process class.  The key for that class is the Handle property

PS> Get-CimClassKey -CIMclass Win32_process

Name               : Handle
Value              :
CimType            : String
Flags              : Property, Key, ReadOnly, NullValue
Qualifiers         : {key, MaxLen, read}
ReferenceClassName :


the Handle is appears to be identical to the ProcessId in value as far as I can determine


This now gets  messy:


Just the Handle.  BTW exmple 3 in the documentation has an error as Handle is a string not an integer

PS> New-CimInstance -ClassName Win32_Process -Property @{Handle='0'}
New-CimInstance : Provider is not capable of the attempted operation
At line:1 char:1
+ New-CimInstance -ClassName Win32_Process -Property @{Handle='0'}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (Win32_Process:CimInstance) [New-CimInstance], CimException
    + FullyQualifiedErrorId : HRESULT 0x80041024,Microsoft.Management.Infrastructure.CimCmdlets.NewCimInstanceCommand


Add the Key parameter

PS> New-CimInstance -ClassName Win32_Process -Property @{Handle='0'} -Key Handle
New-CimInstance : Provider is not capable of the attempted operation
At line:1 char:1
+ New-CimInstance -ClassName Win32_Process -Property @{Handle='0'} -Key ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (Win32_Process (Handle = "0"):CimInstance) [New-CimInstance], CimException
    + FullyQualifiedErrorId : HRESULT 0x80041024,Microsoft.Management.Infrastructure.CimCmdlets.NewCimInstanceCommand


The only way it works is if you use –ClientOnly to make an in memory CIM instance that only exists in your PowerShell session

PS> New-CimInstance -ClassName Win32_Process -Property @{Handle='0'} -Key Handle -ClientOnly

Handle PSComputerName
------ --------------


You can remove the –Key parameter

PS> New-CimInstance -ClassName Win32_Process -Property @{Handle='0'} -ClientOnly

Handle PSComputerName
------ --------------


Win32_Process has a Create method that takes these parameters

PS> $class.CimClassMethods['Create'].Parameters

Name                       CimType Qualifiers                                 ReferenceClassName
----                                 ------- ----------                                 ------------------
CommandLine                 String {ID, In, MappingStrings}
CurrentDirectory            String {ID, In, MappingStrings}
ProcessStartupInformation Instance {EmbeddedInstance, ID, In, MappingStrings}
ProcessId                   UInt32 {ID, MappingStrings, Out}


Using Invoke-CimMethod

PS> Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine='notepad.exe'}

ProcessId ReturnValue PSComputerName
--------- ----------- --------------
     2648           0


Now trying New-CimInstance

PS> New-CimInstance -ClassName Win32_Process -Property @{Handle='0'; CommandLine='notepad.exe'} -Key Handle
New-CimInstance : Provider is not capable of the attempted operation
At line:1 char:1
+ New-CimInstance -ClassName Win32_Process -Property @{Handle='0'; Comm ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (Win32_Process (Handle = "0"):CimInstance) [New-CimInstance], CimException
    + FullyQualifiedErrorId : HRESULT 0x80041024,Microsoft.Management.Infrastructure.CimCmdlets.NewCimInstanceCommand


Other variants of not including the Handle property and changing the handle value all fail with same error


Botton line is that New-CimInstance is a bit of a mess to use – with or without the –Key parameter (which doesn’t seem to do much).


If the CIM class hasa create method Id recommend that you use that as a lot of CIm classes (or their providers) don’t work with New-cimInstance. In reality given that many of the CIM classes are effectively read only – you can’t create a new instance of Win32_ComputerSystem for example – it probably doesn’t matter.

JEA Helper Tool 2.0

JEA – Just Enough Admin – is a security feature in WMF 5.0 and Windows Server 2016 (TP4) – providing RBAC for your Windows servers. You can allow people to perform the tasks needed by their role without giving them full access. 

An introductory document is available -

Doing all this manually can be a bit overwhelming. The JEA helper Tool can ease this work. Version 2.0 is know available for download – details from

Scripting Guy gets Pestered

The Scripting Guy blog has been running a series on Pester written by Dave Wyatt – the modules author.


Pester provides a way to perform, and automate, testing on your PowerShell code.


The series is:

New ScriptAnalyzer

A new version of Script Analyzer is available for download -

A number of the rules have been updated and some new ones added

WMF 5.0 RTM for Windows 8.1 and Windows 7

The PowerShell Team blog

has been updated to show WMF is now available for

Windows 8.1

Windows 7 SP1


On the download center - -

You’ll see x86 versions of WMF 5.0 for Windows 8.1 and Windows 7


For the x64 versions you need to use the appropriate server version. For Windows 8.1 x64 use the Windows 2012 R2 download and for Windows 7 use the Windows 2008 R2 download.


Notice there isn’t any support for Windows 8. You’ll have to use the free upgrade to Windows 8.1


Windows 10 as of the November update is effectively at WMF 5.0 RTM version

Inputting computer names

Somehting I was writing yesterday started me thinking about the way you input a list of computer names to a cmdlet. Many cmdlets have a ComputerName parameter so knowing how to deal with this sort of input will help. There are a range of techniques.


One of the simplest approaches is to create your list as a variable and use the variable:

$servers = 'SERVER02', 'W12R2SCDC01', 'W12R2SUS', 'W12R2DSC', 'W12R2TGT', 'W12R2WEB01', 'W12R2WEB02', 'W12R2OD01'
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $servers


If you don’t need the server list as a variable then input it directly to the cmdlet

Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName 'SERVER02', 'W12R2SCDC01', 'W12R2SUS', 'W12R2DSC', 'W12R2TGT', 'W12R2WEB01', 'W12R2WEB02', 'W12R2OD01'


The drawback is that the list of machine names is embedded in your code. The variable approach above is easier to maintain


A common approach is to use Foreach-Object

$servers = 'SERVER02', 'W12R2SCDC01', 'W12R2SUS', 'W12R2DSC', 'W12R2TGT', 'W12R2WEB01', 'W12R2WEB02', 'W12R2OD01'
$servers | foreach {Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $psitem}


This works but adds unnecessary code. If you are performing other tasks in the foreach processing it may be worthwhile

Likewise using a foreach loop

$servers = 'SERVER02', 'W12R2SCDC01', 'W12R2SUS', 'W12R2DSC', 'W12R2TGT', 'W12R2WEB01', 'W12R2WEB02', 'W12R2OD01'
foreach ($server in $servers) {
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $server


The foreach loop will be quicker but use more memory.

If you create a CSV file make sure you use ComputerName as the field header – then you can do this

Import-Csv .\computers.csv |
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3'

if the header is something else – eg computer – then you need to use foreach-object

Import-Csv .\computers.csv |
foreach {
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $_.Computer


Import-Csv .\computers.csv |
foreach {
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $psitem.Computer


If you have the names in a text file you may be tempted to do this

Get-Content .\computers.txt |
foreach {
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $_


You can also use $psitem instead of $_


A neater way is to do this

Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName (Get-Content .\computers.txt)


which gets us back to the PowerShell one liner solution.


If you’re reading the computernames from Active Directory you have to do a bit of work because the AD cmdlets don’t return a computerName property – they return name.


You can either

Get-ADComputer -Filter * |
foreach {
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $psitem.Name


or if you want to be a bit cleverer

Get-ADComputer -Filter * |
select @{N='ComputerName'; E = {$_.Name}} |
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3'


Use select-object to create a computername property on the pipeline object.


You can of course revert to the one liner solution

Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName (Get-ADComputer -Filter * | select -ExpandProperty name)


Use ExpandProperty on select-object to strip out the Name value and pass that to your cmdlet.


As you can see there are many ways to achieve the same goal – some easier than others. These examples aren’t necessarily complete.

Next time you need to pass a list of values to a cmdlet stop and think about the best way to do it. You might save some typing and more efficient code.

WMF 5.0 now RTM

WMF 5.0 , including PowerShell 5.0, was released to RTM overnight – why does this lways happen when I’m asleep Smile


Versions are available for:

Windows Server 2012 R2

Windows Server 2012

Windows Server 2008 R2 SP1