Friday 26 February 2016

FIM2010: Selective Import Attribute Flow

Intro

Although FIM allows you to filter objects on import in the connector configuration, it does not allow you to filter attribute values for a certain population. An example use case could be: "Import all mobile numbers from HR, but not the mobile numbers for person X, Y and Z". Reason for this could be that the X, Y and Z's numbers should not occur in the global address list or some other system where they can be viewed by others.

This problem can be solved by manipulating the import attribute flow. Simply block the value of the mobile number for these people to flow to the metaverse. To complete this solution, make sure "allow null on export" is enabled on all export attribute flows for mobile. Also, if it is possible for people to update their mobile number in data sources other than HR, make sure there is an import attribute flow from these sources to prevent data loss. Alternatively, it can be solved by manipulating all export attribute flows for mobile numbers. Because this would have an impact on possible future export attribute flows for mobile, the import approach seems the way to go.

Possible solutions

  1. Use two synchronization rules instead of one: one with the scope of people flowing mobile, one with the scope of people not flowing mobile.
  2. advanced attribute flow
    • Based on a list, external to the data source and FIM. Con of this option is that the list needs to be maintained.
    • Based on a flag in the data source. This solution would need a flag for each attribute you need to filter on import.
Because two sync rules seemed too complex for such a simple task, I choose to implement it using an advanced import rule and since no flag was available in the data source, the solution is based on a list.

Solution using Rules Extension

All accounts are listed in an xml file, using their anchor value of the data source where the mobile number is originating from. In the case of an HR system, this could be the database key.

<Filter>
    <User>007</User>
</Filter>

The xml file is read during the initialize phase of the Rules Extension. First, the installation directory location is read from the registry. After that, the file is read and the list is constructed using Linq. The sample code contains constant strings for clarity. Best practice would be to remove these values and put them in a configuration file and read the values from there.


using Microsoft.MetadirectoryServices;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
 
private const string CONFDIR = "Extensions";
private const string FIM_SUBKEY = @"SYSTEM\CurrentControlSet\Services\FIMSynchronizationService\Parameters";
private const string FIM_SUBKEY_VAR = "Path";
private const string filterFile = "mobileFilter.xml";
private List<string> mobileFilter;
 
/// <summary>
/// FIM Extensions folder path. (Microsoft Forefront Identity Manager\2010\Synchronization Service\Extensions)
/// </summary>
public string SourceDirectory
{
  get
  {
    string sourceDirectory = string.Empty;
    using (RegistryKey key = Registry.LocalMachine.OpenSubKey(FIM_SUBKEY, false))
    {
      if (key != null)
      {
        sourceDirectory = key.GetValue(FIM_SUBKEY_VAR).ToString();
      }
    }
    if (string.IsNullOrEmpty(sourceDirectory))
    {
      throw new Exception("Error while reading registry");
    }
    return Path.Combine(sourceDirectory, CONFDIR);
  }
}
 
/// <summary>
/// Initialization of the rules extension object.
/// </summary>
void IMASynchronization.Initialize() 
{
  XDocument mobiles = XDocument.Load(Path.Combine(SourceDirectory, filterFile));
  mobileFilter = mobiles.Root.Elements("User").Select(user => user.Value).ToList();
}

The import rule is rather simple, now that everything is in place. Just check whether the entry that is about to be imported occurs in the list and make the proper decision: either import the value into the metaverse or delete it.


/// <summary>
/// Import mobile number if not on the list of numbers to filter.
/// </summary>
/// <param name="mventry">Destination metaverse entry.</param>
/// <param name="csentry">Source connector space entry.</param>
private void importMobile(MVEntry mventry, CSEntry csentry)
{
  if (csentry["mobile"].IsPresent && !mobileFilter.Contains(csentry.DN.ToString()))
  {
    mventry["mobile"].Value = csentry["mobile"].Value;
  }
  else
  {
    mventry["mobile"].Delete();
  }
}

See Also

FIM2010: Writing Advanced Attribute Flows