GALSYNC Troubleshooting: Synchronization Error "Attribute Does Not Have Exactly One Value"
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.
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:
- Change the name of the GalSync Solution to a unique name
- Update the build path to not to build in the Extensions folder
- Apply code modifications
- 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:
Open Microsoft Visual Studio (2005, 2008 or 2010)
Open the GalSync Solution (%programfiles%\Microsoft Identity Integration Server\Source Code\GalSync)
- If you have Forefront Identity Manager 2010 then the path is (%programfiles%\Microsoft Forefront Identity Manager\2010\Synchronization Service\Source Code\GalSync)
-
Note
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”
In Solution Explorer, right click on the project name and choose Rename
Give it a unique name (e.g. <company name or company acronym>galsync)
From the Project menu, select unique name galsync properties
Select Application. (it may already be selected by default)
Modify the Assembly Name so that it matches your new solution name.
Updating the build path to not to build in the Extensions folder
Under the Project menu select unique name galsync properties.
Select Compile
Review the “Build output path”
Note
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:
Open the GalMA.VB file
Locate the EAFmsExchMasterAccountHistoryForwards sub routine:
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
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
To build the solution, select Build Solution for the Build menu.
Copy/Move your newly created DLL to the Extensions folder
Updating the Identity Manager
To update the Identity Manager:
- Open Identity Manager.
- To open the Options dialog, select Options from the Tools menu.
- In the Rules extension name textbox, type the name of the new dll.
- To close the Options dialog, click OK.
For each affected management agent, perform the following steps:
- In the Management Agents view, select the affected management agent.
- To open the Properties dialog, select Properties from the Actions menu.
- In the Management Agent Designer, select Configure Extensions.
- In the Rules extension name textbox, type the name of the new DLL.
- 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