Skip to main content

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.

Popular posts from this blog

Test connectivity via a specific network interface

Recently while working on a Private cloud implementation, I came across a scenario where I needed to test connectivity of a node to the AD/DNS via multiple network adapters.  Many of us would know that having multiple network routes is usually done to take care of redundancy. So that if a network adapter goes down, one can use the other network interface to reach out to the node. In order to make it easy for everyone to follow along, below is an analogy for the above scenario: My laptop has multiple network adapters (say Wi-Fi and Ethernet) connected to the same network. Now how do I test connectivity to a Server on the network only over say Wi-Fi network adapter?

PowerShell + SCCM : Run CM cmdlets remotely

Today I saw a tweet about using implicit remoting to load the Configuration Manager on my machine by Justin Mathews . It caught my eye as I have never really tried it, but theoretically it can be done. Note - The second tweet says "Cannot find a provider with the name CMSite", resolution to which is in the Troubleshooting section at the end.

PowerShell : Trust network share to load modules & ps1

Problem Do you have a central network share, where you store all the scripts or PowerShell modules ? What happens if you try to run the script from a network share ? or if you have scripts (local) which invoke scripts or import PowerShell modules stored on this network share ? Well you would see a security warning like below (Note - I have set execution policy as 'Unrestricted' not 'bypass' here): Run a .ps1 from the network share Well this is a similar warning, which you get when you download scripts from Internet. As the message says run Unblock-File cmdlet to unblock the script and then run it, let's try it.