Sunday, November 08, 2015

PowerShell + SCCM : WMI Scripting

Why should I use WMI, when there is a PowerShell module available for Configuration Manager (CM Module) already?

Well the cmdlets behind the scene interact with the WMI layer and if you know which WMI classes the corresponding cmdlet work with , it can be of help in future by :

  1. Switching to native WMI calls when the CM cmdlets fail for some reason (probably bug in the CM Module).
  2. Making your scripts more efficient by optimizing the WMI (WQL query) calls, the cmdlet will query all the properties for an Object (select *) you can select only ones you need. 
  3. Lastly no dependency on the CM Module, you can run these automation scripts from a machine not having the CM console installed (needed for CM module).
Moreover ConfigMgr uses WMI extensively, you already have this knowledge leveraging it with PowerShell shouldn't surprise you. This post assumes you have been working with CM cmdlets (you already are versed with PowerShell), know where the WMI namespace for ConfigMgr resides and the basics of WMI.

Example Problem:

I will use one of the problem people have been commenting about a lot on the below post

PowerShell + SCCM 2012 R2 : Create an Application (from MSI) & Deploy it

What they want to do is specify multiple app-categories to an application while creating these apps using PowerShell ?

This seemed trivial at first as the help for the Set-CMApplication cmdlet which is used to set the app category for an application accepts a string array. Probably a bug in the cmdlet (as this seems to be working on the most recent CM module). See below the comment screenshot from the post :

This is strange.
So what you do now ? Don't worry you can at anytime fallback to using PowerShell and WMI  until the bug is fixed ( it seems to be fixed in the latest version).

So what I am going to show you now is 

  1. how to start exploring the WMI Class associated.
  2. Read the documentation.
  3. Use PowerShell + WMI to automate it.

For the above scenario,

I have an Application named Notepad++ and two application categories named "PSCreatedApps" and "OpenSource". I want to add these 2 categories to the application via the WMI only (remember my CM cmdlet has a bug).

Get the WMI Class name:

This shouldn't be too hard to find, in the post we were using the Set-CMApplication cmdlet to set multiple app categories. So the first and the easiest way to find the WMI Class you are playing with is the corresponding Get-CMApplication cmdlet (there are other ways to get this using ConfigMgr console too, find them).

Pipe the output of the Get-CMApplication cmdlet to Get-Member to see the WMI Class you have been fiddling with all along :

The typename says IResultObject#SMS_Application (not the WMI Object) because the CM cmdlets use the IResultObject interface to expose data for result objects (don't worry about that part much). SMS_Application is the WMI Class here.

Another way would be to closely observe the SMSProv.log when you execute the CM cmdlet, reading this log is of utmost importance when Scripting against ConfigMgr.

Well reading the SMSProv.log takes some time and practice but a good way is to dump the Verbose stream from the cmdlet. As this does show all the WQL query being run behind the scenes, so you can do a mapping between the SMSProv.log and understand what might be causing the failure.

Just to show how it is done, see the first Verbose stream message showing the WQL below is where it shows up in the log :

The log is a mine of information and one should invest time in interpreting it while playing with the Cmdlets, WMI (even the actions on the Console show up here, neat trick to get the explore too).

Read the WMI Class documentation

In the documentation for the SMS_Application class you will find that the base class for it is SMS_ConfigurationItemBaseClass . Base class means that SMS_Application (Child Class) class inherits from SMS_ConfigurationItemBaseClass. So we need to be actually looking for documentation on both classes.

Also do a search on the page for all the properties having word "Category" in it, below is a snip of all the properties from the page :

Now at this point we are looking to set a property on the application object which has something to do with the category. So only read/write properties should interest us, drop the LocalizedCategoryInstanceNames property from above :)
Take a moment to notice that read/write properties named CategoryInstance_UniqueIDs, PlatformCategoryInstance_UniqueIDs are pointing us to see the base class documentation (highlighted in yellow). Click on the link and it should take you to the base class, for both the properties you will see :

the documentation for the CategoryInstance_UniqueIDs looks promising. Observe that the Data Type is String Array , which clearly means more than one category unique ids can be assigned. But how do we find these category unique instance ids ?

Leaving the exercise to finding this via WMI only to you and taking a shortcut here by using Get-CMCategory cmdlet :

We have all the key pieces together :
  1. Class Name - SMS_Application
  2. Writable property corresponding to the Categories.
  3. Category Unique Ids for our 2 categories.

Let's get to the final phase of using WMI now purely to set 2 app category on the Application, below is the same operation done via WMI which was intended to be done by the Set-CMApplication cmdlet :

# Import-Module configurationmanager
# No need for this we are using WMI. Set the default parameters for the WMI Calls ( personal preference)

$PSDefaultParameterValues = @{
    'Get-WMiObject:ComputerName'='DexSCCM'; # point the Get-WMIObject to the ConfigMgr server having WMI namespace installed
    'Get-WMiObject:NameSpace'='root/SMS/Site_DEX'; # Point to the correct WMI namespace for CM automation

# get the SMS_Application object instance for application Notepad++
$Application = Get-WmiObject -Query "SELECT * from SMS_Application WHERE LocalizedDisplayName = 'Notepad++' AND IsLatest = 1 AND IsHidden = 0"

# Get the UniqueIds for the categories - PSCreatedApps and OpenSource
$CategoryIDs = Get-WmiObject -Query "SELECT CategoryInstance_UniqueID FROM SMS_CategoryInstance WHERE CategoryTypeName='AppCategories' and LocalizedCategoryInstanceName in ('PSCreatedApps','OpenSource')" |
                    Select-Object -ExpandProperty CategoryInstance_UniqueID

# Let's modify the Object in the memory
$Application.CategoryInstance_UniqueIDs = $CategoryIDs

# Sync the changes to the ConfigMgr Server

ET Voila ! (check the SMSProv.log too when you use this way to troubleshoot).

Below is a gif showing this in action, I tried showing that all things done via the Console, CM Cmdlet or PowerShell actually interface with WMI layer (all actions get recorded in the SMSProv.log) :

Looking for more ConfigMgr + PowerShell stuff, below is link to all my posts around the topic :

Resources :

My friend Stephane has few posts talking about troubleshooting WMI functions and a list of things you need to know when scripting against the SCCM WMI provider.