Showing posts with label IIFP / MIIS / ILM / FIM. Show all posts
Showing posts with label IIFP / MIIS / ILM / FIM. Show all posts

Wednesday, July 6, 2011

ILM Sample Code PrepareMoveRequest Exchange 2010 Cross Forest Mailbox Moves - Multi Forest

Microsoft Identity Lifecycle Manager Service Pack 1 Feature Pack 1 (ILM 2007 SP1 FP1) can be used to pre-stage the user accounts with the appropriate attributes in a destination forest for cross-forest mailbox moves The out of the box GALSync MA cannot be used since it creates contact object instead of user object required for Online Mailbox Move. Microsoft has provided a sample code extension for the management agents to perform this which can be downloaded from:

http://www.microsoft.com/download/en/details.aspx?id=17741

The ILM sample code demonstrates how to sync source mailbox as Mail Enabled Users (MEU).

The problem with this sample code is was only designed for migration between two forests. My customer wishes to pre-stage user accounts from 2 forests into a new forest meaning I have two source forests! In the OneWaySync.xml file by default we have:

<?xml version="1.0" encoding="utf-8" ?>
<config>
<TargetOU>ou=MaiLboxmoves,DC=targetdom,DC=exchange,DC=contoso,DC=com</TargetOU>
<SourceMAName>Source Forest</SourceMAName>
<TargetMAName>Target Forest</TargetMAName>
</config>


I worked with a Microsoft FIM (Forefront Identity Manager) expert named Tracy Yu and together we made changes to the sample code and recompiled a new DLL to account for multiple source forests.

The file we needed to edit was Microsoft.Exchange.Sample.OneWayGALSync.MVRules.dll. The source code for this file is located under the solution folder under the sample ILM sample code package in a file named Microsoft.Exchange.Sample.OneWayGALSync.MVRules.cs. Here is our new code - in red are any changes made:

// ---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//

// ---------------------------------------------------------------------------

///
/// Example ILM 2007 Provisioning Rule to perform one-way cross-forest GAL synchronization
/// with required prerequisites for mailbox moves from source to target forests
///


using System;
using System.Xml;
using Microsoft.MetadirectoryServices;

namespace Microsoft.Exchange.Sample.OneWayGALSync.MVRules
{
public class MVExtensionObject : IMVSynchronization
{
// define variables for configuration setting
private string targetOU;
private string sourceMAName;
//add by tracy
private string sourceMAName1;
private string targetMAName;

public MVExtensionObject()
{
// No additional constructor logic required
}

void IMVSynchronization.Initialize()
{
// initialize the provisioning rules configuration parameters
// from config file in ILM Extensions directory
XmlDocument xmlConfigFile = new XmlDocument();
xmlConfigFile.Load(Utils.ExtensionsDirectory + "\\OneWaySync.xml");
XmlNode xmlConfig = xmlConfigFile.SelectSingleNode("config");
targetOU = xmlConfig.SelectSingleNode("TargetOU").InnerText.Trim();
sourceMAName = xmlConfig.SelectSingleNode("SourceMAName").InnerText.Trim();
targetMAName = xmlConfig.SelectSingleNode("TargetMAName").InnerText.Trim();
//add by tracy
sourceMAName1 = xmlConfig.SelectSingleNode("SourceMAName1").InnerText.Trim();
}

void IMVSynchronization.Terminate()
{
// No additional termination logic required
}

// For each Mailbox in the Source Forest Provision a connected
// Mail User object in the Target Forest.
void IMVSynchronization.Provision(MVEntry mventry)
{
// ConnectedMA sourceMA = mventry.ConnectedMAs[sourceMAName];
//ConnectedMA targetMA = mventry.ConnectedMAs[targetMAName];
//CSEntry csentry;

//modify by tracy

ConnectedMA targetMA = mventry.ConnectedMAs[targetMAName];
CSEntry csentry;
ConnectedMA sourceMA = null;
ConnectedMACollection MACols = mventry.ConnectedMAs;

foreach(ConnectedMA tmpMA in MACols)
{
if(tmpMA.Name == sourceMAName1)
{
sourceMA = mventry.ConnectedMAs[sourceMAName1];
}
else if (tmpMA.Name == targetMAName)
{
//did nothing
}
else
{
sourceMA = mventry.ConnectedMAs[sourceMAName];
}
}


// if the object has been deleted from Source Forest then delete it
// from Target Forest
if (sourceMA.Connectors.Count == 0)
{
targetMA.Connectors.DeprovisionAll();
return;
}

// This example provisioning rule excludes certain Exchange object types
if (sourceMA.Connectors.Count != 1 ||
!mventry["msExchHomeServerName"].IsPresent ||
!mventry["mailNickName"].IsPresent)
{
return;
}

ReferenceValue targetDN = targetMA.EscapeDNComponent("CN=" + mventry["cn"].Value).Concat(targetOU);

// check for Contacts in target forest that have to be converted to MEUs
for (int index = 0; index < targetMA.Connectors.Count; index++)
{
if (targetMA.Connectors.ByIndex[index].ObjectType.ToLower().Equals("contact"))
{
bool duplicateDN = targetMA.Connectors.ByIndex[index].DN.ToString().ToLower().Equals(targetDN.ToString().ToLower());

targetMA.Connectors.ByIndex[index].Deprovision();
if (duplicateDN)
return;
}
}

if (targetMA.Connectors.Count != 0)
return;

// provision a new AD User Object in the targetOU container
csentry = targetMA.Connectors.StartNewConnector("user");
csentry.DN = targetDN;

// provision the following minimal attributes on the new MailUser object
csentry["samAccountName"].Value = mventry["samAccountName"].Value;
csentry["msexchRecipientTypeDetails"].IntegerValue = 0x80;// MailUser
csentry["userAccountControl"].IntegerValue = 0x202; // ACCOUNTDISABLE | NORMAL_ACCOUNT
csentry["msexchRecipientDisplayType"].IntegerValue = -1073741818; // equivalent to *unsigned* 0xC0000006 i.e. ACL-able, Synced, MailUser
csentry["msExchMasterAccountSID"].Value = mventry["msExchMasterAccountSID"].IsPresent ? mventry["msExchMasterAccountSID"].Value : mventry["objectSID"].Value;
csentry["msExchMailboxGUID"].Value = mventry["msExchMailboxGUID"].Value;
csentry["mailNickname"].Value = mventry["mailNickname"].Value;
csentry["proxyAddresses"].Values = mventry["proxyAddresses"].Values;
csentry["proxyAddresses"].Values.Add("X500:" + mventry["legacyExchangeDN"].Value); // this ensures migrated mail that addresses this user is reply-able in target forest
csentry["msExchVersion"].IntegerValue = 44220983382016; // Set version to E14

csentry.CommitNewConnector();
}

bool IMVSynchronization.ShouldDeleteFromMV(CSEntry csentry, MVEntry mventry)
{
throw new EntryPointNotImplementedException();
}
}
}


Now when we can create two source domains in our OneWaySync.xml file for two source management agents.

<?xml version="1.0" encoding="utf-8" ?>
<config>
<TargetOU>ou=MaiLboxmoves,DC=targetdom,DC=exchange,DC=contoso,DC=com</TargetOU>
<SourceMAName>Source Forest 1</SourceMAName>
<SourceMAName1>Source Forest 2</SourceMAName1>
<TargetMAName>Target Forest</TargetMAName>
</config>


Thanks Tracy!

stopped-error-limit

I was using ILM (Identity Lifecycle Manager) to synchronize users cross forest to prepare for cross-forest mailbox moves using the sample code provided by Microsoft. Please see:

http://www.microsoft.com/download/en/details.aspx?id=17741

I was synchronizing over 5000 users to the new forest. Synchronization was failing with stopped-error-limit.



I found out that MIIS / ILM and FIM only allow up to 5000 objects by default. You can change this with a DWORD in the registry which you create under HKLM\SYSTEM\CurrentControlSet\miisserver\Parameters. This is documented by Microsoft on KB2387673

I set my limit to 10,000 which resolved the problem.