Managing AD Terminal Services Configuration with FIM / MIM using the Granfeldt PowerShell Management Agent

Forefront / Microsoft Identity Manager contains numerous Management Agents (MA’s) out of the box. However an MA for managing AD Terminal Services user configuration isn’t one of them. And at first pass you’d think you could just manipulate a few attributes in AD on an AD MA like you do for home directories (aside from creating the file and permissions on the filesystem) and you’d be done. Don’t worry, I made that wrong assumption too.

Overview

In this blog post I’ll document how you can enable Active Directory users with the necessary attributes and file system elements utilising Søren Granfeldt’s extremely versatile PowerShell Management Agent. I’ll show you how to do the minimum of provisioning a terminal services home directory, a terminal services profile directory, update the associated users AD account with those paths and the terminal services drive letter whilst also allowing the user to logon. Understanding how this is done you can then easily then extend the functionality for lifecycle management (e.g. change location of terminal services directories, delete for de-provisioning). For the file system directories and permissions I’ve re-used logic from my AD home director PS MA detailed here which is based on Kent Nordström’s home directory MA stripped back with a few other subtle changes.

My Terminal Services MA is used in conjunction with an Active Directory MA and Declarative Rules in the MIM Portal.

A note on managing Terminal Services Configuration

Terminal Services configuration properties are, historically, held in a single attribute ‘userParameters’ (a BLOB, a Binary Large OBject). Inside the userParameters BLOB are the four attributes we’re interested in;

  • allowLogon
  • terminalServicesHomeDirectory
  • terminalServicesProfilePath
  • terminalServicesHomeDrive

What you see via LDAP in the userParameters attribute is either an empty attribute (no terminal services configuration) or an attribute full of encoded characters. We’ll use this as our trigger for the MIM rules to determine who has an existing configuration and who doesn’t.

Getting Started with the Granfeldt PowerShell Management Agent

In case you haven’t read my other PS MA blog posts here is the background on getting started with the Granfeldt PS MA. If you have you can skip through this section.

First up, you can get it from here. Søren’s documentation is pretty good but does assume you have a working knowledge of FIM/MIM and this blog post is no different. Configuration tasks like adding additional attributes the User Object Class in the MIM Portal, updating MPR’s, flow rules, Workflows, Sets etc are assumed knowledge and if not is easily Bing’able for you to work it out.

Three items I had to work out that I’ll save you the pain of are;

  • You must have a Password.ps1 file. Even though we’re not doing password management on this MA, the PS MA requires a file for this field. The .ps1 doesn’t need to have any logic/script inside it. It just needs to be present
  • The credentials you give the MA to run the scripts as, needs to be in the format of just ‘accountname’ NOT ‘domain\accountname’. I’m using the service account that I’ve used for the Active Directory MA. The target system is the same directory service and the account has the permissions required (you’ll need to add the service account to the appropriate Lync role group for user management)
  • The path to the scripts in the PS MA Config must not contain spaces and be in old-skool 8.3 format. I’ve chosen to store my scripts in an appropriately named subdirectory under the MIM Extensions directory. Tip: from a command shell use “dir /x” to get the 8.3 directory format name. Mine looks like C:\PROGRA~1\MICROS~4\2010\SYNCHR~1\EXTENS~2\TermServ

Schema Script (schema.ps1)

As I’m using the OOTB (out of the box) Active Directory MA to provision the AD account and only showing provisioning of terminal services configuration the schema only consists of the attributes needed to create the terminal services directories, the terminal services drive letter and update the user object in AD.

Password Script (password.ps1)

Just a placeholder file as detailed above.

Import Script (import.ps1)

The import script looks to see if there is a terminal services config for the user (in the userParameters BLOB) and then uses the ADSI provider that allows us to access the terminal services configuration attributes to read and flow through the four attributes we’re interested in.

On the $request line you’ll see I have a filter for the User Object Class and users with an account name starting with ‘U’. For the customer I wrote this for, all users start with U. You can obviously also have Filters on the MA. I have a few there as well, limiting the size of the returned array makes things quicker to run full imports.

Export Script (export.ps1)

The export script is doing a couple of functions. It is creating the terminal services home and profile directories, setting the necessary permissions on the home directory for the user and then updating the AD object with the terminal services home and profile directory paths, the terminal services directory drive letter and allowing the user to logon.

Wiring it all together

In order to wire the functionality all together there are the usual number of configuration steps to be completed. Below I’ve shown a number of the key points associated with making it all work.

Basically create the PS MA, import attributes from the PS MA, add any additional attributes to the Portal Schema, update the Portal Filter to allow Administrators to use the attribute, update the Synchronisation MPR to allow the Sync Engine to flow in the new attributes, create the Set used for the transition, create your Synchronisation Rule, create your Terminal Services Workflow, create your Terminal Services MPR, create your MA Run Profiles and let it loose.

Management Agent Configuration

As per the tips above, the format for the script paths must be without spaces etc. I’m using 8.3 format and I’m using the same service account as my AD MA.

Password script must be specified but as we’re not doing password management it’s empty as detailed above.

If your schema.ps1 file is formatted correctly you can select your attributes.

My join rule is simple. AccountName (which as you’ll see in the Import.ps1 is aligned with sAMAccountName) to AccountName in the MetaVerse.

My import flow is just bringing back in the DN to a custom MV attribute.

Synchronisation Rules

My Terminal Services Outbound Sync rule doesn’t need to be very complex. All it is doing is sync’ing out the Terminal Services Profile and Directory paths, Terminal Services Drive Letter and Allow Logon attributes.

Set

I created a Set that I use as a transition to trigger provisioning to the Terminal Services MA. My Set looks to see if the user account is in AD and active (I have a Boolean attribute not shown and another rule in the MIM Portal that is set based on an advanced flow rule in the Sync engine that has some logic to determine if employment date as sourced from my HR Management Agent is current and the user should be active) and that the Terminal Services Config is not already set (this is also based on a Boolean attribute I set via an Advanced Flow rule on my AD MA based on whether there is a value in the userParameters attribute). See my Exchange mailbox provisioning post using the PS MA for an example of setting a boolean attribute based on attribute being present or not.

Workflow

An action based workflow that is used to trigger the Synchronisation rule on the Terminal Services MA.

MPR

Finally my MPR for provisioning Terminal Services config is based on the transition set,

and my Terminal Services Workflow.

Summary

Using the Granfeldt PowerShell MA it was very easy to provision a terminal services configuration to new users and permission the directories correctly. It is also easy to extend the solution to manage moving terminal services directories to different locations, deletion etc.

 

Follow Darren on Twitter @darrenjrobinson