TechNet Magazine > Home > Issues > 2009 > July >  Managing Active Directory Users with ILM 2007
Identity and Access Management
Managing Active Directory Users with ILM 2007
John McGlinchey
 
At a Glance:
  • Understanding the elements of ILM 2007
  • Provisioning and deprovisioning
  • Building a rules extension for import and export
  • Using run profiles to implement changes

Microsoft Identity Lifecycle Manager (ILM) 2007 is a key tool that enables enterprise administrators to keep users, computers, and other directory objects in sync among diverse directories and databases. Users, computers, groups, and even the OU structure of Active Directory can be integrated with LDAP, SQL Server, and other databases. Attributes can be taken from different sources, merged into an authoritative identity, and provided back to all data sources. With ILM, an enterprise need not design a single authoritative basis for identity but can instead integrate a mesh of diverse data sources with a tailored set of rules that can mimic the processes and procedures of that enterprise.
Some of the most common ILM usage scenarios include synchronizing multiple Active Directories, updating a common Exchange Global Address List (GAL) using multiple Active Directory domains or forests, and synchronizing accounts between LDAP implementations or between LDAP and Active Directory. I have also used ILM to migrate from one or more Active Directories to a new domain in a new forest and for operating system and application provisioning using System Center Configuration Manager (SCCM).
The key concepts and methods of ILM can be an uphill learning experience for infrastructure-oriented administrators with limited development know-how. Not only do you need to know something about ILM and developing ILM extension code, you also need to understand the connected data sources like Active Directory, Exchange, LDAP, SQL Server, and so on. This sort of complexity needs to be broken into small, consumable pieces, which is what I will try to do in this article.

What Is ILM?
ILM 2007 combines Identity Management and Certificate Management that together provide key elements of the Identity and Access Management (IDA) model. The identity management part of ILM was previously known as MIIS (Microsoft Identity Integration Server) and, before that, MMS (Microsoft Meta-Directory Server). (The certificate management part, where the lifecycle of certificates and smart cards are managed, won't be covered in this article, but it can be integrated with the identity management). While identity management may have had an identity crisis of its own, it doesn't have a problem managing your identities.
A key feature of IDA with ILM is its extensibility. If there isn't a simple, clickable way to do something, you can write code to extend the functionality of ILM. Though some simple ILM implementations don't need any code at all, most need to do some sort of provisioning of either user accounts or groups, so you're probably destined to write some code. Don't worry, though. ILM provisioning code is pretty straightforward once you have the key concepts down and understand when and how your code plays into the big picture of IDA.

ILM Elements
As with any complex undertaking, you need to understand the important concepts before diving in.
The Metaverse and Connected Directories The metaverse (MV) is simply a database. It's a pretty complex database, but still just a database. ILM uses the MV to store the configuration of all the connected directories (CDs) that import data into or export data out from the MV. Be warned—do not dig into the MV database. And never mess with it. Do not try to run reports directly from the MV database or change anything in it. You will just end up corrupting it. (I speak here from experience.)
Management Agents Management Agents (MAs) are the tools you use to define CDs. You create a rule for importing and exporting data into the MV from a CD using the MA import and export rules. This rule also defines which MA contributes which attribute and how data is purged from the MV once it disappears from all CDs. TechNet has downloadable scenario documents and rule sets that you can use to get started quickly once you understand how it all works. Once you've mastered simple scenarios, then more complex solutions that involve many data sources, complex data manipulations, and detailed rules can be implemented using ILM.
MAs can filter out data from the CD that isn't needed in order to make the connection more efficient. For example, you may not need to scan every OU in an Active Directory. Selecting only the necessary OUs can make scanning for changed or new objects much faster. In a connected SQL Server database, filtering out unnecessary data can save a lot of time when processing import rules (although an even more efficient method is to use a SQL Server view to provide only the necessary data to the MA in the first place).
Connector Space, Importing, and Exporting Each MA connects the MV through a unique connector space (CS) that mirrors the data elements of the CD. Data is then moved to or from the CS to the MV based on the import and export rules. Creating a new object in a connected directory is a simple matter of creating a connector in the CS for that CD and then exporting that object to the CD.
The example in Figure 1 shows an MA connected to an Active Directory. This is the easiest and most common MA that can be configured. Figure 2 shows the attributes that are flowing into and out of the MV. The arrows define the direction of data flow. Note that not all the attributes are selected, only the ones that are deemed necessary to implement the data flow model. The data is flowed as-is with no changes unless there is an import or export extension.
Figure 1 A management agent connects ILM to a connected directory, in this case, an Active Directory, with the ability to select specific organizational units.
Figure 2 The attribute flow defines how each attribute will import into and export out of ILM.
During attribute flows, you may come across an attribute that is being imported from multiple CDs. If the attribute occurs in more than one CD, you may need to decide which CD will contribute the attribute. This is called attribute precedence.

Joining and Projecting Joining means connecting an entry in a connector space to an existing MV object. The criteria for the join rule are totally up to you. If you are joining users from a database into the MV, you might use employee numbers or some combination of names and numbers. You need to use something that makes a unique connection between the CD and the MV within the CS. Remember that the join criteria must be unique or the join rule will cause an error. That said, ILM is totally extensible and if you wanted to, you could write code to decide which of the non-unique entries to join.
Projecting means pushing data into the MV from a Connected Directory's Connected Space. To do this, you need to create a projection rule (see Figure 3), which involves just a few clicks of the mouse. Provisioning, which we'll get to in a minute, means to push data into the CS of a CD from the MV, and this definitely involves more than a few mouse clicks. Think of it this way: you "project into" the MV and you "provision out" of the MV. You can project without writing code, but to provision, you will have to write some code. That's not to say you can't implement a data flow that doesn't do any provisioning. If all of your objects exist already and you just want to make sure the attributes stay in sync, you might not need any code. Just configure all the import and export data flows and join rules and you're nearly good to go.
Figure 3 Join and projection rules are used to define how objects are associated with or created in ILM.
An MA can have multiple join rules, each with multiple join criteria, but there can only be one projection rule.

Provisioning and Deprovisioning
The code in Figure 4 shows an example of what you need to provision a new user in Active Directory. How did we figure out that we needed to create a new user? In this particular case, we got the information from an H/R system, but it could just as well have come from something as simple as a spreadsheet or text file with new users' information and an MA set up to import those users into the MV. ILM is very flexible and can import data from a variety of sources. For now, let's stick to Active Directory. The example code used here is derived from MIIS 2003 Scenarios available for download from "Microsoft® Identity Integration Server 2003 Scenarios."
''   Copyright (c) Microsoft Corporation.  All rights reserved.

Imports Microsoft.MetadirectoryServices
Imports System.Xml

Public Class MVProvision
    Implements IMVSynchronization

    '
    ' These two variables are initialized based on a xml configuration file
    ' The values are read during the Initialize() method of the Rules Extension
    '
    Dim fabrikamUsersContainer As String
    Dim fabrikamDisabledUsersContainer As String

    '
    ' Number of retries on name conflict
    '
    Private Const RETRY_NUM_LIMIT = 1000

    Public Sub Provision( _
        ByVal mventry As MVEntry) _
        Implements IMVSynchronization.Provision

        Dim employeeStatus As String
        Dim ADMA As ConnectedMA
        Dim dn As ReferenceValue
        Dim container As String
        Dim rdn As String
        Dim myConnector As CSEntry
        Dim csentry As CSEntry
        Dim numADConnectors As Integer
        Dim successful As Boolean = False
        Dim cnForObject As String
        Dim numberToAppend As Integer = 1

        If Not mventry.ObjectType.Equals("person") Then
            Exit Sub
        End If

        If Not mventry("cn").IsPresent Then
            Throw New UnexpectedDataException("cn does not exist on MV bject")
        End If

        ADMA = mventry.ConnectedMAs("ADMA")
        '
        ' Get the cn attribute from MV which will be used to configure CS dn
        '
        cnForObject = mventry("cn").Value.ToString()

        '
        ' Based on the value of "employeeStatus" determine the container in AD
        '
        employeeStatus = mventry("employeeStatus").Value.ToLower
        Select Case employeeStatus
            Case "active"
                container = UsersContainer
            Case "inactive"
                container = DisabledUsersContainer
            Case Else
                '
                ' employeeStatus must be active or inactive to be valid
                ' any other case is an error condition for this object. 
                ' Throw an exception to abort this object's synchronization. 
                '
                Throw New UnexpectedDataException("employeeStatus=" + employeeStatus.ToString)
        End Select

        Do
            Try

                ' Based on the value of "cn" determine the RDN in AD
                rdn = "CN=" & cnForObject

                ' Now construct the DN based on RDN and Container
                dn = ADMA.EscapeDNComponent(rdn).Concat(container)

                '
                ' If there is no connector present, add a new AD connector
                ' and call a subroutine to set the initial values on the CS Object
                '
                numADConnectors = ADMA.Connectors.Count
                If 0 = numADConnectors Then

                    csentry = ADMA.Connectors.StartNewConnector("user")
                    csentry.DN = dn
                    SetInitialValues(csentry, mventry)
                    csentry.CommitNewConnector()
                ElseIf 1 = numADConnectors Then
                    '
                    ' check if the connector has a different DN and rename if necessary
                    ' First get the connector
                    '
                    myConnector = ADMA.Connectors.ByIndex(0)

                    '
                    ' MMS will rename/move if different, if not nothing will happen
                    '
                    myConnector.DN = dn
                Else
                    Throw New UnexpectedDataException("multiple AD connectors:" + 
                      numADConnectors.ToString)
                End If

                successful = True

            Catch ex As ObjectAlreadyExistsException

                '
                ' There is a duplicate object in the target AD, 
                ' change the cn accordingly to avoid conflict
                '
                cnForObject = mventry("cn").Value & " (" _
                                & numberToAppend.ToString & ")"
                numberToAppend = numberToAppend + 1

                If numberToAppend > RETRY_NUM_LIMIT Then
                    Throw New UnexpectedDataException( _
                        "Retry for " & mventry("cn").Value _
                        & " exceeds limit " & numberToAppend.ToString)
                End If

            Finally ' Add cleanup code in the Finally section

            End Try

        Loop While Not successful

    End Sub
    ' Set Values on a NEW provisioned CS Entry
    Private Sub SetInitialValues( _
    ByRef csentry As CSEntry, _
    ByVal mventry As MVEntry)

        csentry("unicodepwd").Values.Add(mventry("employeeID").Value)

    End Sub

    Public Function ShouldDeleteFromMV( _
        ByVal csentry As CSEntry, _
        ByVal mventry As MVEntry) _
        As Boolean Implements IMVSynchronization.ShouldDeleteFromMV

        Throw New EntryPointNotImplementedException
    End Function

    Public Sub Initialize() Implements IMVSynchronization.Initialize

        Const SCENARIO_XML_CONFIG = "\simpleprov.xml"

        Dim config As XmlDocument = New XmlDocument
        Dim dir As String = Utils.ExtensionsDirectory()
        config.Load(dir + SCENARIO_XML_CONFIG)

        Dim rnode As XmlNode = config.SelectSingleNode(
           "rules-extension-properties/account-provisioning/container")
        Dim node As XmlNode = rnode.SelectSingleNode("root")
        Dim rootContainer As String = node.InnerText

        node = rnode.SelectSingleNode("enabled-users")
        UsersContainer = node.InnerText + "," + rootContainer

        node = rnode.SelectSingleNode("disabled-users")
        DisabledUsersContainer = node.InnerText + "," + rootContainer

    End Sub

    Public Sub Terminate() Implements IMVSynchronization.Terminate
    End Sub
End Class
In essence, the code checks to see if a user already exists. If the user doesn't exist, a new connector is created for the user, as shown in Figure 5. If the user does exist, there would already be a connector from Active Directory in the CS, meaning that ILM does not need to query Active Directory itself, just the CS of the MA that connects to Active Directory. The export rule defines the data that is going to flow to Active Directory, so the provisioning code need only create the connector and let the flow rules do the rest. One thing to remember, if you do push data within provisioning code, that data is pushed only when the connector is created, not on every sync cycle as with data flow set up using the flow rules.
'
                ' If there is no connector present, add a new AD connector
                ' and call a subroutine to set the initial values on the CS Object
                '
                numADConnectors = ADMA.Connectors.Count
                If 0 = numADConnectors Then

                    csentry = ADMA.Connectors.StartNewConnector("user")
                    csentry.DN = dn
                    SetInitialValues(csentry, mventry)
                    csentry.CommitNewConnector()

                ElseIf 1 = numADConnectors Then
                    '
                    ' check if the connector has a different DN and rename if necesarry
                    ' First get the connector
                    '
                    myConnector = ADMA.Connectors.ByIndex(0)

                    '
                    ' MMS will rename/move if different, if not nothing will happen
                    '
                    myConnector.DN = dn
                Else
                    Throw New UnexpectedDataException("multiple AD connectors:" + 
                      numADConnectors.ToString)
                End If

                successful = True
To configure provisioning, you will have to take your provisioning code and use Visual Studio to build it into a DLL. The name you give to the DLL doesn't much matter, but you need to put the DLL into the extensions folder where ILM is installed and then use its name to configure provisioning. One thing to remember is that provisioning happens on the MV, not on the individual MAs. The code runs whenever an MA trips over something that needs to be provisioned, but provisioning is not part of the MA configuration; it is part of the overall ILM Identity Manager configuration, as shown in Figure 6. One good troubleshooting step when experiencing errors during MA runs is to temporarily disable provisioning, thereby isolating the testing to just the import and export rules and not your provisioning code. Once you confirm that your import and export rules are working, then you can start troubleshooting your provisioning code to find the problem.
Figure 6 Using a Custom Rules Extension to Define Provisioning
Deprovisioning is the process of disconnecting an object from the MV. There are three choices for specifying an object's status:
  1. Making an object a "disconnector" means that the object is no longer joined, but it is not removed from the CD. If it is not filtered out, it may well be end up right back in the CS on the next import.
  2. Making an object an "explicit disconnector" similarly means the object is no longer joined and is not removed from the CD, but in this case it is also marked to not be reimported.
  3. Deleting the object from the CD means exactly that. The object will be deleted from both the CS and CD on the next export run.
Be very careful with your deprovisioning choices as this can be a major headache if not done properly and, even worse, you may destroy data by selecting the wrong option. Always do your testing in a lab environment with good backups.

Import and Export Extensions
At some point, you will probably find the need to do something with CD data while importing or exporting, like merging multiple attributes together to form a full name or connecting username with domain name to form an e-mail address. When this happens, it's time to start building a rules extension, which is essentially code that enables complex changes. On import, a rules extension means you can take multiple CD attributes as input to a single MV attribute. On export, it means you can bring multiple MV attribute into a single CD attribute. Note that the common element here is that the source of the data flow can use multiple attributes but the target of the data flow must be a single attribute. Again, this is pretty simple as far as code goes, and the resulting DLL will need to be placed into the extensions folder. You then use that name in defining your rules extension within the MA configuration (see Figure 7) as well as within the code to define the rule that gets implemented.
Figure 7 Custom attribute flow rules extensions can be written for complex attribute fl ows.

Run Profiles
Once you have MAs set up to import and export data, you have to tell ILM to run an import, an export, or a synchronization. To do this, you set up a run profile for each of these operations within the MA configuration and then run one of them at a time to get the data to move through the proper steps from CD to CV to MV and then from MV to CV to CD (see Figure 8).
Figure 8Data flows from connected directories to the metaverse through the connector space and back out again.
The operations can be either full, which inspects everything, or delta, which inspects only those objects that have been changed. So for a full import, ILM will inspect every object in the connected directory and import everything into the connector space. A full export does the reverse, from CS to CD. A full sync will sync every object in the MV to every connected object, while a delta sync syncs only the changed objects. When enabled, provisioning gets triggered during the sync and decides if new connectors are needed in a connected directory by inspecting the CS for that CD.
To run a profile, you right-click on an MA, select Run, and then choose a profile. Multiple steps can be combined into a single profile. For example, I typically set up a "Full Import & Full Sync" profile on every MA as well as "Delta Import & Delta Sync" and "Export" profiles. Profiles can also be run from command files using a tool called runMA.vbs, available for free download in the Scenarios mentioned earlier.
ILM MA profiles are typically run sequentially from a batch script within the Windows Scheduler Service. Yes, it seems like a throwback, but though things will probably change in the future, for now you should just build a simple script to run your MAs using runMA. Most directory data doesn't change in real time anyway. Use the task scheduler on the ILM server to run this script as often as you need to keep things up to date. Remember that MAs may need to be run in a specific order based on your data sync model and can have a significant impact on network utilization, so balance the need for data synchronization with the possibly conflicting need for a minimal impact on the network.

The Future
ILM will be getting an upgrade in the near future that will bring a new, Web-based interface, workflow integration, provisioning using a Web-based UI—and another name change. ILM is being adopted into the Forefront family of security and access management products and will soon be known as Forefront Identity Manager (FIM) 2010. Look for articles here on FIM 2010 as it gets closer to release to see how you can apply the lessons learned in this article to the next level of Identity and Access Management.

John McGlinchey, MCP, MCSA, MCSE is a Senior Consultant for Microsoft Consulting Services based in the Philadelphia, PA area, focusing on Identity and Access Management. John has more than 30 years of experience in the computer industry, including more than 20 years of consulting experience. He blogs at blogs.technet.com/johnmcg/, and he is one of the IDAGUYS at blogs.technet.com/idaguys/ who regularly blog about IDA topics. Go figure.

Page view tracker