Monday, May 26, 2014

PowerShell + SCCM 2012 : Create Supersedence Rule

The Application Model in ConfigMgr 2012 let's you do some pretty neat things. One of those things is specifying supersedence.

The ConfigMgr 2012 SDK has an example in C# showing how to create the Supersedence, so I studied it and was able to re-produce it in PowerShell.

Scenario


I have two Notepad++ Applications as below, We will create the Supersedence rule for 6.5.1 making it supersede 6.2.3. Look below for the terminology.




Rest of the Story: code and walkthrough

The code is pretty long and will step through it  in pieces.
Won't be using the ConfigurationManager PowerShell module. This is entirely WMI/CIM and some .NET Fu at work ;)

First let's get the pre-requisites out of the way by doing below:

001
002
003
004
005
006
#Load the Default Parameter Values for Get-WMIObject cmdlet
$PSDefaultParameterValues =@{"Get-wmiobject:namespace"="Root\SMS\site_DEX";"Get-WMIObject:computername"="DexSCCM"}

#load the Application Management DLL
Add-Type -Path "$(Split-Path $Env:SMS_ADMIN_UI_PATH)\Microsoft.ConfigurationManagement.ApplicationManagement.dll"

I usually prefer setting the $PSDefaultParameterValues while testing out stuff as am lazy to specify the ComputerName and Namespace parameter again and again ;)

Also we need to load the Application Management Dll in our session as we will be dealing with the Class of the objects defined in this assembly.

Now first a little background on what we are going to do, the Application stores all the configurations in it like deployment types, supersedence rules, dependencies etc in the serialized XML  format in the property SDMPackageXML ...to make any changes we need to de-serialize -> make changes to the XML -> Serialize it and push it back up to the WMI Instance of the Application. See very easy.

Read ConfigMgr MVP David O'Brien's article on this for more information

Let's get started and brace yourself for a bumpy ride :

First we get a direct reference to the Application using the [wmi] type accelerator because the SDMPackageXML is a Lazy property (lol am a bit lazy too :P )

After that we deserialize that to get back the XML and then we can manipulate it.

001
002
003
004
005
006
007
008
009
010

#Name of the Application which will be superseded
$Name = "Notepad++ 6.2.3"

#Reference to the above application
$supersededapplication = [wmi](Get-WmiObject -Query "select * from sms_application where LocalizedDisplayName='$Name' AND ISLatest='true'").__PATH

#deserialize the XML
$supersededDeserializedstuff = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::DeserializeFromString($supersededapplication.SDMPackageXML)

So now we have the deserialized XML , you can feel free to explore it a bit if you like after this (like I did ) and then you will find that you have to add something to the Supersedes property... to show where all the things are gonna be added in the Object , I came up with a neat idea of showing how the final object (Show-Object) will look like :



If the code looks confusing scroll back up and take a look at the object and it will make more sense where it's gonna go.

If we want an application to be superseded then fundamentally what we have to do is prohibit the deployment type of that application to run. This is what we gonna do using $DTDesiredState later (defined below)


001
002
003
# set the Desired State for the Superseded Application's Deployment type to "prohibit" from running
$DTDesiredState = [Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.DeploymentTypeDesiredState]::Prohibited 

Time to create an intent expression supplying the superseded Application's info to it and whether one need's the superseded Application uninstalled before the new Application (superseding one) installs itself...essentially it is the little check box n the below screenshot:







001
002
003
004
005
006
007
008
009
010
011
012
#Store the arguments before hand
$ApplicationAuthoringScopeId = ($supersededapplication.CI_UniqueID -split "/")[0]
$ApplicationLogicalName = ($supersededapplication.CI_UniqueID -split "/")[1]
$ApplicationVersion =  $supersededapplication.SourceCIVersion
$DeploymentTypeAuthoringScopeId = $supersededDeserializedstuff.DeploymentTypes.scope
$DeploymentTypeLogicalName = $supersededDeserializedstuff.DeploymentTypes.name
$DeploymentTypeVersion = $supersededDeserializedstuff.DeploymentTypes.Version
$uninstall = $false  #this determines if the superseded Application needs to be uninstalled when the new Application is pushed

#create the intent expression
$intentExpression = new-object Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.DeploymentTypeIntentExpression -ArgumentList  $ApplicationAuthoringScopeId, $ApplicationLogicalName, $ApplicationVersion, $DeploymentTypeAuthoringScopeId, $DeploymentTypeLogicalName, $DeploymentTypeVersion, $DTDesiredState, $uninstall
Note - Deployment Type Desired state stored in $DTDesiredState is used in creation of the intent expression.

After the Expression is created we finally create the DeploymentType Rule which gets added as the supersedence before that we create "none" severity  and empty rule context (one can play with these objects a bit )  which are used to create the rule.

001
002
003
004
005
006
007
008
009
010
011

# Create the Severity None
$severity = [Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Rules.NoncomplianceSeverity]::None

# Create the Empty Rule Context
$RuleContext = New-Object -TypeName Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Rules.RuleScope


#Create the new DeploymentType Rule
$DTRUle = New-Object -TypeName Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Rules.DeploymentTypeRule -ArgumentList $severity, $null, $intentExpression

Our DT Rule is ready now and we need to add this to the superseding application's deserialized XML so , let's first get a direct reference to the Application and :

001
002
003
004
005
006
007
008

$ApplicationName = "Notepad++ 6.5.1"
$application = [wmi](Get-WmiObject -Query "select * from sms_application where LocalizedDisplayName='$ApplicationName' AND ISLatest='true'").__PATH


#Deserialize the SDMPackageXML
$Deserializedstuff = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::DeserializeFromString($application.SDMPackageXML)

We need to add the DT Rule to the superseding Application's deploymenttypes:

001
002
#add the supersedence to the deployment type
$Deserializedstuff.DeploymentTypes[0].Supersedes.Add($DTRUle)

Now everything is in place and we are ready to serialize the XML using below:
001
002
003
# Serialize the XML
$newappxml = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::Serialize($Deserializedstuff, $false)

Now set the property back to the instance of the Application using the infamous put() method ;)


001
002
003
004
005
006
007
#set the property back on the local copy of the Object
$application.SDMPackageXML = $newappxml

#Now time to set the changes back to the ConfigMgr
$application.Put()


Verify the Changes in the console :



The code maybe daunting at first but as you test/experiment with it then it starts making a lot more sense..so don't be afraid to play with it.
Awesome...Next topic is dependence rules :)


Am trying to put a list of PowerShell + SCCM blogs out there and do let me know if I missed any.

2 comments:

  1. Great post, I have never seen that stuff explained in such detail !

    ReplyDelete
    Replies
    1. Michael: Thank you for the kind words :)

      Delete