header image

Archive for PowerShell and WMI

File searches with WMI

Posted by: | August 19, 2018 Comments Off on File searches with WMI |

I saw a question about file searches with WMI.

If you just know the file name it’s a very slow process. Painfully slow.

If you have an idea about the folder its much quicker.


function get-wmifile {
param (
[Parameter(Mandatory = $true)]



if ($path.IndexOf('\\') -le 0 ){
$path = $path.replace('\', '\\')

if ($path.IndexOf('*') -ge 0 ){
$path = $path.replace('*', '%')

Write-Verbose -Message "Path to search: $path"

$folders = Get-CimInstance -ClassName Win32_Directory -Filter "Name LIKE '$path'" 
foreach ($folder in $folders){

if ($file) {
Get-CimAssociatedInstance -InputObject $folder -ResultClassName CIM_DataFile |
where Name -Like "*$file" |
Select Name
else {
Get-CimAssociatedInstance -InputObject $folder -ResultClassName CIM_DataFile |
Select Name



Take the path and replace any single \ with \\. WMI expects c:\\test NOT c:\test.

Likewise replace the wildcard * with the WMI wildcard equivalent %

Get the folders that are like the path. Foreach folder use the WMI association between Win32_Directory and CIM_datafile to get the files. if you’ve specified a file then just return that otherwise all files in the folders matching path.


The WMI approach works and if you add in the computer name you can use remoting to get the answer from a remote machine.


As a comparison using Get-ChildItem would be

PS> Get-ChildItem -Path c:\test* -Recurse -Filter ‘p1.txt’ | select Fullname

which is much quicker.


Using WMI to do this is a fun exercise but the fastest approach is to use Get-ChildItem

under: PowerShell and CIM, PowerShell and WMI

user logon time

Posted by: | August 15, 2018 Comments Off on user logon time |

Saw an interesting question about user logon time. How can you tell the logged on user and when they logged on


$logon = Get-CimInstance -ClassName Win32_LogonSession |
sort StartTime -Descending |
select -First 1

$user = Get-CimAssociatedInstance -InputObject $logon -ResultClassName Win32_Account

$props = [ordered]@{
Name = $user.Fullname
UserId = $user.Name
Domain = $user.Domain
LocalAccount = $user.LocalAccount
LogonTime = $logon.StartTime

New-Object -TypeName PSobject -Property $props


Start by getting the last logon session using Win32_LogonSession. Use that to find the associated account using Get-CimAssociatedInstance and the Win32_Account class.


Pull together the output you want and create an object to display.


This deliberately only takes the latest logon and deals with that

under: PowerShell and CIM, PowerShell and WMI

WMI and CIM accelerators

Posted by: | May 26, 2018 Comments Off on WMI and CIM accelerators |

In PowerShell an accelerator is a shortcut to a .NET type. The WMI accelerators have been around since PowerShell v1. The WMI accelerators were heavily used in v1 fill some of the gaps in cmdlet coverage. The CIM accelerators appeared in PowerShell v3 (I think – only discovered them recently!). This is how you use the WMI and CIM accelerators.


There are three WMI accelerators

wmiclass is shortcut for System.Management.ManagementClass
wmi is shortcut for System.Management.ManagementObject
wmisearcher is shortcut for System.Management.ManagementObjectSearcher


And four CIM accelerators are

ciminstance is shortcut for Microsoft.Management.Infrastructure.CimInstance
cimclass is shortcut for Microsoft.Management.Infrastructure.CimClass
cimtype is shortcut for Microsoft.Management.Infrastructure.CimType
cimconverter is shortcut for Microsoft.Management.Infrastructure.CimConverter


CimSession which is a shortcut for Microsoft.Management.Infrastructure.CimSession. Use this to set a parameter type.


Notice that there isn’t a direct correspondence between the WMI and CIM accelerators.

PowerShell v6 only has the CIM accelerators


The WMI accelerators are used like this:


This can be used for creating new instances of CIM classes

PS> $p = [wmiclass]’Win32_Process’
PS> $p.Create(“notepad.exe”)

This is easily replicated using the CIM cmdlets

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



The [wmi] accelerator is used to find an instance BUT you have to use the class key!

PS> [wmi]”root\cimv2:Win32_Process.Handle=’7264′”

NOTE the handle has to be the one reported by CIM NOT the one reported by Get-Process!

Its much easier to use Get-CimInstance and filter on the name

Get-CimInstance -ClassName Win32_Process -Filter “Name=’pwsh.exe'”



This is used to find CIM instances:

PS> $query = [wmisearcher]”SELECT * FROM Win32_Process WHERE Name=’pwsh.exe'”
PS> $query.Get()

Its easier to use Get-CIMinstance these days

PS> Get-CimInstance -ClassName Win32_Process -Filter “Name=’pwsh.exe'”


Looking at the CIM accelerators


This one doesn’t seem to be usable for anything but a type decorator on a parameter.



Using Get-CimClass

PS> Get-CimClass -ClassName Win32_process | fl

CimSuperClassName : CIM_Process
CimSuperClass : ROOT/cimv2:CIM_Process
CimClassProperties : {Caption, Description, InstallDate, Name…}
CimClassQualifiers : {Locale, UUID, CreateBy, DeleteBy…}
CimClassMethods : {Create, Terminate, GetOwner, GetOwnerSid…}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
CimClassName : Win32_Process

The output is of type Microsoft.Management.Infrastructure.CimClass for which cimclass is an accelerator BUT there doesn’t seem to be a way to use the accelerator to access a class. I think this one is only usable as a type on a parameter for a function where you want to pass in a CIM class object.



Microsoft.Management.Infrastructure.CimType is an enum that contains the CIM (and WMI) datatypes:

PS> [cimtype]::Boolean
PS> [cimtype]::UInt32

The full set of CIM data types is

PS> [enum]::GetNames([cimtype])



Some of the CIM data types shown above don’t directly correspond to .NET types you’re used to from PowerShell. You can use [cimconvertor] which is shortcut for Microsoft.Management.Infrastructure.CimConverter to discover the corresponding .NET or CIM data type


PS> [cimconverter]::GetCimType([int32])
PS> [cimconverter]::GetCimType([double])


PS> [cimconverter]::GetDotNetType([cimtype]::SInt32)

IsPublic IsSerial Name BaseType
——– ——– —- ——–
True True Int32 System.ValueType

PS> [cimconverter]::GetDotNetType([cimtype]::Instance)

IsPublic IsSerial Name BaseType
——– ——– —- ——–
True True CimInstance System.Object


For the most part I think the WMI and CIM accelerators are best ignored. Use the CIM cmdlets instead. The cimtype and cimconverter accelerators are useful when developing code to check on types between CIM and .NET

under: PowerShell and CIM, PowerShell and WMI

Examples of replacing WMI cmdlet with CIM cmdlet

Posted by: | September 27, 2017 Comments Off on Examples of replacing WMI cmdlet with CIM cmdlet |

Following my last post I was asked about these Examples of replacing WMI cmdlet with CIM cmdlet.

Example 1

gwmi win32_operatingsystem -computername $Computer -credential $creds,

$cs = New-CimSession -Credential $creds -ComputerName $computer
Get-CimInstance -ClassName Win32_operatingsystem -CimSession $cs

Example 2

get-wmiobject -query “SELECT * FROM Meta_Class WHERE __Class = ‘Win32_Process’” -namespace “root\cimv2” -computername $computer -credential $creds

$cs = New-CimSession -Credential $creds -ComputerName $computer
Get-CimInstance -Query “SELECT * FROM Meta_Class WHERE __Class = ‘Win32_Process’” -CimSession $cs

Example 3

Get-WmiObject -Namespace $namespace -Class SMS_fullcollectionmembership -ComputerName $SCCMServer -filter “Name = ‘$computer’” -credential $creds

$cs = New-CimSession -Credential $creds -ComputerName $SCCMServer
Get-CimInstance -Namespace $namespace -ClassName SMS_fullcollectionmembership ” -filter “Name = ‘$computer’” -CimSession $cs

under: PowerShell and CIM, PowerShell and WMI


Posted by: | September 26, 2017 Comments Off on CIM not WMI |

I still see a lot of people using the WMI cmdlets – Get-WmiObject etc. You really should be using CIM nit WMI. In other words use Get-CimInstance rather than get-WmiObject etc etc.

Why do I say that?

Two main reasons.

Firstly, the WMI cmdlets are effectively deprecated. Any further development effort will be for the CIM cmdlets.

Secondly, and to my mind more important, is that the CIM cmdlets use WS-MAN for access to remote machines. If you have PowerShell remoting enabled you have access to the machine via a CIM session – either ephemeral using the cmdlet name or persistent using a CIM session.

The WMI cmdlets use DCOM for remoting which is blocked by default on the Windows firewall and most other firewalls and routers giving the RPC server is unavailable error.

The only time there is justification for using the WMI cmdlets is if you’re on a machine that has Powershell v2 installed and if that’s the case why haven’t you upgraded? If you can’t does that mean you’re running an application (usually Exchange or System Center) that doesn’t allow you to upgrade PowerShell.

Maybe  its time to perform that upgrade.

As another thought PowerShell v6 includes the CIM cmdlets but not the WMI cmdlets!

under: PowerShell and CIM, PowerShell and WMI

Linking disks, partitions and logical drives

Posted by: | July 11, 2017 Comments Off on Linking disks, partitions and logical drives |

A question of the forums was asking about discovering disk information. They were trying to pipe the output of Get-WmiObject into another Get-WmiObject. that won’t work. There is another way. On Windows machines physical drives are divided into 1 or more partitions which are each divided into one or more logical disks. Linking disks, partitions and logical drives is a relatively simple process.

You can start at the physical disk and work down to the logical disks or start at the logical disk and work back to the physical disk. Lets start with the logical disk.

$diskinfo = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3" | 
foreach { 
  $props = $null 
  $part = Get-CimAssociatedInstance -InputObject $psitem -ResultClass Win32_DiskPartition 
  $disk = Get-CimAssociatedInstance -InputObject $part -ResultClassName Win32_DiskDrive 
  $props = [ordered]@{ 
     Disk = $disk.Index 
     Model = $disk.Model 
     Firmware = $disk.FirmwareRevision 
     SerialNUmber = $disk.SerialNumber 
     'DiskSize(GB)' = [math]::Round(($disk.Size / 1GB ), 2) 
     Partitions = $disk.Partitions 
     Partition = $part.index 
     BootPartition = $part.BootPartition 
     'PartitionSize(GB)' = [math]::Round(($part.Size / 1GB ), 2) 
     Blocks = $part.NumberOfBlocks 
     BlockSize = $part.BlockSize 
     LDiskName = $psitem.Caption 
     FileSystem = $psitem.FileSystem 
      LDiskSize =  [math]::Round(($psitem.Size / 1GB ), 2) 
     LDiskFree =  [math]::Round(($psitem.FreeSpace / 1GB ), 2) 

  New-Object -TypeName PSObject -Property $props



Use Get-CimInstance to retrieve the instances of the Win32_LogicalDisk class. Use a filter for DriveType = 3 – which is local disks (as far as the server is concerned – they could be on a SAN or NAS).

Foreach of the disks get the associated partition and use that object to get the associated physical drive.

CIM (WMI) has the concept of associators and references.

A reference is a pointer showing you which instance is associated with another instance. For example:

PS> Get-CimInstance -ClassName Win32_LogicalDiskToPartition

Antecedent      : Win32_DiskPartition (DeviceID = "Disk #0, Partition #1") 
Dependent       : Win32_LogicalDisk (DeviceID = "C:") 
EndingAddress   : 511578663935 
StartingAddress : 368050176 
PSComputerName  :

Logical disk C: is associated with partition #1 on disk #0

If you want to actually get the associated class then you do this

PS> $ld = Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DeviceID = "C:"' 
 PS> Get-CimAssociatedInstance -InputObject $ld -ResultClass Win32_DiskPartition

Name             NumberOfBlocks       BootPartition        PrimaryPartition     Size                Index 
 ----             --------------       -------------        ----------------     ----                ----- 
 Disk #0, Part... 998458230            False                True                 511210613760        1


PS> Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DeviceID = "C:"' |   Get-CimAssociatedInstance -ResultClass Win32_DiskPartition

Name             NumberOfBlocks       BootPartition        PrimaryPartition     Size                Index 
 ----             --------------       -------------        ----------------     ----                ----- 
 Disk #0, Part... 998458230            False                True                 511210613760        1

Once you’ve go the partition and physical disk instances. Populate your output object and loop. Notice that the pipeline is output directly to the variable $diskinfo. You don’t need to build arrays – get the pipeline to do it for you.

Each logical disk gets an output like this

Disk              : 0 
 Model             : Samsung SSD 840 PRO Series 
 Firmware          : DXM06B0Q 
SerialNUmber      : S1AXNSAF329511V 
DiskSize(GB)      : 476.93 
 Partitions        : 3 
 Partition         : 1 
BootPartition     : False 
PartitionSize(GB) : 476.1 
 Blocks            : 998458230 
BlockSize         : 512 
LDiskName         : C: 
FileSystem        : NTFS 
LDiskSize         : 476.1 
LDiskFree         : 212.33

That’s working up the stack. What about working down. That’s a similar process:

$diskinfo = Get-CimInstance -ClassName Win32_DiskDrive | 
foreach { 
  $disk = $psitem 
  $parts = Get-CimAssociatedInstance -InputObject $psitem -ResultClass Win32_DiskPartition

  foreach ($part in $parts) { 
    Get-CimAssociatedInstance -InputObject $part -ResultClassName Win32_LogicalDisk | 
    foreach { 
       $props = $null

      $props = [ordered]@{ 
        Disk = $disk.Index 
        Model = $disk.Model 
        Firmware = $disk.FirmwareRevision 
        SerialNUmber = $disk.SerialNumber 
        'DiskSize(GB)' = [math]::Round(($disk.Size / 1GB ), 2) 
        Partitions = $disk.Partitions 
        Partition = $part.index 
        BootPartition = $part.BootPartition 
         'PartitionSize(GB)' = [math]::Round(($part.Size / 1GB ), 2) 
        Blocks = $part.NumberOfBlocks 
        BlockSize = $part.BlockSize 
        LDiskName = $psitem.Caption 
        FileSystem = $psitem.FileSystem 
        LDiskSize =  [math]::Round(($psitem.Size / 1GB ), 2) 
        LDiskFree =  [math]::Round(($psitem.FreeSpace / 1GB ), 2) 

      New-Object -TypeName PSObject -Property $props 

Start with getting the instances of Win32_Diskdrive. Foreach instance get the associated partitions – Win32_DiskPartition.

Iterate through the partitions and get the associated logical disk. Create your object and output.

NOTE: neither of these techniques will show the partitions that don’t contain logical drives so you won’t see the boot partition and other “hidden partitions” on modern Windows machines. if you need those look at Win32_DiskPartition directly.

under: PowerShell and CIM, PowerShell and WMI, Storage

Finding a CIM class

Posted by: | June 30, 2017 Comments Off on Finding a CIM class |

One of the problems you might find is finding a CIM class. You know its name but you don’t know which namespace its in.

The old WMI cmdlets allow you to search the namespaces recursively

PS> Get-WmiObject -Class Win32_Process -Namespace root -Recurse -List

   NameSpace: ROOT\CIMV2

Name                                Methods              Properties 
 ----                                -------              ---------- 
 Win32_Process                       {Create, Terminat... {Caption, CommandLine, CreationClassName, CreationDate...}

But the CIM cmdlets don’t have this functionality. I’ve been meaning to do something about this for ages but finally got motivated by something I read while proof reading PowerShell in Action – yes its getting closer, much closer.

What I ended up with is these 2 functions

function get-namespace { 
param ([string]$namespace = 'root') 
  Get-CimInstance -Namespace $namespace -ClassName '__NAMESPACE' | 
  foreach { 
        "$namespace\" + $_.Name 
        get-namespace $("$namespace\" + $_.Name) 

function find-cimclass { 
param ( 
 [string]$namespace = 'root', 

$class = $null

## test namespace for class 
 $class = Get-CimClass -Namespace $namespace -ClassName $classname -ErrorAction SilentlyContinue

if (-not $class) { 
  $namespaces = get-namespace -namespace $namespace 
  foreach ($name in $namespaces){ 
    $class = $null 
    $class = Get-CimClass -Namespace $name -ClassName $classname -ErrorAction SilentlyContinue 
    if ($class){break} 


Find-Cimclass takes a namespace and class name as parameters. It tries to find the class in the given namespace. If it can’t find it then get-namespace is called to generate a list of namespaces to search. The function iterates over the collection of  namespaces testing each one for the class. When it finds the class it returns the class information.

Get-namespace  searches for all instances of the __Namespace class in the given namespace. it then recursively call itself to test each of those namespaces. That way you get the whole tree.

If you’re searching for a given class I recommend that you start at the root class to ensure that you test everywhere.

under: PowerShell and CIM, PowerShell and WMI

Find the logged on user

Posted by: | June 12, 2017 Comments Off on Find the logged on user |

One method of finding the logged on users is to use CIM

$ComputerName = $env:COMPUTERNAME

Get-CimInstance -ClassName Win32_Process -ComputerName $ComputerName -Filter "Name = 'explorer.exe'" | 
foreach { 
 $lguser = Invoke-CimMethod -InputObject $psitem -MethodName GetOwner 
 $Properties = @{ 
 ComputerName = $ComputerName 
 User = $lguser.User 
 Domain = $lguser.Domain 
 Time = $User.CreationDate 
 New-Object -TypeName PSObject -Property $Properties 

Get the Win32_Process instances for explorer.exe and foreach of them use the GetOwner method to get the owners names and domain. Create an object and ouput


under: PowerShell and CIM, PowerShell and WMI

wmic deprecated

Posted by: | January 27, 2017 Comments Off on wmic deprecated |

I saw a forum post today where the question involved the use of the wmi command line tool wmic.


Wmic was deprecated in Windows Server 2012 – https://technet.microsoft.com/en-us/library/hh831568(v=ws.11).aspx. It will eventually be removed.


You should use the CIM cmdlets instead of wmic. The syntax is much easier and the resultant code is easier to understand.


A little known fact – the PowerShell project was originally started as a replacement for wmic.

under: PowerShell and CIM, PowerShell and WMI

Applying updates through WSUS

Posted by: | December 22, 2016 Comments Off on Applying updates through WSUS |

I like to keep the virtual machines in my test lab up to date so have a WSUS server to download and manage updates. The difficulty is applying the updates. With Windows 2012 R2 I used a module that would contact the WSUS server and apply the updates – the was especially useful on server core installations.

I found with Windows 2016 that this COM based module wasn’t reliable so after a bit of investigation discovered that there are some CIM classes that you can use to discover and apply available updates and see what updates have been applied.


All I need is a simple set of code so wrote a bare bones module that offers three functions:

#Scan for available updates
function Get-AvailableUpdate {
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
$result = $ci | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=0";OnlineScan=$true}

#Install all available updates
function Install-AvailableUpdate {
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
Invoke-CimMethod -InputObject $ci -MethodName ApplyApplicableUpdates

#list installed updates
function Get-InstalledUpdate {
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
$result = $ci | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=1";OnlineScan=$true}


Testing so far seems to be good. As this is just for me I’m bothering with adding error testing or other production ready stuff. This works and I’ll fix problems as they occur

under: PowerShell and CIM, PowerShell and WMI, Windows Server 2016, WSUS

Older Posts »