Last week I did a post about pre-provisioning user applications, based on group membership, during OS deployment, which I already thought was pretty cool. I got some nice positive feedback on that post, including a comment of my very well respected colleague and ConfigMgr MVP Kim Oppalfens. He said, “Nice find, but what if you have twenty applications?”. Well, my idea of last week would mean two task sequence steps per applications, so that’s not really an option for a lot of applications… I had to come up with something better.
The biggest challenge about this is that Orchestrator can only return static variables, it’s not possible to return dynamically named variables from a runbook. This meant that the only option left, to really achieve my new goal, was to manually edit the ZTIExecuteRunbook.wsf –script from MDT to turn a static variable into multiple task sequence variables. So in rough lines my runbook has to return one string with all application groups and MDT has to turn it into multiple task sequence variables.
The prerequisites are the same as last week, but to be complete I will sum them up again. This post is based on one basic functional requirement and that’s, that Active Directory (AD) –groups are used to determine whether a user gets and application or not. For the rest of the post the following technical requirements are prerequisites and not further described:
- The Microsoft Deployment Toolkit 2012 Update 1 –package is created.
- The Nework Access Account is member of OrchestratorUser –group.
- Note: By default the Execute Runbook –step will use the credentials of the Network Access Account to connect with Orchestrator.
- The Active Directory Integration Pack is registered, deployed and configured.
- User device affinity is configured to Allow user device affinity with automatic approval in the PXE –tab of the Distribution Point Properties.
Like last week let’s start with configuring the runbook that, in this case, will check all the group memberships. The biggest challenge about this runbook was to find a good way to return the group memberships. The runbook, that I ended-up with, returns a comma separated string with all groups and contains five activities with the following configuration:
- Add an Initialize Data –activity and double-click it. In the Details Information click Add and a new parameter named Parameter 1 will be added. Now click Parameter 1, change the name to UserName, click Ok and click Finish.
- Add a Get User –activity, link it with the previous activity and double-click it. In the Filters click Add. In the Filter Settings –popup, select as Name Sam Account Name, select as Relation Equals, right-click the field next to Value and select Subscribe > Published Data. In the Published Data –popup, select with Activity Initialize Data, select UserName and click Ok, click again Ok and then click Finish.
- Add a Get Organizational Unit –activity, link it with the previous activity and double-click it. In the Filters click Add. In the Filter Settings –popup, select as Name Organizational Unit, select as Relation Equals, set as Value <aOUName>, click Ok and then click Finish.
- Note: This is an optional activity that I use for the next activity to only get specific groups used for application distribution. An other option might be to check on specific group names, or anything that makes the groups differ from other groups.
- Add a Get Group –activity, link it with the previous activity and double-click it. In the Properties click Optional Properties…. In the Add/Remove Property –popup select Search Root, click >> and click Ok. Right-click the field next to Search Root and select Subscribe > Published Data. In the Published Data –popup, select with Activity Get Organizational Unit, select Distinguished Name and click Ok. Then select Filters and click Add. In the Filter Settings –popup, select as Name Member, select as Relation Equals, right-click the field next to Value and select Subscribe > Published Data. In the Published Data –popup, select with Activity Get User, select Distinguished Name, click Ok and click again Ok. Then select Run Behavior, select Flatten and click Finish.
- Add an Return Data –activity link it with the previous activity and double-click it. In the Details right-click the field next to APPId and select Subscribe > Published Data. In the Published Data –popup, select with Activity Get Group, select Sam Account Name, click Ok and click Finish.
- Note: To get the APPId String in the Return Data –activity go to the Properties of the runbook and add it in the Returned Data. Also keep in mind that the runbook has to return APPId, to be functional, as the MDT –script (ZTIExecuteRunbook.wsf) will specifically check for that (see the next paragraph for an explanation).
Now let’s take a look at the MDT –script (ZTIExecuteRunbook.wsf) that is used to execute a runbook via a task sequence. By default this script will just get the returned data from the runbook and make it into a task sequence variable, via this line of code:
oEnvironment.Item(sName) = sValue
As I wanted to keep the default behavior of the script, I adjusted the script to check for a specific name (APPId) in the returned data of the runbook. When the script finds it, the script will cut its comma separated value into pieces and create a new task sequence variable for every piece. These variables are based on the task sequence base variable (see the next paragraph) and can be used, in the task sequence, to install applications according to a dynamic variable list. This all means that I replaced the previous line of code for the following code snippet (don’t forget to update the Distribution Points after updating the script!):
If StrComp(sName, "APPId") = 0 Then aAppGroups = Split(sValue,",") For i = 0 To UBound(aAppGroups) sValue = aAppGroups(i) If Len(i)=1 Then sAppId = "0" & i+1 Else sAppId = i+1 End If sAppName = sName & sAppId oEnvironment.Item(sAppName) = sValue Next Else oEnvironment.Item(sName) = sValue End If
Download the modified script here: ZTIExecuteRunbook.wsf
Now let’s start with configuring the task sequence. The task sequence needs three adjustments (five steps), one to set the User Device Affinity, one to execute the runbook and one to install the applications based on a base variable. For these adjustments follow the next steps:
- Add a Set Task Sequence Variable –step, set Task Sequence Variable to SMSTSAssignUsersMode and set Value to Auto.
- Add a Set Task Sequence Variable –step, set Task Sequence Variable to SMSTSUdaUsers and set Value to <DomainName>\%PrimaryUser%.
- Note: PrimaryUser is a variable that I use to set the primary user for a device (User Device Affinity) and to find the group memberships. There are many methods to set this variable and during this testing I used a computer variable for that.
- Add an Use Microsoft Deployment Toolkit Package –step and Browse to the Microsoft Deployment Toolkit 2012 Update 1 –package.
- Add and Execute Runbook –step, fill in with Orchestrator Server <anOrchestratorServer> and Browse with Runbook to the just created runbook. Then select Specify explicit runbook parameters, fill in with UserName %PrimaryUser%, and select Wait for the runbook to finish before continuing.
- Add an Install Application –step, select Install applications according to dynamic variable list, fill in with Base variable name APPId and click Ok.
- Note: APPId is the variable that is returned by the runbook of the previous step. After the modifications to the ZTIExecuteRunbook –script it captures that variable and turns it’s value into multiple task sequence variables. Those variables now all contain the base variable name plus a number suffix starting at 01.
Like last week, after all the configuring it’s now time to take a look at what the results are when the task sequence is done. This time I will show the results of how the application groups are passed on from the runbook to the task sequence. The first picture shows the application groups in the Output Parameters of the runbook in the Orchestrator Console. Then the second picture shows how the Execute Runbook –step (ZTIExecuteRunbook.log) processes the multiple groups into multiple task sequence variables and at last the third picture shows how the Install Application –step (SMSTS.log) processes the task sequence variables into applications that have to be installed.
In case the name of the groups are not equal to the applications, that need to be installed, and it’s not possible to easily extract the application name from the group name, then it might be necessary to use a Map Published Data –activity in the runbook. This way it can easily turn a group name into an application name, but the biggest down-side is that it will be a static list.then.
To end this post, I would like to thank my colleague Bart Timmermans for helping me better understand the (un-)logics of Orchestrator.