A recent requirement for a project was to have users and groups provisioned from Domain B to Domain A. Simple enough, but a catch was that, as applications were migrated to Domain A, their groups would be “owned” by Domain A, which would now be the authoritative source for all group attributes (in this case, the authoritative source is determined by the OU the group is in in Domain B. The name of this OU is stored in the rules extension configuration file).
A custom rules extension is used to determine which management agent is authoritative, and to be sure the user objects being added to the member attribute are from the appropriate domain.
Setting an advanced attribute flow from member to member gives this message:
“Defining a rules extension import attribute flow to a metaverse reference attribute is not allowed.”
But, there is a way around this.
Here’s the set up:
– Domain A: This is the domain of the parent organization.
– Domain B: This is the domain of the organization being merged into the parent organization.
– The security groups all begin life in Domain B. As the merger progresses, their authoritative source will change to Domain A on a group by group basis.
This is indicated when the group is manually moved to the OU called “Domain A Managed” in Domain B.
Here’s the solution. Code for each step will follow this summary:
- Create a new indexed string multi-valued attribute in the metaverse called memberString.
- Create a new attribute called SourceCSEntryDN to hold the DN value in Domain B for the security group. (This is used to look up the OU the group belongs to on Domain B when the Domain A MA is synching the group.)
- Write a function in the rules extension code to determine which OU the group is in.
- Create an advanced import attribute flow on both management agents to flow members to the new memberString attribute if that MA is authoritative. This value will have the DN of the users who are members of the group.
- Write an export attribute flow rule on both management agents to flow members from the memberString to the member attribute if that MA is authoritative. Part of this function is to find the connector for the appropriate domain on the mventry for the user CN that is in the memberString attribute.
For steps 4 and 5, the check in step 3 is performed to make sure the right attribute values are being treated as authoritative.
For shared code, a general Utils dll called “UtilsForFIM” was created. Code was also added the import and export attribute flow rules in the respective rules extensions.
For step 2, the attribute flow on the Domain B management agent is:
<dn>, cn ->SourceCSEntryDN
This code puts the entire DN of the user object in the SourceCSEntryDN attribute.
Here’s the code for step 2:
Case “cd.group:<dn>,cn->mv.group:SourceCSEntryDN”
Dim sDNPath As String
Dim dn As ReferenceValue = csentry.DN
sDNPath = (dn.Subcomponents(0, dn.Depth).ToString())
mventry(“SourceCSEntryDN”).Value = sDNPath
For step 3, the code is a bit different for the Domain A and Domain B management agents. The Domain A management agent relies on the SourceCSEntryDN whereas the Domain B management agent is able to access the dn from the csentry directly.
Both have an attribute flow as follows on the management agent:
<dn>,member -> memberString
This code checks if the dn of the group contains the OU that indicates the group should be managed by Domain A. There are different functions for each domain. Domain A needs to rely on the value that was put into the SourceCSEntryDN attribute, whereas Domain B can read the DN directly from the csentry object.
Here’s the code for step 3 for Domain A:
Public Shared Function IsGroupManagedByDomAByDN(ByVal sDNPath As String) As Boolean
Dim bIsGroupManagedByDomA As Boolean
Dim sDomAManagedGroupOU As String
bIsGroupManagedByDomA = False
sDomAManagedGroupOU = UtilsForFIM.CommonUtils.ReadAppSetting(“FIMConfig/General”, “DomAManagedGroupOU”).ToString()
If (InStr(sDNPath.ToLower(), sDomAManagedGroupOU.ToLower())) < 1 Then
‘Group is still managed by the acquisition domain.
bIsGroupManagedByDomA = False
Else
‘The group is now managed by DomA.
bIsGroupManagedByDomA = True
End If
Return bIsGroupManagedByDomA
End Function
Here’s the code for step 3 for Domain B:
Public Shared Function IsGroupManagedByDomA(ByVal csentry As CSEntry) As Boolean
Dim bIsGroupManagedByDomA As Boolean
Dim dn As ReferenceValue = csentry.DN
Dim sDomAManagedGroupOU As String
bIsGroupManagedByDomA = False
sDomAManagedGroupOU = UtilsForFIM.CommonUtils.ReadAppSetting(“FIMConfig/General”, “DomAManagedGroupOU”).ToString()
Dim sDNPath As String
sDNPath = (dn.Subcomponents(0, dn.Depth).ToString())
If (InStr(sDNPath.ToLower(), sDomAManagedGroupOU.ToLower())) < 1 Then
‘Group is still managed by the acquisition domain.
bIsGroupManagedByDomA = False
Else
‘The group is now managed by DomA.
bIsGroupManagedByDomA = True
End If
Return bIsGroupManagedByDomA
End Function
For both domains, the code is reading the members in the group and writing the dn value of the users into a multivalued attribute called memberString. Here’s the attribute flow for Step 4 and the code:
<dn>,member -> memberString
The code for this transformation for the Domain A management agent is:
Case “cd.group:<dn>,member->mv.group:memberString”
If (mventry(“SourceCSEntryDN”).IsPresent) Then
Dim bIsGroupManagedByDomA As Boolean = UtilsForFIM.CommonUtils.IsGroupManagedByDomAByDN(mventry(“SourceCSEntryDN”).Value)
If (bIsGroupManagedByDomA = True) Then
mventry(“memberString”).Values.Clear()
Dim i As Integer
For i = 0 To (csentry(“member”).Values.Count – 1)
Dim sMember As String = csentry(“member”).Values.Item(i).ToString()
mventry(“memberString”).Values.Add(sMember)
Next
End If
End If
The code for this transformation when the management agent is Domain B is:
Case “cd.group:member->mv.group:memberString”
Dim bIsGroupManagedByDomA As Boolean
bIsGroupManagedByDomA = UtilsForFIM.CommonUtils.IsGroupManagedByDomA(csentry)
If (bIsGroupManagedByDomA = False) Then
mventry(“memberString”).Values.Clear()
Dim i As Integer
For i = 0 To (csentry(“member”).Values.Count – 1)
Dim sMember As String = csentry(“member”).Values.Item(i).ToString()
mventry(“memberString”).Values.Add(sMember)
Next
End If
For step 5, both management agents have the following attribute flow rule:
member <- memberString
Here is the export attribute flow rule for Domain A:
Case “cd.group:member<-mv.group:memberString”
Dim bIsGroupManagedByDomA As Boolean
If (mventry(“SourceCSEntryDN”).IsPresent) Then
bIsGroupManagedByDomA = UtilsForFIM.CommonUtils.IsGroupManagedByDomAByDN(mventry(“SourceCSEntryDN”).Value)
If (bIsGroupManagedByDomA = False) Then
csentry(“member”).Values.Clear()
UtilsForFIM.CommonUtils.memberStringToMembers(mventry, csentry, csentry.MA.Name)
End If
Else
‘Source csentry dn is missing
End If
And for Domain B:
Case “cd.group:member<-mv.group:memberString”
Dim bIsGroupManagedByDomA As Boolean
bIsGroupManagedByDomA = UtilsForFIM.CommonUtils.IsGroupManagedByDomA(csentry)
If (bIsGroupManagedByDomA = True) Then
csentry(“member”).Values.Clear()
UtilsForFIM.CommonUtils.memberStringToMembers(mventry, csentry, csentry.MA.Name)
End If
Here’s the memberStringToMembers function that translates the membership from the string value to the appropriate csentry for the domain the group is being exported to. Note that this is taking the connector on the mventry of the member for the MA that is processing the current run so that we have the user object in the appropriate domain.
Public Shared Sub memberStringToMembers(ByVal mventry As MVEntry, ByVal csentry As CSEntry, ByVal sMAName As String)
Dim i As Integer
For i = 0 To (mventry(“memberString”).Values.Count – 1)
Dim sMember As String = mventry(“memberString”).Values.Item(i).ToString()
‘Need to find the mventry for this connector and then determine the DomA csentry.
Dim mvEntryMember() As MVEntry
Dim arrMVEntryCN As Array
Dim sMemberCN As String
arrMVEntryCN = sMember.Split(“,”)
sMemberCN = arrMVEntryCN(0).Replace(“CN=”, “”).Replace(“cn=”, “”)
mvEntryMember = Utils.FindMVEntries(“cn”, sMemberCN, 1)
If (mvEntryMember.Length > 0) Then
Dim ManagementAgent As ConnectedMA
Dim Connectors As Integer
ManagementAgent = mvEntryMember(0).ConnectedMAs(sMAName)
Connectors = ManagementAgent.Connectors.Count
If (Connectors > 0) Then
Dim csentryFound As CSEntry
csentryFound = ManagementAgent.Connectors.ByIndex(0)
csentry(“member”).Values.Add(mvEntryMember(0).ConnectedMAs.Item(sMAName).Connectors.ByIndex(0).DN.ToString().ToLower())
End If
End If
Next
End Sub