The goal of this document is to provide an explanation of the problem, and to provide steps on how to modify the GalSync source code to correct the problem. 

Table of Contents

Executing a Delta Synchronization, we receive several “Extension-DLL-Exception” synchronization errors. The review of the Synchronization Error displays that we are working with the msExchMasterAccountHistory data source attribute. The review of the stack trace, we discover more information. The stack trace displays the message “Attribute does not have exactly one value.

Stack Trace

Extension File Name: GALSync.dll
Extension Type: export-flow
Extension Context: msExchMasterAccountHistoryMappingForwards

System.InvalidOperationException: attribute does not have exactly one value
at Microsoft.MetadirectoryServices.Impl.AttributeImpl.get_Value()
at Microsoft.MetadirectoryServices.GALSync.MASynchronizer.EAFmsExchMasterAccountHistoryForwards(CSEntry& csentry, MVEntry& mventry)
at Microsoft.MetadirectoryServices.GALSync.MASynchronizer.MapAttributesForExport(String FlowRuleName, MVEntry mventry, CSEntry csentry)

 

Cause

The cause of the extension-dll-exception is a line of code within the Private Sub Routine "EAFmsExchMasterAccountHistoryForwards" in the GalSync source code.
The line of code is working with the msExchMasterAccountHistory and SIDHistory as single value attributes.
Here is the line of code:

csentry(MASTER_ACCOUNT_HISTORY).Value = mventry(SID_HISTORY).Value

This line of code does occur in a couple different places within this sub routine. You will find this line of code one time for the Group Object, and once for the Person Object.

Resolution

In order to fix the “extension-dll-exception” we will need to modify the GalSync solution that is provided with the installation of the Microsoft Identity Lifecycle Manager 2007 Feature Pack 1 product.
Specifically, we will need to modify the "EAFmsExchMasterAccountHistoryForwards" Sub Routine.

To resolve the issue, you perform the following steps:

  1. Change the name of the GalSync Solution to a unique name
  2. Update the build path to not to build in the Extensions folder
  3. Apply code modifications
  4. Update the Identity Manager Console

Changing the name of the GalSync Solution to a unique name

To change the name of the GalSync Solution to a unique name, perform the following steps:

  1. Open Microsoft Visual Studio (2005, 2008 or 2010)
  2. Open the GalSync Solution (%programfiles%\Microsoft Identity Integration Server\Source Code\GalSync)
    1. If you have Forefront Identity Manager 2010 then the path is (%programfiles%\Microsoft Forefront Identity Manager\2010\Synchronization Service\Source Code\GalSync) 
    1. Change the name of the GalSync Solution to a unique name
    2. Update the build path to not to build in the Extensions folder
    3. Apply code modifications
    4. Update the Identity Manager Console

    noteNote
    You may go through a conversion wizard, as the project was originally developed in Visual Studio .NET 2003 using .NET Framework v1.1.
    If you do get the wizard, go through the wizard, and “Load Project Normally”

     

  3. In Solution Explorer, right click on the project name and choose Rename
  4. Give it a unique name (e.g. <company name or company acronym>galsync)
  5. From the Project menu, select unique name galsync properties
  6. Select Application. (it may already be selected by default)
  7. Modify the Assembly Name so that it matches your new solution name.

Updating the build path to not to build in the Extensions folder

  1. Under the Project menu select unique name galsync properties.
  2. Select Compile
  3. Review the “Build output path

     

    noteNote
    You will want to point this to another directory rather than the Extensions folder.
    Maybe create yourself a Build folder, and point to that folder.

 

Applying code modifications

To apply the code modifications, perform the following steps:

  1. Open the GalMA.VB file
  2. Locate the EAFmsExchMasterAccountHistoryForwards sub routine:
  3. Locate the following line of code in both the group section, and the person section and then comment it out:
    csentry(MASTER_ACCOUNT_HISTORY).Value = mventry(SID_HISTORY).Value
    
  4. Add the following lines of code either above or below that line, but inside of the IF...THEN statement.
     Dim SIDHistoryEntry As Value
    For Each SIDHistoryEntry In mventry(SID_HISTORY).Values
       csentry(MASTER_ACCOUNT_HISTORY).Values.Add(SIDHistoryEntry)
    Next
    
  5. To build the solution, select Build Solution for the Build menu.
  6. Copy/Move your newly created DLL to the Extensions folder

Updating the Identity Manager

To update the Identity Manager:

  1. Open Identity Manager.
  2. To open the Options dialog, select Options from the Tools menu.
  3. In the Rules extension name textbox, type the name of the new dll.
  4. To close the Options dialog, click OK.

For each affected management agent, perform the following steps:

  1. In the Management Agents view, select the affected management agent.
  2. To open the Properties dialog, select Properties from the Actions menu.
  3. In the Management Agent Designer, select Configure Extensions.
  4. In the Rules extension name textbox, type the name of the new DLL.
  5. To close the Properties dialog, click OK.

Source Code

This section lists the original and the updated source code.

Original GAL Sync source code

Private Sub EAFmsExchMasterAccountHistoryForwards(ByRef csentry As CSEntry, ByRef mventry As MVEntry)
   ' Variables
   Dim uac As Long

   ' Clear master account history
   csentry.Item(MASTER_ACCOUNT_HISTORY).Delete()

   ' Handle groups
   ' Only security mv group object result in stamping of resulting contact when dealing with GROUPS
   Dim MAConfig As GALMA = FindMA(csentry)
   Dim isTrustEnabled As Boolean = MAConfig.XFDelegation
   If isTrustEnabled AndAlso mventry(SID_HISTORY).IsPresent AndAlso mventry.ObjectType.Equals(GROUP) Then
      Dim isSecurityGroup As Boolean = (mventry.Item(DISTRIBUTION_GROUP_TYPE).Value And SECURITY_GROUP_TYPE_CODE) = SECURITY_GROUP_TYPE_CODE
      If isSecurityGroup AndAlso mventry.Item(MAIL_NICKNAME).IsPresent Then
         csentry(MASTER_ACCOUNT_HISTORY).Value = mventry(SID_HISTORY).Value
      End If
   End If

   If isTrustEnabled AndAlso mventry.ObjectType.Equals(PERSON) AndAlso mventry(USER_ACCOUNT_CONTROL).IsPresent Then
      ' Get enabled user, flow SID hositry to Master Account history
      uac = mventry(USER_ACCOUNT_CONTROL).IntegerValue
      If ((uac And UAC_USER_ACCOUNT) > 0) AndAlso Not mventry(MASTER_ACCOUNT_SID).IsPresent Then
         If mventry.Item(SID_HISTORY).IsPresent Then
            csentry(MASTER_ACCOUNT_HISTORY).Value = mventry(SID_HISTORY).Value
         End If

      ' If disabled user and MasterAccountSID is not set to SELFSID, flow MasterAccountSid to master account history
      ElseIf ((uac And MyBase.UAC_DISABLED_USER) > 0) Then
         If mventry(MASTER_ACCOUNT_SID).IsPresent AndAlso mventry(MASTER_ACCOUNT_SID).Value <> SELFSID Then
            csentry(MASTER_ACCOUNT_HISTORY).Value = mventry(MASTER_ACCOUNT_SID).Value
         End If
      End If
   End If
End Sub

Updated GAL Sync source code

Private Sub EAFmsExchMasterAccountHistoryForwards(ByRef csentry As CSEntry, ByRef mventry As MVEntry)
   ' Variables
   Dim uac As Long

   ' Clear master account history
   csentry.Item(MASTER_ACCOUNT_HISTORY).Delete()

   ' Handle groups
   ' Only security mv group object result in stamping of resulting contact when dealing with GROUPS
   Dim MAConfig As GALMA = FindMA(csentry)
   Dim isTrustEnabled As Boolean = MAConfig.XFDelegation
   If isTrustEnabled AndAlso mventry(SID_HISTORY).IsPresent AndAlso mventry.ObjectType.Equals(GROUP) Then
      Dim isSecurityGroup As Boolean = (mventry.Item(DISTRIBUTION_GROUP_TYPE).Value And SECURITY_GROUP_TYPE_CODE) = SECURITY_GROUP_TYPE_CODE
      If isSecurityGroup AndAlso mventry.Item(MAIL_NICKNAME).IsPresent Then
         ‘ csentry(MASTER_ACCOUNT_HISTORY).Value = mventry(SID_HISTORY).Value
         Dim SIDHistoryEntry As Value
         For Each SIDHistoryEntry In mventry(SID_HISTORY).Values
            csentry(MASTER_ACCOUNT_HISTORY).Values.Add(SIDHistoryEntry)
         Next
      End If
   End If
   
   If isTrustEnabled AndAlso mventry.ObjectType.Equals(PERSON) AndAlso mventry(USER_ACCOUNT_CONTROL).IsPresent Then
      ' Get enabled user, flow SID hositry to Master Account history
      uac = mventry(USER_ACCOUNT_CONTROL).IntegerValue
      If ((uac And UAC_USER_ACCOUNT) > 0) AndAlso Not mventry(MASTER_ACCOUNT_SID).IsPresent Then
         If mventry.Item(SID_HISTORY).IsPresent Then
            ‘ csentry(MASTER_ACCOUNT_HISTORY).Value = mventry(SID_HISTORY).Value
            Dim SIDHistoryEntry As Value
            For Each SIDHistoryEntry In mventry(SID_HISTORY).Values
               csentry(MASTER_ACCOUNT_HISTORY).Values.Add(SIDHistoryEntry)
            Next
         End If
      ' If disabled user and MasterAccountSID is not set to SELFSID, flow MasterAccountSid to master account history
      ElseIf ((uac And MyBase.UAC_DISABLED_USER) > 0) Then
         If mventry(MASTER_ACCOUNT_SID).IsPresent AndAlso mventry(MASTER_ACCOUNT_SID).Value <> SELFSID Then
            csentry(MASTER_ACCOUNT_HISTORY).Value = mventry(MASTER_ACCOUNT_SID).Value
         End If
      End If
   End If
End Sub

Additional Information