Monthly Archive




I stumbled on the Get-SupportedFileSystems cmdlet today. Its part of the Storage module and is defined as a function. Digging a bit further its from a CDXML module based on a CIM class. But which CDXML file contains the definition?

PS> Get-ChildItem -Path 'C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Storage' -File | Select-String -Pattern 'Get-SupportedFileSystems' -SimpleMatch

C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Storage\Storage.psd1:117:        'Get-SupportedFileSystems',
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Storage\Volume.cdxml:405:      // Get-SupportedFileSystems

Looking in Volume.cdxml shows we’re working with the ROOT/Microsoft/Windows/Storage/MSFT_Volume class. You can use this directly

PS> Get-CimInstance -Namespace ROOT/Microsoft/Windows/Storage -ClassName MSFT_Volume | select DriveLetter, FileSystem

DriveLetter FileSystem
----------- ----------

When you use Get-SupportedFileSystems all you get back is the filesystem

PS> Get-SupportedFileSystems -DriveLetter C

The DriveLetter parameter can take an array of chars but if you supply a driveletter where there isn’t a defined filesystem you get an error

PS> Get-SupportedFileSystems -DriveLetter D
Get-SupportedFileSystems : Failed
Activity ID: {25bde807-4d9f-4216-8640-94268ff80624}
At line:1 char:1
+ Get-SupportedFileSystems -DriveLetter D
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (StorageWMI:ROOT/Microsoft/...age/MSFT_Volume) [Get-SupportedFileSystems],
+ FullyQualifiedErrorId : StorageWMI 4,Get-SupportedFileSystems

or if the drive isn’t defined

PS> Get-SupportedFileSystems -DriveLetter E
Get-SupportedFileSystems : No MSFT_Volume objects found with property 'DriveLetter' equal to 'E'.  Verify the value of the property and retry.
At line:1 char:1
+ Get-SupportedFileSystems -DriveLetter E
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (E:Char) [Get-SupportedFileSystems], CimJobException
+ FullyQualifiedErrorId : CmdletizationQuery_NotFound_DriveLetter,Get-SupportedFileSystems

The DriveLetter parameter accepts pipeline input by propertyname BUT it has to be a Char not a string.

What would be useful would be to get the drives using Get-PSDrive and pass to Get-SupportedFileSystems

PS> Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize

Name Used (GB) Free (GB) Provider   Root CurrentLocation
---- --------- --------- --------   ---- ---------------
C       202.61    273.55 FileSystem C:\          Scripts
D                        FileSystem D:\

Get-PSDrive outputs the driveletter as the name property and just make life fun its a string.

Time for some PowerShell magic.

This looks good

Get-PSDrive -PSProvider FileSystem |
select @{N='DriveLetter'; E={[char]$_.Name}} |

but it fails because D doesn’t have a filesystem defined.

So try this

Get-PSDrive -PSProvider FileSystem |
foreach {
$props = [ordered]@{
DriveLetter = $_.Name
FileSystem = Get-SupportedFileSystems -DriveLetter $_.Name -ErrorAction SilentlyContinue
New-Object -TypeName PSObject -Property $props

DriveLetter FileSystem
----------- ----------
C           NTFS

A simple way to check the filesystem used on your Windows machines

Filter early and WQL

What’s wrong with this:

Get-CimInstance -ClassName Win32_Service |
where {$_.State -eq 'Running' -and $_.StartName -notlike 'LocalSystem' -and $_.StartName -notlike 'NT Authority*'} |
select PSComputerName, Name, DisplayName, State, StartName


Nothing except that its inefficient. if you ran this against a remote machine the filtering would happen on the local machine AFTER you’d dragged everything across the network. May not matter for a few machines but when you get to hundreds or thousands of machines it will have an impact


You need to use a filter. First try would be something like this:

Get-CimInstance -ClassName Win32_Service  -Filter "State = 'Running' AND StartName != 'LocalSystem' AND NOT StartName LIKE 'NT Authority%'"|
select PSComputerName, Name, DisplayName, State, StartName


Unfortunately any services with a NULL StartName will also be filtered out


This will work

Get-CimInstance -ClassName Win32_Service  -Filter "State = 'Running' AND Startname != 'LocalSystem' AND StartName != 'NT AUTHORITY\\LocalService' AND StartName != 'NT AUTHORITY\\NetworkService'"|
select PSComputerName, Name, DisplayName, State, StartName


Same results are obtained with Get-WmiObject

Open source PowerShell and OMI

OMI – the Open Source CIM server is available on github

This appears to be a later version than currently shown on the open group web site


Combine this with open source PowerShell


and the DSC on Linux


And you have your basis for managing Linux machines

IIS information

In my recent post about getting server information from a IIS web server I said I post about getting similar information from later machines.


You still have the root\MirosoftIISv2 namespace available if you install the IIS 6.0 tools but one question to keep in mind – how long will they continue to be available?


Your alternative is the root\webadministration names space. You can use the Site class to get the relevant information


$serverdata = @()
Get-CimInstance -Namespace root\webadministration -ClassName Site -ComputerName $env:COMPUTERNAME |
foreach {

$serverdata += New-Object -TypeName PSObject -Property @{
Port = [string]::Join(',', ($_.Bindings | select -ExpandProperty BindingInformation))
SiteName = $_.Name
SiteId = $
PSComputerName = $_.PSComputerName
Status = Invoke-CimMethod -InputObject $_ -MethodName GetState | select -ExpandProperty ReturnValue



Remember that COM objects are inert so you can’t call the method directly on the object. otherwise the info is about the same

CDXML filter parameters

I was recently asked about adding a filter parameter to a cmdlet created through CDXML. If you’ve not seen it before (see PowerShell and WMI Chapters 18 & 19 from CDXML allows you to creat ecmdlets by wrapping a WMI class in some simple XML.


The resultaing CDXML (Cmdlet Definition XML) is thn published as a module.  Here’s a simple example using the Win32_NetworkAdapterConfiguration class

<?xml version='1.0' encoding='utf-8'?>
<PowerShellMetadata xmlns=''>
  <Class ClassName='ROOT\cimv2\Win32_NetworkAdapterConfiguration'>
      <GetCmdletParameters DefaultCmdletParameterSet='DefaultSet'>


The first 2 lines are boilerplate. The NameSpace and WMI class are defined on line 3, follwoed by a version number (arbitary) and a default noun for you cmdlet to use.  Instance cmdlets defines how you’ll pull the data for existing instances of the class – in other words the Get-NetworkAdapterConfiguration cmdlet.


Save as a CDXML file and import as a module

Import-Module .\NetworkAdapterConfiguration.cdxml


Get-Module will sjow it as a Cim module with a single exported command.   Use it like any other cmdlet

PS> Get-NetworkAdapterConfiguration | ft -a

ServiceName  DHCPEnabled Index Description
-----------  ----------- ----- -----------
kdnic        True        0     Microsoft Kernel Debug Network Adapter
mwlu97w8     True        1     Marvell AVASTAR Wireless Composite Device
msu64w8      False       2     Surface Ethernet Adapter
mwlu97w8     True        3     Marvell AVASTAR 350N Wireless Network Controller
RFCOMM       False       4     Bluetooth Device (RFCOMM Protocol TDI)
BthPan       True        5     Bluetooth Device (Personal Area Network)
vwifimp      True        6     Microsoft Wi-Fi Direct Virtual Adapter
vwifimp      True        7     Microsoft Wi-Fi Direct Virtual Adapter
RasSstp      False       8     WAN Miniport (SSTP)
RasAgileVpn  False       9     WAN Miniport (IKEv2)
Rasl2tp      False       10    WAN Miniport (L2TP)
PptpMiniport False       11    WAN Miniport (PPTP)
RasPppoe     False       12    WAN Miniport (PPPOE)
NdisWan      False       13    WAN Miniport (IP)
NdisWan      False       14    WAN Miniport (IPv6)
NdisWan      False       15    WAN Miniport (Network Monitor)


Using the cmdlet is equivalent to

Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration


but is easier and requires less typing.

Very often you’ll want to pick a specific adapter – for instance


Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter 'Index=3'


You can implement the same kind of filters using CDXML. You add a queryable properties section as shown below:

<?xml version='1.0' encoding='utf-8'?>
<PowerShellMetadata xmlns=''>
  <Class ClassName='ROOT\cimv2\Win32_NetworkAdapterConfiguration'>
      <GetCmdletParameters DefaultCmdletParameterSet='DefaultSet'>

          <Property PropertyName='Index'>
            <Type PSType ='UInt32'/>
            <RegularQuery AllowGlobbing='true'>
              <CmdletParameterMetadata PSName='Index'  ValueFromPipelineByPropertyName='true' CmdletParameterSets='DefaultSet' />


Set the paraemter name – same as property to use here – and the type (unsigned integer). Decide whether pipeline input and wildcards (globbing) are allowed and save the file.


Re-import the module (use the Force) and your new parameter is available

Get-NetworkAdapterConfiguration -Index 3


Its important to understand CDXMLMI – even if you never create a CDXML module – because 2/3 of the cmdlets in Windows Server 2012 and later are created this way.

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.

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"