GALSync and DirSync in harmony

It is quite typical to have either a multiple-forest directory synchronisation solution, or a directory synchronisation solution, in an organisation that also performs some type of Global Address List (GAL) synchronisation, a.k.a. GALSYNC.  Forefront Identity Manager (FIM) provides an out-of-box solution for GALSYNC that many Exchange Server customers utilise today.  Introduce Office 365 into the mix and you’ll be implementing a solution that Microsoft call Directory Synchronization, or DIRSYNC.

DIRSYNC is commonly achieved using the Directory Synchronization appliance – a service that runs on-premises and “replicates” Active Directory Domain Services (AD DS) objects into the cloud, specifically into Windows Azure Active Directory (AAD).  DIRSYNC is basically FIM, with some serious operational efficiency wrapped around it, however the DIRSYNC appliance doesn’t work in a multi-forest/multi-Exchange organisation topology, therefore the solution is handled by FIM (but a separate instance of FIM to that doing the GALSYNC as DirSync and GALSync cannot really exist on the same box).

Customers with multiple on-premises AD DS forests with two or more Exchange Server organisations will be synchronising the GALs between organisations.  When it comes to synchronising these on-premises identities into AAD there are two typical configurations that I’m discussing in the context of this post:

  • One side of the GALSYNC is moving to Office 365 (figure 1)
  • All forests are moving to Office 365 (figure 2)

image

Figure 1: DIRSYNC and GALSYNC

image

Figure 2: Multi-forest DIRSYNC (FIM) and GALSYNC

When moving on-premises mailboxes to the cloud, a.k.a. Exchange Online (EXO), there exist a couple of problems:

  1. If an on-premises user emails a recipient in one of the external Exchange organisations using a contact in the local GAL and is migrated to Exchange Online (EXO) the Outlook Recipient cache can break if the GAL in EXO is built from the real on-premises objects and not the synchronised contact objects.
  2. The inverse is also true.  If an EXO user is off-boarded, i.e. moved back to on-premises, the recipient cache won’t be able to locate the EXO object using the on-premises GAL.

Thankfully these things are more or less handled by the GALSYNC that ships with FIM and the DIRSYNC hybrid logic.  If you have GALSYNC you can probably skip to the next section, where I get to crux of this post; if you don’t have GALSYNC, and instead implement your own scripted process, you might be interested what I have to say next.

GALSync and X500 proxyAddresses

The Outlook recipient cache issues are handled by adding X500 proxyAddresses values.  The source object’s legacyExchangeDN must be added to its proxyAddresses attribute, as an X500 address.  All downstream contact object’s legacyExchangeDN values must also be added to the proxyAddresses attribute of the source object and it’s downstream replicas.  By doing this you ensure “replyability” when a mail object moves between Exchange organisations.  The OOB GALSYNC solution does this for you.  If you have a custom solution be sure to mimic this behaviour.  The picture below depicts this – blue arrows show the general flow of information from source (user) into downstream (contact).  Orange arrows are the downstream/target object values going back to source.  In the end all objects have the same proxyAddresses values.  I didn’t want to muddy the drawing with arrows from contact to contact, but the end result is that this does happen.

image

Figure 3: High-level depiction of GALSYNC

The off-boarding issue is fixed by enabling Hybrid mode in DirSync, a.k.a. write-back.  In FIM that means implementing the required EAFs on each ADMA.  This is actually needed in a multi-forest topology even if hybrid isn’t being used – you need to get the cloudLegacyExchangeDN into the on-premises proxyAddresses.

Hybrid write-back and GALSync

When you enable DirSync hybrid (or implement your own EAF in the FIM pattern) you will, unless you configure your EAF accordingly, write the cloudLegacyExchangeDN into the proxyAddresses of contact objects that are owned by GALSYNC, i.e. the authoritative objects are in another Exchange organisation.  The next time GALSYNC runs it will remove the X500 address (the proxyAddresses attribute of the GALSYNC-managed contact is owned by GALSYNC).  The next time DirSync runs it will add the value again.  This process will repeat during each synchronisation and will be quite normal for each isolated instance of the FIM Synchronization service and won’t actually generate errors.  It’ll just cause replication churn in the on-premises AD DS forest.

Arguably you can stop this in the FIM DIRSYNC service, however should you (you can’t stop this if you’re using the DirSync appliance)?  This is actually needed.  If EXO users are sending emails to these contacts and are off-boarded back to the on-premises infrastructure their Outlook recipient cache will result in an NDR.  Or worse, it could work sometimes and other times not, depending on the timings of synchronisation and OAB downloads.

The solution to this issue is to tweak the GALSYNC code to not remove X500 addresses contributed by EXO.  I’ll discuss how I have handled this in the next section.  First, though, if you don’t care about how I’ve handled it and just want to download a DLL that works…

Download

If you don’t want to modify the source code and compile it yourself you can download a customised DLL from here.

Couple of points about the DLL.  I upgraded the default solution by loading it in Visual Studio 2012.  I then changed the target framework to .NET 3.5 (runtime of .NET 2.0).

Code changes

I’ll show you the original code and the changes required to fix the issue.  It’s very simple.

The original code

Here’s the original EAFProxyAddressesForwards sub-routine (method) that updates the proxyAddresses attribute.

        Private Sub EAFProxyAddressesForwards( _
            ByVal csentry As CSEntry, _
            ByRef mventry As MVEntry)

            '
            ' flow to others
            '
            Dim value As value

            If Not csentry.ObjectType.Equals(CONTACT) Then
                LogAndThrowUnexpectedDataException( _
                    "Unhandled object type in MapProxyAddressesForward " _
                    & "called with entry " & csentry.ToString _
                    & " and mventry " & mventry.ToString)
            End If

            '
            ' We clear all the values first if the object is not in
            ' the authoritative container.
            '
            If Not IsInContactOUs(csentry) Then
                csentry(PROXY_ADDRESSES).Delete()
            End If

            For Each value In mventry(PROXY_ADDRESSES).Values
                InsertProxyAddress(value.ToString, _
                    csentry(PROXY_ADDRESSES).Values)
            Next

            '
            ' we take all the values from MV and add to cs.
            ' NOTE: we can not distinguish whether some MA outside GALSync
            ' contributes any values
            '
            For Each value In mventry(LEGACY_EXCHANGE_DN).Values
                InsertProxyAddress(X500_PREFIX & value.ToString, _
                    csentry(PROXY_ADDRESSES).Values)
            Next

        End Sub

Basically, delete the CS value and flow the MV value.  Note that this is between the metaverse (MV) and the connector space (CS), and doesn’t result in changes to the actual connected system unless there are new elements in a given synchronisation.

The modified code

Here’s my modified code.  I’ve added some comments to show where I begin and end the customisations.  I’ll explain the two customisations after you’ve read the code.

        Private Sub EAFProxyAddressesForwards( _
            ByVal csentry As CSEntry, _
            ByRef mventry As MVEntry)

            '
            ' flow to others
            '
            Dim value As Value

            If Not CSEntry.ObjectType.Equals(CONTACT) Then
                LogAndThrowUnexpectedDataException( _
                    "Unhandled object type in MapProxyAddressesForward " _
                    & "called with entry " & CSEntry.ToString _
                    & " and mventry " & MVEntry.ToString)
            End If

            ' ***** BEGIN customisation *****
            '
            ' We clear all the values first if the object is not in
            ' the authoritative container.
            '
            'If Not IsInContactOUs(csentry) Then
            'csentry(PROXY_ADDRESSES).Delete()
            'End If

            Dim x500ProxyAddresses As New List(Of String)

            If Not IsInContactOUs(CSEntry) Then
                For Each value In CSEntry(PROXY_ADDRESSES).Values
                    If value.ToString().Contains("/o=ExchangeLabs/") Then
                        ' we need to preserve the O365 legacyExchangeDN value to write-back
                        ' after clearing
                        x500ProxyAddresses.Add(value.ToString())
                    End If
                Next
                CSEntry(PROXY_ADDRESSES).Delete()
            End If
            ' ***** END customisation *****

            For Each value In MVEntry(PROXY_ADDRESSES).Values
                InsertProxyAddress(value.ToString, _
                    CSEntry(PROXY_ADDRESSES).Values)
            Next

            '
            ' we take all the values from MV and add to cs.
            ' NOTE: we can not distinguish whether some MA outside GALSync
            ' contributes any values
            '
            For Each value In MVEntry(LEGACY_EXCHANGE_DN).Values
                InsertProxyAddress(X500_PREFIX & value.ToString, _
                    CSEntry(PROXY_ADDRESSES).Values)
            Next

            ' ***** BEGIN customisation *****
            ' add any preserved EXO proxyAddresses that might have come from the cloud
            Dim listItem As String
            For Each listItem In x500ProxyAddresses
                InsertProxyAddress(listItem, CSEntry(PROXY_ADDRESSES).Values)
            Next
            ' ***** END customisation *****
        End Sub

There are two changes in the code above:

  1. Before deleting the CSEntry proxyAddresses value I check for any X500 addresses that contain the string /o=ExchangeLabs/.  If I find one of these values I take a copy of it so that we can add it back shortly.
  2. After adding the metaverse values I then add the preserved X500 addresses that were added by DirSync and would have been removed by the original csentry(PROXY_ADDRESSES).Delete() command.

One little note, there’s obviously a using statement to handle the List<String> too:

Imports System.Collections.Generic

Summary

If GALSYNC interacts with an Exchange organisation that is either migrating to Office 365 or going to be running as a hybrid cloud and on-premises messaging system you need to modify GALSYNC to allow Office 365 Directory Synchronisation to add an X500 proxyAddresses value to the on-premises, non-authoritative contact objects created, managed and deleted by the GALSYNC solution.

Hope this helps.

Advertisements

About Paul Williams

IT consultant working for Microsoft specialising in Identity Management and Directory Services.
This entry was posted in Azure Active Directory, FIM, FIM 2010 R2, Office 365 and tagged , , , , , , , , , . Bookmark the permalink.

16 Responses to GALSync and DirSync in harmony

  1. Pingback: Windows Azure Active Directory Connector part 2: multi-forest directory synchronization | Yet another identity management blog

  2. Pingback: (2014-03-21) GALSync, DIRSync And SSO With Office 365 Blog Posts From MSResource.NET « Jorge's Quest For Knowledge!

  3. Excellent Post Paul thanks, do you happen to know if the same logic is true of the older MIIS sync? Do I need to consider modifying its logic to not remove the cloud X500 addressing also?

    Thanks in advance,
    David

    • Yes, I believe you will need to do this irrespective of FIM/ILM/MIIS version. Basically, DirSync sitting elsewhere adds a proxyAddresses value and GALSync will take it straight back out because it manages proxyAddresses for contacts it creates. If Hybrid write-back isn’t enabled then there is no issue.

      • David Sampson says:

        Sorry Paul I completely missed your original replies and just found myself back at this post. Thanks for both your responses.

  4. Also are you aware of any alternatives to using FIM, I’ve come across NetSec’s GalSync but have no experience of it. I’m hoping MS will introduce GALSync alongside federated Org relationships natively in the tenant rather that relying on on prem AD but no sign of it yet. In some Hybrid scenarios I find customers end up creating shared mailboxes for example that only exist in the cloud and dont make it back into AD thus getting missed from any GALSyncs.

    • Agree, it’s a pain. I haven’t got a good feel for alternatives. I’ve replaced custom solutions and an old version of MAXware with GALSync. I’ve never taken GALSync out so I or anybody else can replace it with something else.

      Cross-tenant GALSync would be cool and would simplify several on-premises topologies alright! 🙂

  5. philipwong says:

    Is that just replace your galsync.dll with the original one? Any other configuration need to be done in FIM? thanks.

    • For this specific issue it is just a matter of replacing the OOB DLL. If you have customised the solution yourself you will need to include the customisation as described. There is no configuration required. Just the minor code modification.

  6. Pingback: How to extend FIM GALSync solution with additional contact sources | IDArchitect.NET

  7. ChrisM says:

    very useful, one question, were is this added ?.
    Imports System.Collections.Generic

  8. Rodrigo Castillo says:

    Greatpost. If I need the GAL Sync for Lync too, do i have to do any other change?

    Thanks in advance

  9. Rodrigo Castillo says:

    Great Post. Do I have to do any other change for Lync online?

    Thanks in advance,

    • Not 100% guaranteed but I have not seen any reason to modify my GALSync implementations because of LYO, but where I have implemented GALSync in O365 environments I can’t recall LYO being used in any fashion other than POC, so can’t be sure. Let me know if you see any issues, but I can’t think of anything off the top of my head.

  10. Joseph Moussallem says:

    Hello Paul
    ive been searching a long time for such a post
    i have a scenario i wish you could just help me a bit
    i have ForestA without exchange onpremises (Exchange online on O365) and ForestB with exchange
    the purpose is to to GALSync between the two entities
    i am not being able to sync the users of Forest A and make them as mail contacts in Forest B
    ive tried a lot
    i tried to enable write back in order maybe to write some attributes from office365 to onpremises so the FIM can do the GALSync
    not working
    please help very urgenttttt
    and thank you a lot in advance

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s