Usually when you need information from CIM (WMI), you only need to query a single instance. Sometimes, you need to dig through a series of class relationships to find what you want. I’ve built a pair of PowerShell scripts to make that easier.
CIM vs WMI
A quick word on CIM and WMI — CIM stands for “Common Information Model”. It is a standard governed by the Distributed Management Task Force. Microsoft extended CIM when they added it to Windows and branded it “Windows Management Instrumentation”. In the intervening years, they have largely moved away from their original WMI-stamped interfaces in favor of newer CIM-labelled features. The underlying data and functions have not changed.
In Windows PowerShell, you can use cmdlets with *Wmi*, such as Get-WmiObject. These cmdlets did not find their way into PowerShell Core, and never will. The CIM cmdlets, such as Get-CimInstance, exist in both PowerShell offerings.
I have begun using the CIM cmdlets exclusively. They’re faster in most cases and boast many features that you cannot find in the WMI cmdlets, such as separation of System properties from Instance properties and simple ways to detect whether or not a property is a key. The only features I have been unable to locate in the CIM cmdlets is a way to uncover glue classes without exploring the namespace or a way to specify antecedent vs. descendant associators. Those are topics for another article.
Simple CIM Queries
Let’s start with a run-down of this article’s focus. Let’s say that you want to know your system’s serial number so that you can get support from the manufacturer. Just query Win32_Bios:
Get-CimInstance -ClassName Win32_Bios
or, more practically when working interactively:
You’ll get an object with a SerialNumber property that you can (maybe) use:
That sort of thing tends to work perfectly well in the default namespace (root/cimv2). Most of the classes have only one instance, so you don’t need to bother with relationships. Many of the other namespaces tell a different tale.
CIM Association Queries
What happens when you have multiple instances of a class, and you seek information related to one of them that is held by another class? As an example, you can find all of your system’s power management settings inside instances of Win32_PowerSetting in root/cimv2/power. But, how do you know if one attaches to the current power plan? Instances of Win32_PowerPlan represent the system’s power plans, so you can ask CIM to tell you how they connect.
So, if you want to only see the settings for the active power plan, start by asking CIM for that plan:
OK, I suppose that’s not so bad, right? Of course, if you want to step through all of the power plans instead of just the active one, you’d have to telescope out another foreach, but still OK, right?
But wait, we still have the Win32_PowerSettingSubgroup and Win32_PowerSettingDefinition instances that contain useful information. So, more nested foreach’s right?
That’s just in the root/CIMV2/power branch. I typically work in root/virtualization/v2 where I frequently need to walk five or more steps to get what I need. That’s a lot of foreachs. Just looking at the script gives that “Eww, gross!” feeling.
A Tale of Two Scripts
We have two problems: discoverability and code bloat.
Problem 1: Discoverability
The example that I gave above only has us walk through a couple of levels to figure out how everything is connected. It gets really tedious very quickly as you need to traverse more deeply. Without a good breadcrumb system, even a minor distraction can cause you to lose your place in the hierarchy.
The CIM cmdlets do little to help as, even though CIM contains the necessary information, you cannot restrict your association search to only antecedents or only descendants. So, you sometimes find yourself accidentally walking back up a tree. Other times, the tree structure is complicated enough that you must walk the tree in multiple directions.
Problem 2: Code Bloat
Once you finally learn the relationship between your source and destination instance(s), now you need to code the thing up. Sometimes you get “lucky” and the target instance has a property that you can use in a -Filter. But, that’s brittle, and you don’t always get lucky. So, you wind up with a lot of foreachs. And then you discover that you also need to walk a different path using different criteria, so you have a lot of nearly duplicated script.
The Solution, in Two Scripts
I built two scripts to address these problems.
Find-CimAssociatedInstance is the workhorse of these two scripts. You give it the source instance that you want to work from and the class name of the associated instance that you want to find, and turn it loose.
You can use the script to do everything, but my primary intent was to overcome the discoverability problem. In its first design (unpublished), I naively walked each association tree until I found what I was looking for or until I hit a brick wall, then I went back and started at the next first-level association. That approach generally yields the fastest results in one-off uses, but has no concern for the overall traversed distance.
So, I rebuilt it to search all first-level instances, then all second-level instances, etc. That approach takes the most time, but results in the shortest possible distance.
The script does take into account that several trees have multiple pathways to the same object, which makes duplicate positives and infinite recursion possible. It protects itself against checking the same object for associations more than once.
Unfortunately, because the CIM cmdlets do not have good support for antecedent vs. descendants distinctions, the script must search radially. That can cause it to loop upward and discover relationships that you don’t want. To combat that, I provided the -ExcludeBranches parameter. Give it a class name or a path, even partials. It will not search any matching instances for associations.
Two Script Modes
The script has two modes. The first returns the discovered instances. The second returns the textual path to those instances. Specify -PathOnly to get the second behavior.
To solve our problem indicated above, just run this:
Find-CimAssociatedInstance does a lot of work. For long trees, it can take quite a while. I needed a script to quickly get to what I wanted when I no longer needed the discovery phase. So, I wrote Get-CimDistantInstance. I can interactively use Find-GetCimAssociatedInstance with the -PathOnly parameter. I can use its output in a permanent script file.
So, after discovering that I get to Win32_PowerSetting via the index class, I can write a script like this:
Neat, tidy, and I could search a deeper tree with the exact same number of script lines.
Notice that the path does not contain the name of the source instance, which means that you cannot directly use the output of Find-CimAssociatedInstance. That was a necessary restriction because multiple CIM instances relate to other instances of the same name, and any fool-proofing to prevent unexpected behavior ballooned the script unacceptably. Since I expect you’ll use Find- interactively and save the results permanently, it seemed acceptable to leave it out.
To quickly transform the output of Find-CimAssociatedInstance -PathOnly so that Get-CimDistantInstance can use it:
I can only test so much on my own, and I certainly did not think of all possibilities. Make requests and suggestions. Submit code fixes and enhancements, if you like. I prefer to have them on the GitHub pages, but you can use the comments section here, if you wish.