Skip to main content

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  :


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')
$accelerators::Add('SccmSerializer',[type]'Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer')


#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)

#endregion

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 = $App2Deserializedstuff.DeploymentTypes.name
$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
$operand.Add($intentExpression)

#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
$App1Deserializedstuff.DeploymentTypes[0].Dependencies.Add($DTRUle)


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

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
Cya

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.