PowerShell CIM CmdLets – Working with lazy properties

In my last post, I explained how to solve one of the stumbling blocks when working with the PowerShell CIM CmdLets. The second problem, which was both pretty nasty as it basically prevented me from using the CIM CmdLets properly for SCCM and pretty time consuming, as it was hard to find a solution, was working with lazy properties.
The Problem
Lazy properties are unique to SCCM. As some of the objects in SCCM can be pretty large or can consume a lot of resources, the SCCM WMI provider has an additional qualifier called “lazy”. All poperties that are marked with this qualifier won’t be returned on default. That means, if you e.g. get a list of SMS_AuthorizationList items, which is the equivalent of the “Software Update Groups” in the ConfigMgr console. the Updates property that contains a list of Updates of this Software Update Group, will be there, but it will be null. Or better, not set.
Why is this an issue?
First, you might actually need the value in your script. E.g. the above mentioned list of updates is probably one of the more important information in this object.
The bigger issue, which is actually a real big problem, arises, if you are trying to update an object. Lets assume you want to change the description of a SCCM object. So you query for this object and filter e.g. by the current name or its ID or similar. Then you update the description property and as soon as you save this object back to WMI, you have basically corrupted it, as all lazy properties have been erased! Be sure you wouldn’t be the first one that ran into this issue.
Trevor Sullivan has a very good blog post about this at PowerShell: ConfigMgr WMI Provider (feat. Lazy Properties)
The key thing to remember with lazy properties is that you have to get a reference to each WMI instance / object explicitly. There are typically two common ways to accomplish this
- Using the __PATH property
12345678$UpdateLists = Get-WmiObject -Namespace root\sms\site_TST -Class SMS_AuthorizationListforeach ($UpdateList in $UpdateLists) {$UpdateList = [wmi]"$($UpdateList.__PATH)"$Updates = $UpdateList.Updates...} - Using the Get() method
12345678$UpdateLists = Get-WmiObject -Namespace root\sms\site_TST -Class SMS_AuthorizationListforeach ($UpdateList in $UpdateLists) {$UpdateList.Get()$Updates = $UpdateList.Updates...}
However, the object returned by Get-CimInstance has neither a __PATH property nor does it have a Get() (or equivalent) method!
The Solution
This was a really nasty problem for me, as I had started converting most of my scripts that deal with WMI related operations using the CIM CmdLets. And I was unable to find a proper solution. I wasn’t the only one as e.g Trevor Sullivan published an Introduction to the CIMCmdlets PowerShell Module on The Scripting Guys blog saying:
For example, people who are automating Microsoft System Center 2012 Configuration Manager, the lack of the __PATH property value is highly detrimental, as it does not allow them to deal with WMI classes & properties marked with the “Lazy” qualifier.
So whenever I had to deal with lazy properties in SCCM I had to fall back to the WMI CmdLets. Basically making the benefits of the CIM CmdLets useless.
At the end, the solution came again from the post CIM Cmdlets – Some Tips & Tricks from the PowerShell team. It wasn’t obvious, but “Tip #10 Making Get/Enumerate efficient” did the trick finally. They demonstrated how to refresh the data of an object, without retrieving it again:
1 2 3 4 5 |
PS:> # Get instance of a class PS:> $p = Get-CimInstance -ClassName Win32_PerfFormattedData_PerfOS_Processor PS:> # Perform get again by passing the instance received earlier, and get the updated properties. The value of $p remains unchanged. PS:> $p | Get-CimInstance | select PercentProcessorTime |
The solution is to update the current “limited” object that doesn’t contain any value for lazy properties, but the properties themselves, by passing it to the Get-CimInstance CmdLet and then save it back to the original (or optionally a new) object.
1 2 3 4 5 6 7 8 |
$UpdateLists = Get-CimInstance -Namespace root\sms\site_TST -Class SMS_AuthorizationList foreach ($UpdateList in $UpdateLists) { $UpdateList = $UpdateList | Get-CimInstance $Updates = $UpdateList.Updates ... } |
This will also read the value of all lazy properties as well!
I hope you find this tip valuable. I was honestly struggling with this for months. So for me it definitely is, as I can now concentrate on one set of CmdLets without taking care about what type of CmdLet to use for what type of operation.
Hey Maik,
This is really useful information, I did not know it exist.
Thanks for sharing.
Dexter
Hey, thanks for this info, really saved me some time. I was asked to review some code that’s in WMI, but the same principle applies.
$wmi = Get-WmiObject -Namespace root\sms\site_IS1 -Class SMS_AuthorizationList -Filter ‘CI_ID = 16819446’
$wmi.Get()
$wmi.Updates
Other examples I found used the WMI class to just convert the path, but doing this precludes using credentials, and didn’t fit my workflow.