OK, this has turned out to be a long post and we need to start with some terminology. Its important here to understand the names and acronyms so you can go an research further. Remember that this is a PowerShell tutorial not a windows or remote management tutorial, we are going to dip our toes into remote management, but wider discussion on that might come in a later article
The PowerShell team and Microsoft published a blog post in 2012 that gave the following definitions
CIM: Common Information Model (CIM) is the DMTF standard [DSP0004] for describing the structure and behavior of managed resources such as storage, network, or software components.
WMI: Windows Management Instrumentation (WMI) is a CIM server that implements the CIM standard on Windows.
WS-Man: WS-Management (WS-Man) protocol is a SOAP-based, firewall-friendly protocol for management clients to communicate with CIM servers.
WinRM: Windows Remote Management (WinRM) is the Microsoft implementation of the WS-Man protocol on Windows.Introduction to CIM cmdlets, PowerShell Team, August 24th 2012 – https://devblogs.microsoft.com/powershell/introduction-to-cim-cmdlets/
As you might have guessed both CIM and WMI are used to gather information or to configure some settings on both local and remote machines, they are ways to connect to a common information repository. Both WMI and CIM use the same repository, they just use different technologies to connect to it and have slightly differing capabilities because of this. The recommendation now is to use CIM commands where possible, WMI commands are considered deprecated by Microsoft.
Here is a list from the eBook that accompanies 10961C – Automating Administration with Windows PowerShell
You should use CIM cmdlets instead of the older WMI cmdlets, because:
– CIM cmdlets use DCOM when querying the local computer.
– CIM cmdlets use WS-MAN for remote access.
– CIM cmdlets can use DCOM or WS-MAN for session-based connections to remote computers.
– CIM uses CIM sessions for accessing multiple devices.
– CIM improves the way of dealing with WMI associations.
– You can use the Get-CimClass cmdlet for investigating WMI classes10961C – Automating Administration with Windows PowerShell © Microsoft 2017
Using CIM sessions, CIM cmdlets can connect to computers that do not have WMF V2.0 or newer and do not have PowerShell Remoting enabled this means that CIM commands can be used on Linux computers as well as Windows whereas WMI is Windows only.
The repository that CIM and WMI use is organised into namespaces, each namespace is the equivalent of a folder to group related items together. Namespaces in turn hold classes, each class is a manageable component such as processors, disk drives, services and user accounts. Each computer will have namespaces relevant to its role and function so a server may be different to a client and a domain controller may be different to a file server.
To get the top level namespaces type this cmdlet – note that there are two underscores preceding the word Namespace. If you are unsure you can TAB complete after you have typed
-ClassName NOTE: I was only able to get this cmdlet to work using a server OS
Get-CIMInstance -Namespace root -ClassName __NameSpace
This will deliver a list of root namespaces.
To find the list of classes available in each namespace you can use the cmdlet
Get-CIMClass using the example below, simply replace the Namespace with the one you are interested in.
Get-CIMClass -NameSpace root\hardware
The image shows a concatenated list of classes from the command when typed into PowerShell on a VM running Windows Server 2019 in VMware Workstation.
Its worth noting that you can also use the cmdlet
Get-WMIObject -NameSpaces root\hardware -List to achieve a similar end, but TAB completion on the
-Namespace parameter is only available when using
The eBook that accompanies 10961C – Automating Administration with Windows PowerShell says the following:
Finding class documentation might be difficult, but you can find a lot of information on the Microsoft Developer Network (MSDN) website at https://aka.ms/vgw7v0 and in the TechNet Microsoft Script Center at https://aka.ms/xu24li. Although many Microsoft product groups and independent software vendors expose management information in the repository, only a few of them create formal documentation. In most cases, an Internet search for a class name provides your best option for finding the documentation that exists.
The classes in the root\CIMv2 namespace comprise an exception. The MSDN Library typically documents these classes well. However, an Internet search provides the best way to locate the documentation for a particular class. For example, typing the class name Win32_OperatingSystem into an Internet search engine is the fastest way to locate the documentation webpage for that class.
Remember that CIM and WMI are not native parts of Windows PowerShell. Instead, they are external technologies that Windows PowerShell can use and understand. However, because they are external technologies, Windows PowerShell Help does not document the repository classes.10961C – Automating Administration with Windows PowerShell © Microsoft 2017
It can be a guessing game finding the right namespace and class. Finding detailed information and help, beyond what is demonstrated here, can be difficult and time consuming. Common tasks are easily searched using Google or your chosen search engine and the more you use it the better you will become at guessing the right combination.
Using CIM and WMI
We have seen that CMI and WMI cmdlets are similar to any other PowerShell cmdlets, the difficulty is knowing which class you need to address so lets start by trying to find a class we can work with, every computer has disks so let’s start there.
Get-CimClass -NameSpace root\CIMV2 *disk* | ft -autosize
This brings a list of all the classes that match the statement “contains the word disk in the CimClassName“. I piped it into format-table and added the -Autosize parameter to ensure we can read the entire CimClassName Column. You can of course do this for any keyword, if you need to look for network or processors or users, just substitute the search word.
Get-CimClass -NameSpace root\CIMV2 *processor* | ft -autosize
As mentioned, every computer has a disk or two connected and as such we can now see that there are some classes to enumerate information about these. Let’s play here for a while.
Get-CIMInstance -Classname Win32_LogicalDisk Get-WmiObject –Class Win32_LogicalDisk
The commands above list all of the connected drives on the local machine, both commands return the same information in a slightly different format or layout but nonetheless they both work. I had to pipe
Format-Table to get it listed as a table, its default was to output a list.
You can further filter these to only find specific drive types
Get-CIMInstance -Classname Win32_LogicalDisk -Filter "DriveType=2" Get-WmiObject –Class Win32_LogicalDisk -Filter "DriveType=2"
Drive types listed here are:
- 0 = Unknown
- 1 = No Root Directory
- 2 = Removable Disk
- 3 = Local Disk
- 4 = Network Drive
- 5 = Compact Disk
- 6 = RAM Disk
So we have listed all the removable drives connected to my machine, or have we? Well, no we haven’t, I don’t know why and it’s beyond this tutorial but USB Flash Drives and USB Desktop drives are treated differently. Look back at the image that shows the full output of all logical drives, on my system G: and H: are named USB-1 and USB-2 respectively and they are indeed desktop USB drives. I am sure there is an explanation and I highlight the fact that you need to research this if you use that command. In terms of PowerShell though you have been able to audit and inventory the logical disks on the local machine using CIM and WMI
WQL is the WMI query language. It is structured similarly to SQL queries with SELECT, FROM and WHERE statements. Both
Get-WMIObject will accept WQL queries when you use the
WQL is a whole different tutorial, but here are a few examples, a bunch of further useful examples can be found on the CodeProject Website
# Get the logical disk information we retrieved earlier using WQL Get-CimInstance –Query "SELECT * FROM Win32_LogicalDisk WHERE DriveType = 3" Get-WmiObject –Query "SELECT * FROM Win32_LogicalDisk WHERE DriveType = 3" # Get information on running windows services Get-CIMInstance -query "SELECT * from win32_Service WHERE State = 'Running'" # Get the BIOS information and store all attributes in a variable $query = Get-CIMInstance -query "SELECT * from win32_BIOS"
Everything we have done thus far has been on our local machine, clearly the idea of PowerShell is to automate common tasks and auditing/gathering information isn’t really a task you want to do manually on each individual machine. So how do we complete these task on remote machines? Well that’s easy we use the parameter
Get-WMIObject -ComputerName Parent-DC1 -Class Win32_BIOS
Oh, the computer says no… of course, I am connecting to a machine in another, untrusted domain, so I need to tell it which credentials to use, lets add the
-Credentials parameter as well
Get-WmiObject -ComputerName Parent-DC1 -Credential Marvel\Administrator -Class Win32_BIOS
You’ll recognise this:
and that works …
But, but you said we should use CIM not WMI as that is deprecated. Yes I did indeed, but
Get-CIMInstance does not have a parameter
-credential. It uses WS-Man and WinRM to authenticate using Kerberos with the current logged on user and clearly that won’t work for me, as the remote computers are in an untrusted domain. I had to specify credentials in the WMI cmdlet. You can see the error related to WinRM in the screen capture below.
Get-CimInstance will work however, if you are on the same domain, or a trusted domain, with appropriate permissions. The alternative is to use CIM Sessions.
Create a new CIM Session and run the
Get-CimInstance command using
-session instead of
$session = New-CIMSession -ComputerName Child-Dc1 -credential film\administrator Get-CIMInstance -CimSession $session -query "SELECT * from win32_BIOS"
The advantage of this method is that you can create multiple session and store them in the one variable and then launch the command with that stored session, it will run in all the sessions held in the variable. To open a session, the WinRM service must be running and the WinRM exception activated in the Windows firewall, this is often not the case on Windows desktop OS unless specifically configured by Group Policy.
First Create the sessions and store them:
$session = New-CIMSession -ComputerName Client1,Client2 -credential film\administrator
Then run the command using the stored sessions:
Get-CimInstance -CIMsession $session -query "SELECT * from win32_BIOS"
You can of course combine these two commands onto one line if you wish, here is one example of doing that. Note that the cmdlet
New-CimSession is enclosed in brackets. This forces that cmdlet to run first and passes the returned values into the Get-CimInstance cmdlet.
Get-CimInstance -CIMsession (new-cimsession -ComputerName Client1, Client2 -Credential film\administrator) -query "SELECT * from win32_BIOS"
You should always remove any open sessions especially when scripting, so you release any resources they are using and remove the capability of anyone else using the authenticated session. You can do this simply by using
Remove-CimSession cmdlet and there are several ways to do so:
Remove-CimSession -ID 1 #Explicitly remove a session by its ID $session | Remove-CimSession #Remove all sessions stored in $session Get-CimSession | Remove-CIMSession #Remove all open sessions
WMI and CIM are powerful tools that can assist your administration of windows and other computers but need some planning and forethought. WMI cmdlets are considered deprecate but do allow a simpler method of quick access to machines not in your domain, CIM on the other hand allows a much simpler method to manage the same command on multiple machines without having to script anything.
This barely touches the surface, but I hope gives you a brief introduction.