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
$Application.Put()

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 :
http://www.dexterposh.com/p/collection-of-all-my-configmgr.html


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.
http://powershelldistrict.com/troubleshoot-wmi-functions/

http://powershelldistrict.com/top-6-things-you-need-to-know-when-scripting-with-sccm/

8 comments:

  1. I am wondering why I haven't read this post before. This is really a great post Dex! It is a shame I didn't had this information when I first started scripting WMI + ConfiMgr.
    Pretty smart to get the name of the classes using the ConfigMgr Cmdlets. I want to point out to your readers, that if no cmdlet is available, the best option would be to create something in the Console, and actively look at the SMSProv.log log. All the referenced WMI Classes are at least 'mentionned' in there.

    ReplyDelete
    Replies
    1. Yeah ! Stephane, it would have been nice to have few more posts around on getting started with ConfigMgr WMI scripting, when I started too. Though I learned few things the hard way, I intended to share this post to help someone starting by :)

      Delete
  2. I have a custom bat script I want to execute against the msi. The MSI is published to Application Catalog using Application model, not package. Is this possible? Or is it restrict to package only.

    ReplyDelete
  3. You are correct, you need to use Packages for running any custom scripts.
    BTW this could be possible with Application model too if you are using PowerShell App Deployment toolkit for wrapping up your applications (exe or msi) for deployments.

    In our company all the apps are wrapped around with this toolkit, making it highly customizable deployment option.

    ReplyDelete
  4. hi, greate post, i'm working on a powershell GUI to automate the creating applications process and i'm trying changing all the steps where i'm using sccm cmdlets to WMI. So at this point I need to now how can I set the Icon to the application, create a new app categorie if it doesn't exist and how can I distribute the application to the DP via WMI.

    Note: if it doesn't possible make this operations via WMI can you tell me what other alternatives I have

    best regards,
    João Torres

    ReplyDelete
    Replies
    1. Creating App category and distributing application to the DP is possible there might be links to that in my blog, but setting the application icon would be tricky.

      As a general exploring technique, I used to perform actions in the CM console and then monitor the SMSProvider.log to see which CIM classes and methods are used behind the scene. You can try that approach too.

      Delete
  5. Minor comment: This method doesn't seem to work when importing CSVs and working with them. It errors out with "generic failure" which is annoying. I wish Microsoft would fix the actual cmdlet.

    ReplyDelete
    Replies
    1. Did you try adding a sleep after the call to the put() method ?
      I think, it would take some time for the WMI provider to process those changes.

      Delete