Active Directory Group Synchronization HOWTO

Last modified 13 Mar 2024 09:31 +01:00

This is a minimalistic HOWTO to demonstrate synchronization of groups between AD and midPoint.

If you are not experienced using Active Directory resources, please see Active Directory with LDAP connector.

This tutorial consists of two steps:

  1. Basic synchronization of users and groups

  2. Using a meta-role to simplify the solution

Step 1: Basic synchronization of users and groups

Please import the file samples/resources/ad/ad-resource-groups-basic.xml. Its most important parts are shown here:

<c:resource oid="11111111-2222-3333-4444-000000000000">

    <name>Active Directory Group Sync</name>

    ...

    <schemaHandling>

        <!-- handling of user accounts -->

        <objectType>
            <kind>account</kind>
            <objectClass>ri:AccountObjectClass</objectClass>
            <default>true</default>

            ...

            <!-- This defines an association between user and groups he is a member of -->
            <association>
                <ref>ri:group</ref>
                <displayName>AD Group Membership</displayName>
                <kind>entitlement</kind>
                <intent>group</intent>
                <direction>objectToSubject</direction>
                <associationAttribute>ri:member</associationAttribute>
                <valueAttribute>icfs:name</valueAttribute>
                <explicitReferentialIntegrity>false</explicitReferentialIntegrity>
            </association>
        </objectType>

        <!-- handling of groups -->

        <objectType>
            <kind>entitlement</kind>
            <intent>group</intent>
            <objectClass>ri:CustomGroupObjectClass</objectClass>
            <default>true</default>
            <attribute>
                <ref>icfs:name</ref>
                <matchingRule>mr:stringIgnoreCase</matchingRule>
                <outbound>
                    <source>
                        <path>$focus/name</path>
                    </source>
                    <expression>
                        <script>
                            <code>
                                'cn='+name+',ou=Groups,dc=example,dc=com'
                            </code>
                        </script>
                    </expression>
                </outbound>
            </attribute>
            <attribute>
                <ref>ri:cn</ref>
                <matchingRule>mr:stringIgnoreCase</matchingRule>
                <inbound>
                    <target>
                        <path>$focus/name</path>
                    </target>
                </inbound>
            </attribute>
            <attribute>
                <ref>ri:description</ref>
                <outbound>
                    <strength>strong</strength>
                    <source>
                        <path>description</path>
                    </source>
                </outbound>
                <inbound>
                    <strength>weak</strength>
                    <target>
                        <path>$focus/description</path>
                    </target>
                </inbound>
            </attribute>
        </objectType>

    </schemaHandling>

    ...

</c:resource>

The schema handling section defines an account object class (in a traditional way) and then a group object class.Group class definition is similar to the account one - it defines handling of three attributes, namely DN, CN and Description. We can see e.g. that DN is constructed from the role name, suffixed with "ou=Groups,dc=example,dc=com", placing it in example.com/Groups OU. The <association> element in user schema handling defines an association between user account and a group. See Entitlements (section on associations). Besides that, we define synchronization in order to synchronize users and roles/groups between midPoint and AD resource.

<c:resource ...>

    ...

    <!--
        Synchronization section describes the synchronization policy, timing,
        reactions and similar synchronization settings based on correlator rules defined correlation section.
    -->
            <correlation>
                <correlators>
                    <items>
                        <name>personalNumber-correlation</name>
                        <description>Correlation using personalNumber. Please note: inbound mapping for personalNumber is used only during correlation.</description>
                        <enabled>false</enabled>
                        <item>
                            <ref>personalNumber</ref>
                        </item>
                        <composition>
                            <tier>21</tier>
                        </composition>
                    </items>
                    <items>
                        <name>samAccountName-correlation</name>
                        <enabled>true</enabled>
                        <item>
                            <ref>c:name</ref>
                            <search>
                                <matchingRule>polyStringOrig</matchingRule>
                            </search>
                        </item>
                        <composition>
                            <tier>2</tier>
                        </composition>
                    </items>
                </correlators>
            </correlation>
            <synchronization>
                <reaction>
                    <name>set-linked</name>
                    <lifecycleState>active</lifecycleState>
                    <situation>linked</situation>
                    <actions>
                        <synchronize/>
                    </actions>
                </reaction>
                <reaction>
                    <name>set-unlinked</name>
                    <lifecycleState>active</lifecycleState>
                    <situation>unlinked</situation>
                    <actions>
                        <link/>
                    </actions>
                </reaction>
                <reaction>
                    <name>set-unmatched</name>
                    <lifecycleState>active</lifecycleState>
                    <situation>unmatched</situation>
                    <actions>
                        <addFocus/>
                    </actions>
                </reaction>
                <reaction>
                    <name>set-deleted</name>
                    <lifecycleState>active</lifecycleState>
                    <situation>deleted</situation>
                    <actions>
                        <synchronize/>
                    </actions>
                </reaction>
                <reaction>
                    <name>set-disputed</name>
                    <lifecycleState>active</lifecycleState>
                    <situation>disputed</situation>
                    <actions>
                        <createCorrelationCase/>
                    </actions>
                </reaction>
            </synchronization>
        </objectType>
        ...
</c:resource>

Here, the user-related part is written as usual. The new one is group-related part. However, there is nothing special even in this part: it simply says that groups (i.e. ri:CustomGroupObjectClass / kind=entitlement / intent=group) have to be synchronized with roles, and describes reactions to individual situations. Besides this, there are two synchronization tasks defined:

<task oid="11111111-2222-3333-4444-100000000000">
    <name>Synchronization: Active Directory (users)</name>
    <taskIdentifier>11111111-2222-3333-4444-100000000000</taskIdentifier>
    <ownerRef oid="00000000-0000-0000-0000-000000000002"/>
    <executionStatus>runnable</executionStatus>
    <handlerUri>http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/live-sync/handler-3</handlerUri>
    <objectRef oid="11111111-2222-3333-4444-000000000000" type="c:ResourceType"/>
    <recurrence>recurring</recurrence>
    <binding>tight</binding>
    <schedule>
        <interval>5</interval>
    </schedule>
</task>

This one synchronizes users (nothing special here).

<task oid="11111111-2222-3333-4444-100000000001">
    <name>Synchronization: Active Directory (groups)</name>
    <extension>
        <mext:kind xmlns:mext="http://midpoint.evolveum.com/xml/ns/public/model/extension-3">entitlement</mext:kind>
    </extension>
    <taskIdentifier>11111111-2222-3333-4444-100000000001</taskIdentifier>
    <ownerRef oid="00000000-0000-0000-0000-000000000002"/>
    <executionStatus>runnable</executionStatus>
    <handlerUri>http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/live-sync/handler-3</handlerUri>
    <objectRef oid="11111111-2222-3333-4444-000000000000" type="c:ResourceType"/>
    <recurrence>recurring</recurrence>
    <binding>tight</binding>
    <schedule>
        <interval>5</interval>
    </schedule>
</task>

This one synchronizes groups, as indicated by "kind = entitlement" property in an extension. Note that groups are defined as default intent of entitlement kind, so it is not necessary to specify intent here. What this setup does:

  1. AD→midPoint

    • It synchronizes AD accounts and groups from AD to midPoint - i.e. when a new account is created in AD, it appears in midPoint as a corresponding account shadow and a user. When new group is created, it appears in midPoint as a new entitlement shadow and a role.

      You can try it to see if it works.

  2. midPoint→AD

    • It is able to provision users from midPoint to AD: you just have to add or assign a user the corresponding resource account.

    • It is able to provision groups from midPoint to AD.

The second point is a bit more complicated: at minimum, you have to tell the midPoint that the role should be provisioned to AD. It is done by adding the following assignment to the role:

    <assignment>
       <construction>
          <resourceRef oid="11111111-2222-3333-4444-000000000000" type="ResourceType"/>
          <kind>entitlement</kind>
          <intent>group</intent>
       </construction>
    </assignment>

Just like a user can have assigned an account on a resource, a role can have assigned an "account" (a group, in this case) on a resource. What is missing in both cases, is a rule that would say "any user having this role has to have an account on AD with corresponding group assigned". For this, an inducement is used. By using inducements, you can prescribe not only that an account on a particular resource should exist, but you can also set its attributes and/or assignments - and exactly that is what we are interested in: assigning an entitlement (a group) that corresponds to this role. You can use associationTargetSearch, or a less flexible, but perhaps more straightforward way that uses a simple object reference:

    <inducement>
       <construction>
          <resourceRef oid="11111111-2222-3333-4444-000000000000" type="ResourceType"/>
          <kind>account</kind>
          <association>
             <ref>ri:group</ref>
             <outbound>
                <expression>
                   <value>
                      <shadowRef oid="88c95eb4-f2a3-4b63-b269-18696e52c03f"/>
                   </value>
                </expression>
             </outbound>
          </association>
       </construction>
    </inducement>

(note that oid="88c95eb4-f2a3-4b63-b269-18696e52c03f" points to a shadow of this role → i.e. the group we are talking about)Now, when you assign this role to a user, an account for him will be created on a resource, and it will be a member of the given group.MidPoint allows you to avoid all these nuances by using its sophisticated mechanisms, namely:

  • object templates,

  • roles with higher-order inducements (meta roles).

An object template is used to automatically assign a meta role to any role created. A meta role is used to create all the necessary assignments/inducements to that role. This leads us to the second step:

Step 2: Using a meta-role to simplify the solution

See samples/resources/ad/ad-resource-groups-advanced.xml, but do not import it at this moment, as we will import things in it stepwise. First, we create a meta-role that will do exactly the thing we did manually in the above step:

  1. it creates an assignment to an AD group on our resource,

  2. it creates an inducement prescribing creation of user accounts with AD group on the resource.

Note that for the meta-role, item #1 is an inducement (as it creates assignments for any role that possesses this metarole) and item #2 is a second-order inducement (as it creates first-order inducements for any role that possesses this metarole).

<role oid="11111111-2222-3333-4444-200000000001"
       xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
       xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
       xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3"
       xmlns:ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3">

    <name>Metarole for groups</name>

    <!-- This inducement causes creation of AD group for any role that possesses this metarole -->
    <inducement>
        <construction>
            <resourceRef oid="11111111-2222-3333-4444-000000000000" type="c:ResourceType"/>
            <kind>entitlement</kind>
            <intent>group</intent>
        </construction>
    </inducement>


    <!-- This inducement causes creation of AD account that is in AD group for any USER that possesses any role that possesses this metarole -->
    <!-- That's why this is called second-order inducement -->
    <inducement>
        <construction>
            <resourceRef oid="11111111-2222-3333-4444-000000000000" type="c:ResourceType"/>
            <kind>account</kind>
            <intent>default</intent>
            <association>
                <ref>ri:group</ref>
                <outbound>
                    <expression>
                         <associationFromLink>
                             <projectionDiscriminator>
                                 <kind>entitlement</kind>
                                 <intent>group</intent>
                             </projectionDiscriminator>
                         </associationFromLink>
                    </expression>
                </outbound>
            </association>
        </construction>
        <order>2</order>
    </inducement>
</role>

If you import this metarole and create a role (e.g. "r1") having this metarole assigned, you’ll see that on AD a group r1 has been created, and a midPoint shadow for it has been created as well, and linked to group r1. Moreover, if you now create a new midPoint user, and assign him role r1, his account on AD will be created and it will be a member of r1 AD group.

Now, what is missing? If you create a role in midPoint, you have to manually assign it our metarole. Similarly, if a group is created in AD, the corresponding role in midPoint is again without the metarole. Here, an object template is going to help us.

<objectTemplate oid="11111111-2222-3333-4444-300000000001"
                xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
                xmlns='http://midpoint.evolveum.com/xml/ns/public/common/common-3'
                xmlns:c='http://midpoint.evolveum.com/xml/ns/public/common/common-3'
                xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3">
    <name>Role Template</name>

    <mapping>
        <name>Metarole assignment</name>
        <authoritative>true</authoritative>
        <expression>
            <assignmentTargetSearch>
                <targetType>c:RoleType</targetType>
                <oid>11111111-2222-3333-4444-200000000001</oid>             <!-- our meta role -->
            </assignmentTargetSearch>
        </expression>
        <target>
            <path>assignment</path>
        </target>
    </mapping>

</objectTemplate>

Besides creating the template, we have to tell midPoint to use it for roles. We have to include the following to the system configuration:

<objectTemplate>
    <type>c:RoleType</type>
    <objectTemplateRef oid="11111111-2222-3333-4444-300000000001"/>
</objectTemplate>

Now, when you create a role (let’s say r2), it will get automatically assigned the metarole, what causes creation of AD group and automatic assignment of this group to any user that has this "r2" role. In a similar way, when you create a group (let’s say r3) in AD, a role r3 will be created in midPoint and it will be assigned this metarole.

Actually, this example is a way too simplistic. For example, in reality, we would not want to provision all roles (including e.g. Superuser) to the Active Directory resource. So we would probably mark roles that have to be provisioned by some flag (let’s say role type == "replicated") and then use this condition in the object template and in synchronization settings. We skipped this in order to focus on basic principles of synchronization. For a more realistic setting, please see the OrgSync Story Test.

For more information please see:

Thanks to Tim Tompkins for providing a sample AD resource definition from which parts of this HOWTO were taken.

Was this page helpful?
YES NO
Thanks for your feedback