Wednesday, June 04, 2014

PowerShell + SCCM 2012 : Create Dependency Rules

Following on my last post on creating supersedence rules in ConfigMgr Applications with PowerShell, this post is all about creating dependency rules.

The approach is very similar to the last one and Adam Meltzer had already posted the sample C# version of the code here .

Have to admit I tried to re-use the code from the last time and in the Process screwed one of the Applications (by putting up wrong serialized XML on the WMI Instance of the App).

There can be multiple dependencies in ConfigMgr (attached with a OR or AND operator), but for the sake of simplicity we will look at a very simple scenario  :


For Application 1 ( Notepad++ in this case) we will add an Application 2 (e.g .NET 4 ) as the dependency. I know that hardly is a dependency but I am a bit lazy to create other applications at the moment...though I can do that with #PowerShell too..but am too lazy for that too :P 

At the end of the Code we will get this relationship:

Code and Walkthrough:

[UPDATE] fixed few typos in the code, tested it again in my environment it does work.

To be entirely honest I reverse-engineered this one as there were too many things to look at. 

Created a dependency on some other random application (from Console) and then got the de-serialized Object back, studied it and worked my way through the changes needed.

Note - the code about to follow doesn't require any PowerShell Module but however is dependent on few assemblies (can be found on a machine having ConfigMgr Console installed).

First let put the pre-requisites out of the way:

#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"
Add-Type -Path "$(Split-Path $Env:SMS_ADMIN_UI_PATH)\Microsoft.ConfigurationManagement.ApplicationManagement.MsiInstaller.dll"

Note here one extra dll is referenced the second one ending with MsiInstaller.dll

Now we will try to get the direct reference to the Applications Objects which will be used later on :

#Creating Type Accelerators - for making assemlby refrences easier later
$accelerators = [PSObject].Assembly.GetType('System.Management.Automation.TypeAccelerators')

#region Application 1
$ApplicationName1 = "Notepad++ 6.2.3"

#get direct reference to the Application's WMI Instance
$application1 = [wmi](Get-WmiObject -Query "select * from sms_application where LocalizedDisplayName='$ApplicationName1' AND ISLatest='true'").__PATH

#Deserialize the SDMPackageXML
$App1Deserializedstuff = [SccmSerializer]::DeserializeFromString($application1.SDMPackageXML)

#endregion Application 1

#region Application 2

#Name of the Application which will be added as a dependency
$ApplicationName2 = ".NET4"

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

#deserialize the XML
$App2Deserializedstuff = [SccmSerializer]::DeserializeFromString($application2.SDMPackageXML)


One another neat trick which we can use here is that of creating type accelerators. Note that I create a TA 'SccmSerializer' at the firest 3 lines (above code) to save me typing up that long typename again and again.

Now while creating Dependency we do things a little bit differently then what was done with Supersedence...not a surprise there :D 

Below is the Object hierarchy depicting where all the changes go :

Object Figure #1
Now all the things will be added inside this Dependencies property, So before we start another Object view showing things we will configure in the code :

Object Figure #2

In short we will create an Deployment Type Rule  using  Expression, Annotation,  Severity and an empty Rule Context.

Creating Expression is the real piece of work here and this is where we will start first , Have put the few numbers in some order to follow along:

Object Figure #3
The below code will create a Deployment Type expression and the code is in order corresponding to the numbers in Object Figure #3:

  • Code below for Region 1: Store all the Arguments before hand

#region 1

#Store the arguments before hand
$ApplicationAuthoringScopeId = ($application2.CI_UniqueID -split "/")[0]
$ApplicationLogicalName = ($application2.CI_UniqueID -split "/")[1]
$ApplicationVersion =  $application2.SourceCIVersion
$DeploymentTypeAuthoringScopeId = $App2Deserializedstuff.DeploymentTypes.scope
$DeploymentTypeLogicalName = $
$DeploymentTypeVersion = $App2Deserializedstuff.DeploymentTypes.Version
$EnforceDesiredState = $True  #this determines if the dependency Application will be auto installed

#endregion 1

  • Code Below for Region 2: Desired State of the dependency is Mandatory / Required

#region 2
# set the Desired State as "Required" or Mandatory
$DTDesiredState = [Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.DeploymentTypeDesiredState]::Required

#endregion 2

  • Code Below Region 3: Create an Intent Expression and add it to Operand
    Note - The Operand is a custom collection of the Intent Type Expressions ;)

#region 3
#create the intent expression which will be addded to the Operand
$intentExpression = new-object Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.DeploymentTypeIntentExpression -ArgumentList  $ApplicationAuthoringScopeId, $ApplicationLogicalName, $ApplicationVersion, $DeploymentTypeAuthoringScopeId, $DeploymentTypeLogicalName, $DeploymentTypeVersion, $DTDesiredState, $AutoInstall

#Create the Operand - Note the typename of this one
$operand = New-Object  Microsoft.ConfigurationManagement.DesiredConfigurationManagement.CustomCollection[Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.DeploymentTypeIntentExpression]

#add the Intent Expression to the Operand

#endregion 3

  • Code Below Region 4: Create an OR operator which can be used later to combine several Operands. 

#region 4

#create the new OR operator
$OrOperator = [Microsoft.ConfigurationManagement.DesiredConfigurationManagement.ExpressionOperators.ExpressionOperator]::Or

#endregion 4
  • Code Below Region 5 : use Operator and Operand to create the Expression

#region 5

#Now the Operator and Operand are added to the Expression
$BaseExpression = New-Object -TypeName Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.DeploymentTypeExpression -ArgumentList $OrOperator,$operand

#endregion 5

Voila our expression is ready :)

Moving on to the Object Figure #2
We now need to create Severity, Annotation and an empty Rule Context (will suffice).
Annotation is nothing but a name given to the Dependency Rule andis a must for Dependency Rules (try skipping that)

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

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

#Create the Annotation - Name & description of the Dependency
$annotation = New-Object -TypeName Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Rules.Annotation
$annotation.DisplayName.Text = "DependencyName"

Putting Everything Together

So we have all the ingredients ready now to finally create the Deployment Rule and add it to the Dependencies property (Object Figure #1)

#Create the new DeploymentType Rule
$DTRUle = New-Object -TypeName Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Rules.DeploymentTypeRule -ArgumentList $("DTRule_"+[guid]::NewGuid().Guid),$severity, $annotation, $BaseExpression

#add the DepolymentType Rule to Dependecies

Now the Deployment Type Rule is added to the de-serialized XML and we are now ready to serialize it and push it to the WMI Instance of the Application.

# Serialize the XML
$newappxml = [SccmSerializer]::Serialize($App1Deserializedstuff, $false)

#set the property back on the local copy of the Object
$application1.SDMPackageXML = $newappxml

#Now time to set the changes back to the ConfigMgr

In the last line when you try to update the WMI Instance of the Application you need to acquire a SEDO lock on the Application Object. Make sure that no one is editing the Application at that point of time.

Proof :

Will go back to my Room (LAB) and attach the Screenshot later.

As promised :

Thanks that's it for today. What's next ? Requirement Rules