0% found this document useful (0 votes)
369 views54 pages

Extensible Connectivity 2.0 Management Agent

Extensible Connectivity 2.0 Management Agent FIM agent for password management ans synchronization with AD

Uploaded by

boualem.ini
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
369 views54 pages

Extensible Connectivity 2.0 Management Agent

Extensible Connectivity 2.0 Management Agent FIM agent for password management ans synchronization with AD

Uploaded by

boualem.ini
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 54

Extensible Connectivity 2.

0
Management Agent
Document History

Author Furqan Asghar


Author’s position Product Specialist - FIM
Provider -
Customer -
Project Name -

Provider Solution Manager -

Date 8/12/2012
Document No. -
Version number 1.1

Change Record

Date Author Version Change Reference


17-Juy-2012 Furqan Asghar 1.1 Included the Password Extension.

Reviewers

Date Author Version Position


Table of Contents
Extensible Connectivity 2.0 Management Agent........................................................................................................... 1 
Document History .......................................................................................................................................................... 2 
Executive Summary ....................................................................................................................................................... 4 
Audience ............................................................................................................................................................................................. 4 
Extensible Connectivity Management Agent 2.0 ........................................................................................................... 5 
Extensible Connectivity 2.0 Extension Project ...................................................................................................................... 5 

Creating the ECMA Project ...................................................................................................................................................... 5 

Call based vs. File Based ECMA ............................................................................................................................................. 7 

Code Examples: Call Based ECMA 2.0 ................................................................................................................................. 7 

Code Examples: File Based ECMA 2.0 .............................................................................................................................. 21 

Extensible Connectivity 2.0 Management Agent Creation ........................................................................................... 29 

Password Change Notification ................................................................................................................................................ 46 

Understanding Object Identifiers: Anchor and DN ......................................................................................................... 47 

Setting anchor and dn values.............................................................................................................................................. 48 

Import changes to anchor and DN ................................................................................................................................... 49 

Return values, Errors, and Exceptions ................................................................................................................................... 49 

Export Errors, Special Actions .............................................................................................................................................. 50 

Export Errors, Object level errors ....................................................................................................................................... 50 

Discovery errors, continue run ............................................................................................................................................ 51 

Discovery errors, fatal ............................................................................................................................................................. 51 

Connection errors, fatal ......................................................................................................................................................... 51 

Exceptions ................................................................................................................................................................................... 52 

General Information..................................................................................................................................................................... 53 

Known Issues .................................................................................................................................................................................. 53 


Executive Summary

This document contains technical knowledge of creating Extensible Connectivity 2.0 Management Agent
for Microsoft Forefront Identity Manager 2010 R2.

Microsoft Forefront Identity Manager comes with a variety of OOB Management Agents that are and can
be used to connect to a verity of Microsoft and third party applications. However, if any third party
application does not have an OOB Management Agent included in FIM 2010 R2, we can create that
management agent with the help of the Extensible Connectivity 2.0 Management Agent Framework that is
provided by FIM 2010 R2.

As an example for a Call Based ECMA, an Access Extensible Connectivity Management Agent has been
created. As an example for a File Based ECMA, an XML Extensible Connectivity Management Agent has
been created.

Also along with the Export and Import of data to and from FIM 2010 R2, the code examples in this
document also implement the Password Extensions which enable real-time password synchronization with
the Active Directory.

Audience
This document is for all those who are interested in creating Custom Management Agents for the
Microsoft Forefront Identity Manager 2010 R2.

As this is a technical document, some knowledge of Microsoft FIM 2010 R2 is required to understand the
contents. The reader should also have knowledge of Microsoft Visual Studio 2010 and should know the
C# Programming Language.

A prior programming experience, most preferably in C#, is clearly required to implement the steps
underlined in this document.
Extensible Connectivity Management Agent 2.0

The Management Agent for Extensible Connectivity 2.0 (ECMA 2.0) is a new management agent for FIM
2010 that contains many features unavailable in existing management agents. Similar to the Management
Agent for Extensible Connectivity that is included in FIM 2010, the ECMA 2.0 can be customized for any
data source, and can be packaged and distributed to other FIM environments. In addition, the ECMA 2.0

1. Can implement a call-based import and export, as well as a file-based import and export

2. Provides more flexibility to define custom parameters

3. Runs in design mode and packaged mode for ease of configuration in Synchronization Service Manager

4. Expands schema support to include LDAP and databases

5. Provides support for batch export and import

6. Supports multiple partitions

7. Provides a new streamlined template for creating extension projects in Microsoft Visual Studio

Extensible Connectivity 2.0 Extension Project


Creating the ECMA Project
Creating the Extensible Connectivity 2.0 Extension Project is quite easy. Just open up the FIM 2010
Synchronization Service GUI, go to the Management Agents tab and Right Click. Then go to the Create
Extension Project Sub Menu and Click on the Extensible Connectivity 2.0 Extension, you will be provided
by the following screen.

Depending on the requirements you can select the Programming Language (Visual Basic .NET or Visual
C#), Visual Studio Version (Visual Studio 2008 or Visual Studio 2010), Project Name and Project Location.

Note: The project name that you provide here is what you will get in the list when you are
creating the MA.

When the Visual Studio launches the following initial project is created. According to the requirements,
the programmer has to insert code. The code for the Call Based and File Based Management Agent are
both included in this document, however the programmer will have to tweak the code or recreate it if real
world implementations.
Once the code has been written and compiled successfully, the Extensible Connectivity Management
Agent can be created in FIM 2010.

Call based vs. File Based ECMA


A Call Based ECMA is more preferred as it eliminates the need of files and file system. A Call Based ECMA
collects and writes data directly to the application. This kind of ECMA is most suitable for Applications and
External Systems which provide a sort of External Interface to communicate to the application or the low
level database that the application uses. This can be a web service or APIs that can integrate with the .NET
environment.

A File Based ECMA is more simple and easier to code and develop. However it is not the suitable choice
for an ECMA when a Call Based ECMA can be developed for the external system. Thus a File Based ECMA
should only be developed when a Call Based ECMA cannot be created. A File Based ECMA is advised for
External Systems that do not support external access or interface to the system or its low level database.
Moreover a File Based ECMA can be developed for a system that supports internal scripts or GUI to
import or export files or can run scripts internally to read external files and process the commands therein
accordingly.

Following are the code examples for both a Call Based ECMA and a File Based ECMA.

Code Examples: Call Based ECMA 2.0


Important Functions that should be implemented in this type of ECMA are as follows:

1) GetConfigParameters: This function describes the configuration parameters that will be


required by the management agent to successfully make a connection to the call based
system, for example a webservice. 
2) GetSchema: This function describes the Schema pertaining to an object in the external
system. It contains attributes for a relevant object in the external system, for example
fields/columns of a table. 
3) OpenImportConnection: This function is used to implement logic for connecting to the
external system when an import run is executed in FIM 2010 Synchronization Service. 
4) GetImportEntries: This function is executed when an import run is executed on FIM 2010
Synchronization Service. In this function you should program the logic to import Adds,
Deletes and Updates to the data into FIM 2010 Connector Space etc.  
5) CloseImportConnection: This function is executed when an import run has been executed
completely with or without errors. In this function you should program the logic for closing
the connection anything that is required before or after closing. 
6) OpenExportConnection: This function is used to implement logic for connecting to the
external system when an export run is executed in FIM 2010 Synchronization Service. 
7) PutExportEntries: This function is executed when an export run is executed on FIM 2010
Synchronization Service. In this function you should program the logic to export Adds,
Deletes and Updates to the data from the FIM 2010 Connector Space to the External System. 
8) CloseExportConnection: This function is executed when an export run has been executed
completely with or without errors. In this function you should program the logic for closing
the connection anything that is required before or after closing. 
9) OpenPasswordConnection: This function is executed when a Password Change Notification is
received. In this function you should program the logic for opening the connection before
the password change is executed on the target MA. 
10) SetPassword: This function is executed when a Password Change Notification is received for
a NEW Password. In this function you should program the logic for setting the password for
the object on the target MA. 
11) ChangePassword: This function is executed when a Password Change Notification is received
for a Change Password. Both the OLD and the NEW password are received as parameters. In
this function you should program the logic for changing the old password for the object on
the target MA. Conditions must be put in place to check the old password before replacing it
with the new password. 
12) ClosePasswordConnection: This function is executed after the Password Change
Notification is received and executed. In this function you should program the logic for
closing the connection after the password change is executed on the target MA.

Following is the Code Example that demonstrates a Call Based ECMA.

using System; 
using System.IO; 
using System.Xml; 
using System.Text; 
using System.Collections; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Collections.Specialized; 
using Microsoft.MetadirectoryServices; 
 
namespace CallBasedMA 

    using System; 
    using System.IO; 
    using System.Xml; 
    using System.Text; 
    using System.Collections.Specialized; 
    using System.Collections.Generic; 
    using System.Collections.ObjectModel; 
    using Microsoft.MetadirectoryServices; 
    using System.Data.OleDb; 
    using System.Data; 
 
    namespace FimSync_Ezma 
    { 
        public class EzmaExtension : 
        IMAExtensible2CallExport, 
        IMAExtensible2CallImport, 
            // IMAExtensible2FileImport, 
            //IMAExtensible2FileExport, 
            //IMAExtensible2GetHierarchy, 
         IMAExtensible2GetSchema, 
         IMAExtensible2GetCapabilities, 
         IMAExtensible2GetParameters, 
            IMAExtensible2Password 
        //IMAExtensible2GetPartitions 
        { 
            private int m_importDefaultPageSize = 12; 
            private int m_importMaxPageSize = 50; 
            private int m_exportDefaultPageSize = 10; 
            private int m_exportMaxPageSize = 20; 
            public string myConnection; 
            public string myFile; 
            public string myTable; 
            OleDbConnection conn; 
            OleDbCommand cmd; 
            OleDbDataAdapter adapter; 
            DataSet da; 
            public string myFirst; 
            public string myLast; 
            public string myEmail; 
            public string myEmpID; 
            public string myFull; 
            public string myAccount; 
 
            // 
            // Constructor 
            // 
            public EzmaExtension() 
            { 
                // 
                // TODO: Add constructor logic here 
                // 
                WriteException.WriteExceptionToFile("In the Constructor"); 
            } 
 
            public MACapabilities Capabilities 
            { 
                get 
                { 
                    MACapabilities myCapabilities = new MACapabilities(); 
 
                    myCapabilities.ConcurrentOperation = true; 
                    myCapabilities.ObjectRename = false; 
                    myCapabilities.DeleteAddAsReplace = true; 
                    myCapabilities.DeltaImport = false; 
                    myCapabilities.DistinguishedNameStyle = 
MADistinguishedNameStyle.None; 
                    myCapabilities.ExportType = MAExportType.AttributeUpdate; 
                    myCapabilities.NoReferenceValuesInFirstExport = false; 
                    myCapabilities.Normalizations = MANormalizations.None; 
 
                    return myCapabilities; 
                } 
            } 
 
            public IList<ConfigParameterDefinition> 
GetConfigParameters(KeyedCollection<string, ConfigParameter> 
configParameters, 
                                                                
ConfigParameterPage page) 
            { 
                List<ConfigParameterDefinition> configParametersDefinitions = new 
List<ConfigParameterDefinition>(); 
 
                switch (page) 
                { 
                    case ConfigParameterPage.Connectivity: 
 
                        
configParametersDefinitions.Add(ConfigParameterDefinition.Cr
eateStringParameter("File", "")); 
                        WriteException.WriteExceptionToFile("GetConfigParameters: 
File"); 
                        
configParametersDefinitions.Add(ConfigParameterDefinition.Cr
eateStringParameter("Table", "")); 
                        WriteException.WriteExceptionToFile("GetConfigParameters: 
Table"); 
 
 
                        break; 
 
 
                    case ConfigParameterPage.Global: 
 
 
                        break; 
 
                    case ConfigParameterPage.Partition: 
                        break; 
 
                    case ConfigParameterPage.RunStep: 
 
                        break; 
                } 
 
                return configParametersDefinitions; 
            } 
 
            public ParameterValidationResult 
ValidateConfigParameters(KeyedCollection<string, 
ConfigParameter> configParameters, 
                                                                       
ConfigParameterPage page) 
            { 
 
 
                ParameterValidationResult myResults = new 
ParameterValidationResult(); 
 
                
WriteException.WriteExceptionToFile(string.Format("ValidateC
onfigParameters: myResults: {0}, {1}, {2}, {3}", 
myResults.Code, myResults.ErrorMessage, 
myResults.ErrorParameter, myResults.ToString())); 
 
                return myResults; 
 
            } 
 
            public Schema GetSchema(KeyedCollection<string, ConfigParameter> 
configParameters) 
            { 
                WriteException.WriteExceptionToFile("In Get Schema"); 
 
                Microsoft.MetadirectoryServices.SchemaType personType = 
Microsoft.MetadirectoryServices.SchemaType.Create("AccessUse
r", false); 
 
 
                myFile = configParameters["File"].Value; 
                WriteException.WriteExceptionToFile(myFile); 
                myTable = configParameters["Table"].Value; 
                WriteException.WriteExceptionToFile(myTable); 
 
                DataSet myData = this.AccessSchema(myFile, myTable); 
                WriteException.WriteExceptionToFile("Got Schema!"); 
 
 
                string[] SQLSchema = new 
string[myData.Tables["Columns"].Rows.Count]; 
 
 
                for (int i = 0; i <= myData.Tables["Columns"].Rows.Count ‐ 1; 
i++) 
                { 
 
                    SQLSchema[i] = 
myData.Tables["Columns"].Rows[i].ItemArray.GetValue(0).ToStr
ing().Trim(); 
                    string myattrib = SQLSchema[i]; 
 
                    if (myattrib == "EmployeeID") 
                    { 
                        
personType.Attributes.Add(SchemaAttribute.CreateAnchorAttrib
ute(myattrib, AttributeType.String)); 
                        WriteException.WriteExceptionToFile("in EmployeeID Check: 
" + myattrib); 
                    } 
 
                    else 
                    { 
                        
personType.Attributes.Add(SchemaAttribute.CreateSingleValued
Attribute(myattrib, AttributeType.String)); 
                        WriteException.WriteExceptionToFile("in EmployeeID Else 
Check: " + myattrib); 
                    } 
 
                } 
 
                Schema schema = Schema.Create(); 
                schema.Types.Add(personType); 
 
                WriteException.WriteExceptionToFile("Returning Schema!!!"); 
                return schema; 
            } 
 
            public DataSet AccessSchema(string file, string table) 
            { 
                WriteException.WriteExceptionToFile("In Access Schema!!!"); 
 
                try 
                { 
                    myConnection = ("Provider=Microsoft.ACE.OLEDB.12.0;Data 
Source=" + file + ";Persist Security Info=False;"); 
                    WriteException.WriteExceptionToFile(myConnection); 
                    try 
                    { 
                        conn = new OleDbConnection(myConnection); 
                        conn.Open(); 
                        WriteException.WriteExceptionToFile("Connection OK!!"); 
                    } 
                    catch (Exception exp) 
                    { 
                        WriteException.WriteExceptionToFile(exp.ToString()); 
                    } 
 
                    cmd = new OleDbCommand(); 
                    cmd.CommandType = CommandType.Text; 
                    string cmdText = "Select * from " + table + ""; 
                    WriteException.WriteExceptionToFile(cmdText); 
                    cmd.CommandText = cmdText; 
                    cmd.Connection = conn; 
                    WriteException.WriteExceptionToFile("Assigned connection to 
Command!"); 
                    adapter = new OleDbDataAdapter(cmd); 
                    DataTable dt1 = new DataTable(); 
                    adapter.Fill(dt1); 
                    WriteException.WriteExceptionToFile("Adapter Filled"); 
 
                    DataTable dt2 = new DataTable("Columns"); 
                    dt2.Columns.Add("COLUMN_NAME", typeof(string)); 
 
                    for (int i = 0; i < dt1.Columns.Count; i++) 
                    { 
                        dt2.Rows.Add(dt2.NewRow()); 
                        
WriteException.WriteExceptionToFile(dt1.Columns[i].ColumnNam
e); 
                        dt2.Rows[dt2.Rows.Count ‐ 1][0] = 
dt1.Columns[i].ColumnName; 
                    } 
 
                    da = new DataSet(); 
                    da.Tables.Add(dt2); 
                    return da; 
                } 
                catch (Exception exp) 
                { 
                    WriteException.WriteExceptionToFile(exp.ToString()); 
                } 
                return null; 
            } 
 
            public OpenImportConnectionResults OpenImportConnection( 
                                           KeyedCollection<string, 
ConfigParameter> configParameters, 
                                           Schema types, 
                                           OpenImportConnectionRunStep 
importRunStep) 
            { 
                myFile = configParameters["File"].Value; 
                myTable = configParameters["Table"].Value; 
 
                myConnection = ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" 
+ myFile + ";Persist Security Info=False;"); 
                conn = new OleDbConnection(myConnection); 
                cmd = new OleDbCommand(); 
                cmd.CommandType = CommandType.Text; 
                string cmdText = "Select * from " + myTable; 
                cmd.CommandText = cmdText; 
                cmd.Connection = conn; 
 
                return new OpenImportConnectionResults(); 
            } 
 
 
            public GetImportEntriesResults 
GetImportEntries(GetImportEntriesRunStep importRunStep) 
            { 
 
                adapter = new OleDbDataAdapter(cmd); 
                da = new DataSet(); 
                adapter.Fill(da, "Employees"); 
                GetImportEntriesResults importReturnInfo; 
                List<CSEntryChange> csentries = new List<CSEntryChange>(); 
 
                for (int i = 0; i <= da.Tables["Employees"].Rows.Count ‐ 1; i++) 
                { 
                    CSEntryChange csentry1 = CSEntryChange.Create(); 
 
                    csentry1.ObjectModificationType = ObjectModificationType.Add; 
                    csentry1.ObjectType = "AccessUser"; 
 
                    for (int j = 0; j < da.Tables["Employees"].Columns.Count; 
j++) 
                    { 
                        
csentry1.AttributeChanges.Add(AttributeChange.CreateAttribut
eAdd(da.Tables["Employees"].Columns[j].ColumnName, 
da.Tables["Employees"].Rows[i].ItemArray.GetValue(j).ToStrin
g().Trim())); 
                    } 
                    csentries.Add(csentry1); 
                } 
 
                importReturnInfo = new GetImportEntriesResults(); 
                importReturnInfo.MoreToImport = false; 
                importReturnInfo.CSEntries = csentries; 
                return importReturnInfo; 
            } 
 
            public CloseImportConnectionResults 
CloseImportConnection(CloseImportConnectionRunStep 
importRunStepInfo) 
            { 
                conn.Close(); 
                return new CloseImportConnectionResults(); 
            } 
 
            public int ImportMaxPageSize 
            { 
                get 
                { 
                    return m_importMaxPageSize; 
                } 
            } 
 
            public int ImportDefaultPageSize 
            { 
                get 
                { 
                    return m_importDefaultPageSize; 
                } 
            } 
 
            public void OpenExportConnection(KeyedCollection<string, 
ConfigParameter> configParameters, 
                                Schema types, 
                                OpenExportConnectionRunStep exportRunStep) 
            { 
                myFile = configParameters["File"].Value; 
                myTable = configParameters["Table"].Value; 
 
                myConnection = ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" 
+ myFile + ";Persist Security Info=False;"); 
                conn = new OleDbConnection(myConnection); 
                cmd = new OleDbCommand(); 
                cmd.CommandType = CommandType.Text; 
                conn.Open(); 
 
            } 
 
            public PutExportEntriesResults PutExportEntries(IList<CSEntryChange> 
csentries) 
            { 
                int i = 0; 
 
                foreach (CSEntryChange csentryChange in csentries) 
                { 
                    myEmpID = csentries[i].DN.ToString(); 
 
                    if (csentryChange.ObjectType == "AccessUser") 
                    { 
 
                        #region Add 
 
                        if (csentryChange.ObjectModificationType == 
ObjectModificationType.Add) 
                        { 
                            #region a 
 
                            foreach (string attrib in 
csentryChange.ChangedAttributeNames) 
                            { 
 
                                switch (attrib) 
                                { 
                                    case "FirstName": 
                                        myFirst = 
csentryChange.AttributeChanges["FirstName"].ValueChanges[0].
Value.ToString(); 
                                        break; 
 
                                    case "LastName": 
                                        myLast = 
csentryChange.AttributeChanges["LastName"].ValueChanges[0].V
alue.ToString(); 
                                        break; 
 
                                    case "EMail": 
                                        myEmail = 
csentryChange.AttributeChanges["EMail"].ValueChanges[0].Valu
e.ToString(); 
                                        break; 
 
                                    case "AccountName": 
                                        myAccount = 
csentryChange.AttributeChanges["AccountName"].ValueChanges[0
].Value.ToString(); 
                                        break; 
 
                                    case "FullName": 
                                        myFull = 
csentryChange.AttributeChanges["FullName"].ValueChanges[0].V
alue.ToString(); 
                                        break; 
                                } 
                            } 
                            #endregion 
 
                            string cmdText = "Insert into " + myTable + 
"(FirstName, LastName, EMail, EmployeeID, FullName, 
AccountName) VALUES ('" + myFirst + "','" + myLast + "','" + 
myEmail + "','" + myEmpID + "','" + myFull + "','" + 
myAccount + "')"; 
                            cmd.CommandText = cmdText; 
                            cmd.Connection = conn; 
 
                            cmd.ExecuteNonQuery(); 
 
                        } 
 
                        #endregion 
                        #region Delete 
                        if (csentryChange.ObjectModificationType == 
ObjectModificationType.Delete) 
                        { 
 
                            myEmpID = csentries[i].DN.ToString(); 
                            string cmdText = "Delete from " + myTable + "Where 
EmployeeID = '" + myEmpID + "'"; 
                            cmd.CommandText = cmdText; 
                            cmd.Connection = conn; 
                            cmd.ExecuteNonQuery(); 
 
 
                        } 
                        #endregion 
 
                        #region Update 
                        if (csentryChange.ObjectModificationType == 
ObjectModificationType.Update) 
                        { 
 
                            foreach (string attribName in 
csentryChange.ChangedAttributeNames) 
                            { 
 
 
                                if 
(csentryChange.AttributeChanges[attribName].ModificationType 
== AttributeModificationType.Add) 
                                { 
                                    myEmpID = 
csentryChange.AnchorAttributes[0].Value.ToString(); 
                                    string attribValue = 
csentryChange.AttributeChanges[attribName].ValueChanges[0].V
alue.ToString(); 
                                    string cmdText = "Update" + myTable + "SET" + 
attribName + " = '" + attribValue + "' Where EmployeeID = '" 
+ myEmpID + "'"; 
                                    cmd.CommandText = cmdText; 
                                    cmd.Connection = conn; 
                                    cmd.ExecuteNonQuery(); 
                                } 
                                else if 
(csentryChange.AttributeChanges[attribName].ModificationType 
== AttributeModificationType.Delete) 
                                { 
 
                                    myEmpID = 
csentryChange.AnchorAttributes[0].Value.ToString(); 
                                    string cmdText = "Update " + myTable + " SET 
" + attribName + " = 'NULL' Where EmployeeID = '" + myEmpID 
+ "'"; 
                                    cmd.CommandText = cmdText; 
                                    cmd.Connection = conn; 
                                    cmd.ExecuteNonQuery(); 
                                } 
                                else if 
(csentryChange.AttributeChanges[attribName].ModificationType 
== AttributeModificationType.Replace) 
                                { 
                                    myEmpID = 
csentryChange.AnchorAttributes[0].Value.ToString(); 
                                    string attribValue = 
csentryChange.AttributeChanges[attribName].ValueChanges[0].V
alue.ToString(); 
                                    string cmdText = "Update " + myTable + " SET 
" + attribName + " = '" + attribValue + "' Where EmployeeID 
= '" + myEmpID + "'"; 
                                    cmd.CommandText = cmdText; 
                                    cmd.Connection = conn; 
                                    cmd.ExecuteNonQuery(); 
                                } 
                                else if 
(csentryChange.AttributeChanges[attribName].ModificationType 
== AttributeModificationType.Update) 
                                { 
                                    myEmpID = 
csentryChange.AnchorAttributes[0].Value.ToString(); 
                                    string attribValue = 
csentryChange.AttributeChanges[attribName].ValueChanges[0].V
alue.ToString(); 
                                    string cmdText = "Update " + myTable + " SET 
" + attribName + " = '" + attribValue + "' Where EmployeeID 
= '" + myEmpID + "'"; 
                                    cmd.CommandText = cmdText; 
                                    cmd.Connection = conn; 
                                    cmd.ExecuteNonQuery(); 
                                } 
 
 
 
 
                            } 
                        } 
 
                        #endregion 
                    } 
 
                    i++; 
                } 
 
 
                PutExportEntriesResults exportEntriesResults = new 
PutExportEntriesResults(); 
 
                return exportEntriesResults; 
            } 
 
 
            public void CloseExportConnection(CloseExportConnectionRunStep 
exportRunStep) 
            { 
                conn.Close(); 
            } 
 
            public int ExportDefaultPageSize 
            { 
                get 
                { 
                    return m_exportDefaultPageSize; 
                } 
                set 
                { 
                    m_exportDefaultPageSize = value; 
                } 
            } 
 
            public int ExportMaxPageSize 
            { 
                get 
                { 
                    return m_exportMaxPageSize; 
                } 
                set 
                { 
                    m_exportMaxPageSize = value; 
                } 
            } 
 
 
 
 
            ///////////////////////////////////// Password Management 
///////////////////////////////// 
 
 
 
            OleDbCommand cmdP; 
            OleDbConnection connP; 
 
 
            public void OpenPasswordConnection(KeyedCollection<string, 
ConfigParameter> configParameters, Partition partition) 
            { 
                // 
                // TODO: Remove this throw statement if you implement this method 
                // 
                //throw new EntryPointNotImplementedException(); 
                try 
                { 
                    string myFile = @"E:\Projects\FIM\Users.mdb"; 
                    //WriteException.WriteExceptionToFile(myFile); 
                    string myConnection = 
("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + myFile + 
";Persist Security Info=False;"); 
                    connP = new OleDbConnection(myConnection); 
                    connP.Open(); 
                    cmdP = new OleDbCommand(); 
                    cmdP.CommandType = CommandType.Text; 
                    cmdP.Connection = connP; 
 
                } 
                catch (Exception exp) 
                { 
                    WriteException.WriteExceptionToFile(exp.ToString()); 
                } 
            } 
 
            public void ClosePasswordConnection() 
            { 
                try 
                { 
                    cmdP.Dispose(); 
                    connP.Close(); 
                    connP.Dispose(); 
                } 
                catch (Exception exp) 
                { 
                    WriteException.WriteExceptionToFile(exp.ToString()); 
                } 
            } 
 
            public ConnectionSecurityLevel GetConnectionSecurityLevel( 
                ) 
            { 
                return ConnectionSecurityLevel.NotSecure; 
            } 
 
            public void SetPassword( 
                CSEntry csentry, 
                System.Security.SecureString newPassword, 
                PasswordOptions options 
                ) 
            { 
                try 
                { 
                    IntPtr StringPointer = 
System.Runtime.InteropServices.Marshal.SecureStringToBSTR(ne
wPassword); 
                    string newPasswordStr = 
System.Runtime.InteropServices.Marshal.PtrToStringBSTR(Strin
gPointer); 
                    cmdP.CommandText = "UPDATE Users SET [Password] = '" + 
newPasswordStr + "' WHERE EmployeeID = '" + 
csentry["EmployeeID"].StringValue + "'"; 
                    WriteException.WriteExceptionToFile(cmdP.CommandText); 
                    cmdP.ExecuteNonQuery(); 
                    WriteException.WriteExceptionToFile("Password change 
Successfully for User: " + 
csentry["EmployeeID"].StringValue); 
                } 
                catch (Exception exp) 
                { 
                    WriteException.WriteExceptionToFile(exp.ToString()); 
                } 
            } 
 
            public void ChangePassword(CSEntry csentry, 
System.Security.SecureString OldPassword, 
System.Security.SecureString NewPassword) 
            { 
                try 
                { 
                    IntPtr StringPointerNew = 
System.Runtime.InteropServices.Marshal.SecureStringToBSTR(Ne
wPassword); 
                    string newPasswordStr = 
System.Runtime.InteropServices.Marshal.PtrToStringBSTR(Strin
gPointerNew); 
                    IntPtr StringPointerOld = 
System.Runtime.InteropServices.Marshal.SecureStringToBSTR(Ol
dPassword); 
                    string oldPasswordStr = 
System.Runtime.InteropServices.Marshal.PtrToStringBSTR(Strin
gPointerOld); 
                    cmdP.CommandText = "UPDATE Users SET Password = '" + 
newPasswordStr + "' WHERE EmployeeID = '" + 
csentry["EmployeeID"].StringValue + "' AND Password = '" + 
oldPasswordStr + "'"; 
                    cmdP.ExecuteNonQuery(); 
                } 
                catch (Exception exp) 
                { 
                    WriteException.WriteExceptionToFile(exp.ToString()); 
                } 
            } 
 
            public void RequireChangePasswordOnNextLogin( 
                CSEntry csentry, 
                bool fRequireChangePasswordOnNextLogin 
                ) 
            { 
                //throw new EntryPointNotImplementedException(); 
            } 
        }; 
    } 
 
    public static class WriteException 
    { 
        static bool DEBUG = false; 
        public static void WriteExceptionToFile(string strException) 
        { 
            if (!DEBUG) return; 
 
            File.AppendAllText("E:\\Projects\\exception.txt", strException + 
"\r\n"); 
        } 
    } 

 

Code Examples: File Based ECMA 2.0


Important Functions that should be implemented in this type of ECMA are as follows:

1) GetConfigParameters: This function describes the configuration parameters that will be


required by the management agent to successfully open the file from the file system, for
example a Path to the File to import data from. 
2) WriteImportFile: In this function you can generate an export dump of the connected
system. It is called once the beginning of an import run execution. If the dump file is not
created an error is raised. If a dump is created, the FIM 2010 R2 MA Reads data from this file. 
3) ReadExportFile: In this function you can read a file that contains objects to be exported
into the connected system. This function is called after the export run has been successfully
executed on the FIM 2010 R2 Synchronization Service. 
4) OpenPasswordConnection: This function is executed when a Password Change Notification is
received. In this function you should program the logic for opening the connection before
the password change is executed on the target MA. 
5) SetPassword: This function is executed when a Password Change Notification is received for
a NEW Password. In this function you should program the logic for setting the password for
the object on the target MA. 
6) ChangePassword: This function is executed when a Password Change Notification is received
for a Change Password. Both the OLD and the NEW password are received as parameters. In
this function you should program the logic for changing the old password for the object on
the target MA. Conditions must be put in place to check the old password before replacing it
with the new password. 
7) ClosePasswordConnection: This function is executed after the Password Change
Notification is received and executed. In this function you should program the logic for
closing the connection after the password change is executed on the target MA.

Following is the Code Example that demonstrates a Call Based ECMA.

using System; 
using System.IO; 
using System.Xml; 
using System.Text; 
using System.Collections.Specialized; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using Microsoft.MetadirectoryServices; 
 
namespace FimSync_Ezma 

    public class EzmaExtension : 
        //IMAExtensible2CallExport, 
        //IMAExtensible2CallImport, 
    IMAExtensible2FileImport, 
    IMAExtensible2FileExport, 
        //IMAExtensible2GetHierarchy, 
        //IMAExtensible2GetSchema, 
    IMAExtensible2GetCapabilities, 
    IMAExtensible2GetParameters, 
            IMAExtensible2Password 
    //IMAExtensible2GetPartitions 
    { 
        // 
        // Constructor 
        // 
        public EzmaExtension() 
        { 
            // 
            // TODO: Add constructor logic here 
            // 
        } 
 
        public MACapabilities Capabilities 
        { 
            get 
            { 
                MACapabilities myCapabilities = new MACapabilities(); 
 
                myCapabilities.ConcurrentOperation = true; 
                myCapabilities.ObjectRename = false; 
                myCapabilities.DeleteAddAsReplace = true; 
                myCapabilities.DeltaImport = true; 
                myCapabilities.DistinguishedNameStyle = 
MADistinguishedNameStyle.None; 
                myCapabilities.ExportType = MAExportType.AttributeUpdate; 
                myCapabilities.NoReferenceValuesInFirstExport = false; 
                myCapabilities.Normalizations = MANormalizations.None; 
 
                return myCapabilities; 
            } 
        } 
 
 
        public IList<ConfigParameterDefinition> 
GetConfigParameters(KeyedCollection<string, ConfigParameter> 
configParameters, 
                                                             ConfigParameterPage 
page) 
        { 
            List<ConfigParameterDefinition> configParametersDefinitions = new 
List<ConfigParameterDefinition>(); 
 
            switch (page) 
            { 
                case ConfigParameterPage.Connectivity: 
                    break; 
 
                case ConfigParameterPage.Global: 
 
                    
configParametersDefinitions.Add(ConfigParameterDefinition.Cr
eateStringParameter("Full Import File", "")); 
                    
configParametersDefinitions.Add(ConfigParameterDefinition.Cr
eateStringParameter("Delta Import File", "")); 
                    
configParametersDefinitions.Add(ConfigParameterDefinition.Cr
eateStringParameter("Export File", "")); 
                    
configParametersDefinitions.Add(ConfigParameterDefinition.Cr
eateDropDownParameter("Export Encoding", 
"ASCII,Unicode,UTF8", false, "UTF8")); 
                    break; 
 
                case ConfigParameterPage.Partition: 
                    break; 
 
                case ConfigParameterPage.RunStep: 
                    
configParametersDefinitions.Add(ConfigParameterDefinition.Cr
eateCheckBoxParameter("Omit Xml Declaration", false)); 
                    break; 
            } 
 
            return configParametersDefinitions; 
        } 
 
        public ParameterValidationResult 
ValidateConfigParameters(KeyedCollection<string, 
ConfigParameter> configParameters, 
                                                                   
ConfigParameterPage page) 
        { 
            ParameterValidationResult myResults = new 
ParameterValidationResult(); 
            return myResults; 
        } 
 
        public WriteImportFileResults WriteImportFile( 
                                             KeyedCollection<string, 
ConfigParameter> configParameters, 
                                             Schema types, 
                                             WriteImportFileRunStep 
importRunStep) 
        { 
 
            try 
            { 
                StreamWriter swImport = new StreamWriter(importRunStep.FilePath, 
true); 
 
                swImport.WriteLine( 
                  "{0},{1},{2},{3},{4},{5}", 
                  "objectclass", 
                  "delta", 
                  "EmployeeID", 
                  "FirstName", 
                  "LastName", 
                  "Email" 
                  ); 
 
                string inputxml = ""; 
                XmlDocument doc = new XmlDocument(); 
 
                if (importRunStep.ImportType == OperationType.Full) 
                { 
                    inputxml = MAUtils.MAFolder + @"\" + configParameters["Full 
Import File"].Value; 
                } 
 
                if (importRunStep.ImportType == OperationType.Delta) 
                { 
                    inputxml = MAUtils.MAFolder + @"\" + configParameters["Delta 
Import File"].Value; 
                } 
 
                doc.Load(inputxml); 
 
                XmlNodeList xnList = doc.SelectNodes("/Users/object"); 
 
                foreach (XmlNode node in xnList) 
                { 
                    string objectclass = node["objectclass"].InnerText; 
                    string delta = node["delta"].InnerText; 
                    string EmployeeID = node["EmployeeID"].InnerText; 
                    string FirstName = node["FirstName"].InnerText; 
                    string LastName = node["LastName"].InnerText; 
                    string Email = node["Email"].InnerText; 
 
                    string fullline = objectclass + "," + delta + "," + 
EmployeeID + "," + FirstName + "," + LastName + "," + Email; 
                    swImport.WriteLine(fullline); 
                } 
 
                swImport.WriteLine(); 
 
                swImport.Close(); 
            } 
            catch (Exception exp) 
            { 
                WriteException.WriteExceptionToFile(exp.ToString()); 
            } 
            return new WriteImportFileResults(); 
        } 
 
        public void ReadExportFile(KeyedCollection<string, ConfigParameter> 
configParameters, 
                                     Schema types, 
                                     ReadExportFileRunStep exportRunStep) 
        { 
            XmlWriter m_xmlWriterExport; 
 
            StreamReader sr = new StreamReader(exportRunStep.FilePath); 
            string lineContents = null; 
 
            XmlWriterSettings xmlSettings = new XmlWriterSettings(); 
 
            string encoding = configParameters["Export Encoding"].Value; 
            if (encoding.Equals("ASCII")) 
            { 
                xmlSettings.Encoding = Encoding.ASCII; 
            } 
            else if (encoding.Equals("UTF8")) 
            { 
                xmlSettings.Encoding = Encoding.UTF8; 
            } 
            else 
            { 
                xmlSettings.Encoding = Encoding.Unicode; 
            } 
 
            string omitXmlDecl = configParameters["Omit Xml Declaration"].Value; 
            if (omitXmlDecl.Equals("1")) 
            { 
                xmlSettings.OmitXmlDeclaration = true; 
            } 
 
            string exportfile = configParameters["Export File"].Value.ToString(); 
 
            m_xmlWriterExport = XmlTextWriter.Create(MAUtils.MAFolder + @"\" + 
exportfile, xmlSettings); 
            m_xmlWriterExport.WriteStartElement(Nodes.Root); 
            m_xmlWriterExport.WriteAttributeString(Nodes.FullExport, 
(OperationType.Full == exportRunStep.ExportType) ? "true" : 
"false"); 
 
            m_xmlWriterExport.WriteElementString(Nodes.PartitionDN, 
exportRunStep.StepPartition.DN); 
 
            while (null != (lineContents = sr.ReadLine())) 
            { 
                char[] commaEscape = new char[] { ',' }; 
                char[] quoteEscape = new char[] { '"' }; 
                string[] valueComponents = lineContents.Split(commaEscape); 
 
                if (Nodes.ObjectClass == valueComponents[0].Trim(quoteEscape)) 
                { 
                    continue; 
                } 
 
                m_xmlWriterExport.WriteStartElement(Nodes.Object); 
 
                m_xmlWriterExport.WriteElementString( 
                    Nodes.ObjectClass, 
                    valueComponents[0].Trim(quoteEscape) 
                    ); 
 
                m_xmlWriterExport.WriteElementString( 
                    Nodes.Delta, 
                    valueComponents[1].Trim(quoteEscape) 
                    ); 
 
                m_xmlWriterExport.WriteElementString( 
                    Nodes.EmployeeID, 
                    valueComponents[2].Trim(quoteEscape) 
                    ); 
 
                m_xmlWriterExport.WriteElementString( 
                    Nodes.FirstName, 
                    valueComponents[3].Trim(quoteEscape) 
                    ); 
 
                m_xmlWriterExport.WriteElementString( 
                    Nodes.LastName, 
                    valueComponents[4].Trim(quoteEscape) 
                    ); 
 
                m_xmlWriterExport.WriteElementString( 
                    Nodes.Email, 
                    valueComponents[5].Trim(quoteEscape) 
                    ); 
 
                m_xmlWriterExport.WriteEndElement(); 
            } 
 
            m_xmlWriterExport.WriteEndElement(); 
 
            m_xmlWriterExport.Close(); 
        } 
 
 
        struct Nodes 
        { 
            public const string Root = "Users"; 
            public const string PartitionDN = "partition‐dn"; 
            public const string FullExport = "full‐export"; 
            public const string Object = "object"; 
            public const string ObjectClass = "objectclass"; 
            public const string Delta = "delta"; 
            public const string EmployeeID = "EmployeeID"; 
            public const string FirstName = "FirstName"; 
            public const string LastName = "LastName"; 
            public const string Email = "Email"; 
        } 
 
 
 
 
            ///////////////////////////////////// Password Management 
///////////////////////////////// 
 
 
 
            OleDbCommand cmdP; 
            OleDbConnection connP; 
 
 
            public void OpenPasswordConnection(KeyedCollection<string, 
ConfigParameter> configParameters, Partition partition) 
            { 
                // 
                // TODO: Remove this throw statement if you implement this method 
                // 
                //throw new EntryPointNotImplementedException(); 
                try 
                { 
                    string myFile = @"E:\Projects\FIM\Users.mdb"; 
                    //WriteException.WriteExceptionToFile(myFile); 
                    string myConnection = 
("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + myFile + 
";Persist Security Info=False;"); 
                    connP = new OleDbConnection(myConnection); 
                    connP.Open(); 
                    cmdP = new OleDbCommand(); 
                    cmdP.CommandType = CommandType.Text; 
                    cmdP.Connection = connP; 
 
                } 
                catch (Exception exp) 
                { 
                    WriteException.WriteExceptionToFile(exp.ToString()); 
                } 
            } 
 
            public void ClosePasswordConnection() 
            { 
                try 
                { 
                    cmdP.Dispose(); 
                    connP.Close(); 
                    connP.Dispose(); 
                } 
                catch (Exception exp) 
                { 
                    WriteException.WriteExceptionToFile(exp.ToString()); 
                } 
            } 
 
            public ConnectionSecurityLevel GetConnectionSecurityLevel( 
                ) 
            { 
                return ConnectionSecurityLevel.NotSecure; 
            } 
 
            public void SetPassword( 
                CSEntry csentry, 
                System.Security.SecureString newPassword, 
                PasswordOptions options 
                ) 
            { 
                try 
                { 
                    IntPtr StringPointer = 
System.Runtime.InteropServices.Marshal.SecureStringToBSTR(ne
wPassword); 
                    string newPasswordStr = 
System.Runtime.InteropServices.Marshal.PtrToStringBSTR(Strin
gPointer); 
                    cmdP.CommandText = "UPDATE Users SET [Password] = '" + 
newPasswordStr + "' WHERE EmployeeID = '" + 
csentry["EmployeeID"].StringValue + "'"; 
                    WriteException.WriteExceptionToFile(cmdP.CommandText); 
                    cmdP.ExecuteNonQuery(); 
                    WriteException.WriteExceptionToFile("Password change 
Successfully for User: " + 
csentry["EmployeeID"].StringValue); 
                } 
                catch (Exception exp) 
                { 
                    WriteException.WriteExceptionToFile(exp.ToString()); 
                } 
            } 
 
            public void ChangePassword(CSEntry csentry, 
System.Security.SecureString OldPassword, 
System.Security.SecureString NewPassword) 
            { 
                try 
                { 
                    IntPtr StringPointerNew = 
System.Runtime.InteropServices.Marshal.SecureStringToBSTR(Ne
wPassword); 
                    string newPasswordStr = 
System.Runtime.InteropServices.Marshal.PtrToStringBSTR(Strin
gPointerNew); 
                    IntPtr StringPointerOld = 
System.Runtime.InteropServices.Marshal.SecureStringToBSTR(Ol
dPassword); 
                    string oldPasswordStr = 
System.Runtime.InteropServices.Marshal.PtrToStringBSTR(Strin
gPointerOld); 
                    cmdP.CommandText = "UPDATE Users SET Password = '" + 
newPasswordStr + "' WHERE EmployeeID = '" + 
csentry["EmployeeID"].StringValue + "' AND Password = '" + 
oldPasswordStr + "'"; 
                    cmdP.ExecuteNonQuery(); 
                } 
                catch (Exception exp) 
                { 
                    WriteException.WriteExceptionToFile(exp.ToString()); 
                } 
            } 
 
            public void RequireChangePasswordOnNextLogin( 
                CSEntry csentry, 
                bool fRequireChangePasswordOnNextLogin 
                ) 
            { 
                //throw new EntryPointNotImplementedException(); 
            } 
    }; 
 
 
    public static class WriteException 
    { 
        static bool DEBUG = true; 
        public static void WriteExceptionToFile(string strException) 
        { 
            if (!DEBUG) return; 
 
            File.AppendAllText("E:\\Projects\\exception.txt", strException + 
"\r\n"); 
        } 
    } 

 
 

Extensible Connectivity 2.0 Management Agent Creation


Once the ECMA Management Agent Project has been successfully complied in Visual Studio 2010, a dll
file is automatically created and placed in the following directory of the server.

‘C:\Program Files\Microsoft Forefront Identity Manager\2010\Synchronization Service\Extensions\’

Note: This document assumes that your development platform is on the FIM 2010 R2
Synchronization Server itself. If your development environment is located into another machine
not hosting the FIM 2010 R2 Synchronization Service, you will have to configure or copy your dll
manually to the above location of the FIM 2010 R2 Synchronization Server, to be able to create
the FIM Extensible Management Agent 2.0.

Please follow the screen shots below to understand how to create the ECMA on the FIM 2010 R2
Synchronization Service GUI.
Password Change Notification
For the Password Management for any MA where the AD has to be the Source of managing passwords,
the Password Change Notification Service has to be installed and configured on all the domain
controllers.

For download this service and more information on PCNS (Password Change Notification Service) please
follow the below link.

http://www.microsoft.com/en-us/download/details.aspx?id=19495

http://technet.microsoft.com/en-us/library/cc720654(WS.10).aspx

Note: Please note that the PCNS Service should be Installed on ALL the domain controllers.
Failure to do so shall not record password changes on the domain controller that the service is
not running on.
Following is the setup and configuration of PCNS relevant to the test environment used in this document.

Setspn.exe ‐a PassChangeNS/fimtestdc.fimtest.com fimtest\FIMServiceAccount 
 
Usage: 
Setspn.exe ‐a <user defined named for target FIM 2010 R2 server>/<fully qualified 
domain name of the server running FIM 2010 R2>\<domain\user 
name of the FIM 2010 R2 service account> 
For example: Setspn.exe ‐a PCNSCLNT/fab‐dev‐01.usergroup.fabrikam.com fab‐dev‐
01\MIISServAccount 

Pcnscfg.exe addtarget /n:PassChangeNSCfg /a:FIMTestDC.fimtest.com 
/s:PassChangeNS/fimtestdc.fimtest.com /fi:"Domain Users" 
/f:3 
 
Usage: 
Pcnscfg.exe addtarget /n:<user‐defined friendly name of the target server running 
FIM 2010 R2> /a:<fully‐qualified domain name of the server 
running FIM 2010 R2> /s:<the SPN for the FIM 2010 R2 server> 
/fi:<the specified inclusion group> /f:3 
 
For example: 
Pcnscfg.exe addtarget /n:miisdemo /a:fab‐dev‐01.usergroup.fabrikam.com 
/s:PCNSCLNT/fab‐dev‐01.usergroup.fabrikam.com /fi:“Domain 
Users” /f:3 

Understanding Object Identifiers: Anchor and DN


Objects are identified with up to two different identifiers, anchor and dn. An anchor is an immutable
(cannot be changed) unique identifier and is mandatory for an object. Which attribute, or attributes, is
used for the anchor is defined on the management agent. Which attribute is used for the dn is defined in
the schema. The dn is the attribute which other objects will use to refer to this object and it is mutable
(can be changed). The dn attribute is despite its name not necessarily in LDAP dn style. If the anchor is
also the value used by other objects to reference it, then no DN is needed.

The existence and format of the dn attribute is defined by the MA Capability dnStyle. dnStyle can have the
following values:

Value Description

None The management agent does not have a separate dn attribute. The anchor is used for
references between objects. Objects cannot be renamed.
Generic The management agent has a dn. It can be of type string, integer or binary but it is
not in LDAP style. Object renames are allowed.

LDAP The management agent has a dn and it is of LDAP style. An LDAP style dn allows the
interface GetHierarchy to be used and that Provisioning Hierarchy is enabled. Object
renames are allowed.

Setting anchor and dn values


The anchor is set either in provisioning code or returned from the connected system during export. If the
anchor is a constructed (concatenated) anchor then all components of the anchor must have a value. For
Generic and LDAP dnStyle the dn must be set in provisioning code. An object must have an anchor after
export. If there isn’t an anchor for an object, an error will be thrown.

The provisioning code will be different depending on the scenario:

 Connected Data Sources that Expect server to Provide the Anchor Attributes
 Connected Data Sources that Generate the Anchor Attributes
 Connected Data Sources that Generate the Anchor Attributes

The MA’s export code will also be different depending on the scenario:

 Connected Data Sources that Generate the Anchor Attributes


 Connected Data Source Changes the dn during export

Anchor attributes cannot be changed in provisioning code or EAF once the object has been exported.

The following table lists errors the Sync Engine can return with respect to anchor issues.

Error Description

E_MMS_MA_MULTI_VALUED_ANCHOR_COMPONENT The anchor attribute is made of multi-valued


attributes.

E_MMS_MA_MISSING_ANCHOR_COMPONENT One of the anchor attributes is empty and


was not returned by the connected system
during export.

E_MMS_MA_ANCHOR_TOO_LONG

E_MMS_STORE_EXPORT_ASSIGN_DUPLICATE_ANCHOR During export an anchor was returned


already in use.

The following table lists errors the Sync Engine can return with respect to dn issues.
Error Description

E_MMS_MA_INVALID_DN Returned when the dn is not valid LDAP.

E_MMS_STORE_EXPORT_ASSIGN_DUPLICATE_DN During export an anchor was returned already in use.

Import changes to anchor and DN


During full and delta import it is possible that a change to an attribute which is making up the anchor is
read. If an anchor attribute is changed it is treated as a new object. During Full Import the new object is
created and the old object is obsoleted (deleted at the end of Full Import). During Delta Import and if an
update to an anchor attribute is received the cs object is transformed into a transient object and the MA
Import method will be called with an ObjectFullImport request with the new anchor value. The transient
object will be removed by an explicit delete or during Full Import.

If a change to the dn is detected and the dnStyle is Generic or LDAP then this is treated as an object
rename.

Return values, Errors, and Exceptions


During import and export the methods can provide error information to the sync engine. These errors can
be returned on an object level or as a fatal error.

Depending on the error the step run result or WMI return message will be different.

Fatal errors: stopped-

Other errors: competed-export-errors, completed--warnings

Errors can be categorized into the following:

 Export Errors
 Special action. An error where the sync engine should take an action to correct the problem.
These will not be visible in the UI.
 Object level error visible in the UI
 Import Errors
 Discovery error which tell the sync engine to not commit the watermark but continue the run.
 Discovery error which is a fatal import error and stops the run.
 Connection errors
 Connectivity issues which will stop the run.
 Error from the Sync Engine to the MA.
Export Errors, Special Actions
The following list can be returned on an object level during ExportEntries. These also have a
corresponding object level error when the sync engine is not supposed to take a special action.

All these errors will put the object into the next export pass.

Error Notes

(cd-missing-object-sa) Change object from update to add.


convert-update-to-add
(cd-existing-object-sa) Change object from add to update.
convert-add-to-update
(dn-attribute-failure-sa) Put the object in a second export pass.
next-pass-retry
(non-existing-parent-sa) Re-provision parent hierarchy
provision-parent

Export Errors, Object level errors


The following list can be returned on an object during ExportEntries.

Error Notes

cd-error This error is returned when the connected data source has a specialized error
type. This error is accompanied by the <cd-error> element, and the
information contained there should aid in troubleshooting.

cd-missing-object This error is returned when a modify of an object is exported to the


connected data source, but the object cannot be found in the connected data
source.

cd-existing-object This error is returned when an add is exported to the connected data source,
but the object is already present in the connected data source.

dn-attributes-failure This error is returned when exporting an add or modify sets a reference value
for which there is no corresponding connected data source object. If you see
this error, use the connector space object viewer to determine which changes
to reference attributes were not successfully exported.

Non-existent-parent This error is returned by the management agent for LDAP when either the
export of an add or a rename fails because the parent object does not exist in
the connected data source.
Discovery errors, continue run
The following list can be returned on an object level during ImportEntries. These will be reported as errors
in the UI. When the first object with a discovery error is returned the watermark will not be persisted for
the remainder of the run.

Error Notes

missing-dn It indicates that the management agent could read the element and
parse it, but there was no domain name value for the object.

dn-not-ldap-conformant This error is returned when a management agent reports a domain name
value that does not conform to the LDAP specification.

invalid-dn This error is returned when a management agent reports that a domain
name does not meet an FIM constraint.

Most discovery errors will be detected by the Sync Engine when it is trying to persist the connector space
object. These will be visible in the UI but they are not available for the MA to consume.

Discovery errors, fatal


The following list can be returned on an object level during ImportEntries. These errors indicate a
corrupted delta change log and that a full import is needed to correct. These errors will also terminate the
run (EndImport will be called).

Error Notes

parse-error This error is returned in delta mode when it cannot parse an entry.

read-error This error is returned by call-based management agents when there is a generic error
reading a particular object.

Connection errors, fatal


Connection errors can appear in import, export, and password management.

The EndImport/EndExport method will be called after an exception is thrown.

Error Notes

stopped-server-down The run step stopped because the data source server is down.

stopped-bad-server-credentials The run step stopped because the extensible management


agent reported that the credentials are invalid.
stopped-extensible-extension-error The run step stopped because the extensible management
agent returned an ExtensibleExtensionException exception.

stopped-entry-point-not-implemented The run step stopped because the extensible management


agent does not implement the entry point.

stopped-unexpected-data The run step stopped because the extensible management


agent encountered unexpected data.

stopped-extension-dll-exception The run step stopped because the extension DLL threw an
exception.

Exceptions
The following is a list of exceptions that can be thrown by the Extensible Connectivity 2.0 Management
Agent.

Exception Type Condition

AccessDeniedException This is thrown when the password set is denied.

BadServerCredentialsException The credentials that are used to connect to the connected directory
are not valid. When this exception is thrown by this method, the run
stops and the WMI Provider returns the string stopped-bad-server-
credentials.

This exception does not generate an event log entry.

ClosePasswordConnectionException Thrown when an error occurred while closing the connection.

EntryPointNotImplementedException The rules extension does not implement this method. When this
exception is thrown by this method, the run stops and the WMI
Provider returns the string stopped-entry-point-not-implemented.

This exception does not generate an event log entry.

ExtensibleExtensionException An unexpected error occurred in the extension. When this exception


is thrown by this method, the run stops and the WMI Provider
returns the string stopped-extensible-extension-error.

This exception generates an event log entry.

ObjectNotFoundException Thrown when a specified object cannot be found.


ObjectTypeNotSupportedException Thrown when the object type does not support password set.

PasswordExtensionException An unexpected error occurred in the extension.

This exception generates an event log entry.

PasswordIllFormedException Thrown when the password specified was ill formed.

PasswordPolicyViolationException Thrown when the password specified violates the password policy.

ServerDownException The connected directory extension cannot establish a connection to


the connected directory. When this exception is thrown by this
method, the run stops and the WMI Provider returns the string
stopped-server-down.

This exception does not generate an event log entry.

UnexpectedDataException The method received unexpected data. When this exception is


thrown by this method, the run stops and the WMI Provider returns
the string stopped-unexpected-data.

This exception generates an event log entry.

General Information
The following is a list of general information about using the ECMA 2.0.

Refresh partitions after importing a server configuration.


If you are using an ECMA 2.0 and import a server configuration into the Forefront Identity Manager
Synchronization Service, you will need to refresh the partitions. This is required to update partition
mappings. Partitions should get a new set of DNs in a production environment, different from the pre-
production environment.

Attribute names can only contain the a‐z, A‐Z, 0‐9, and ‘‐’
Attribute names currently can only be composed of the following a-z, A-Z, 0-9, and ‘-‘.

When doing a Full Export you may see Add or Replace.


When doing a full export the objectModificationType can be either Add or Replace. There is not functional
difference between the two.

LDAP style DN's on File based MA's, will not utilize the GetHeirarchy() interface.

Known Issues
The following is a list of known issues.
Can create an Import run profile for an Export Only MA and run without error.
If you create an Export-only Management Agent, but create an Import run profile for it, you will not get an
error or a cancelation of the run when you use it. Instead the MA will show a run status of ‘in-progress’
until the admin cancels it manually.

Delete Export on call‐based attribute‐update exports on anchors will result in superfluous information
being returned
If the developer is doing a Delete during export on a call-based, attribute-update type export, they could
potentially see superfluous Attribute and Value Modification Types for the anchor attribute. These extra
pieces of information should just be ignored by the Management Agent, as they are not needed for the
attribute delete.

Unable to get past Global Parameters page if Connectivity Parameters is not defined.
If you implement an ECMA 2.0 that does not have any Connectivity Parameters defined but does have
Global Parameters defined, you may not be able to get past the Global Parameters page if you refresh the
dll. The reason this is happening is because it is up to the connectivity page to update the global
parameters. With no connectivity page, we asked for a refresh of the global parameters but don't have the
means to refresh global parameters at all.

MissingParentObjectException
If you provision an object to a call based ECMA 2 that is using Generic DN style and that object has a DN
that is modeled after LDAP DNs (ex. CN=User,OU=Test,DC=microsoft,DC=com) you will get a
MissingParentObjectException.

The reason this occurs is that even though you set up Generic DN Style, the DN capability you set still will
take into account hierarchy in this mode. Specifically it looks for commas. To workaround this, do not put
commas in your DN name when using Generic style DN.

ObjectModificationType.Update and AttributeModificationType.Add combination not supported.


If you are using ObjectModificationType.Update then using AttributeModificationType.Add on the objects
attributes, it is not supported. The reason is that when using AttributeModificationType.Add, it is assumed
that the attribute does not already exist and this will cause issues if the attribute does exist. It is sufficient
to use Replace, Update, or Delete on these attributes.

Attribute with AttributeOperation.ImportOnly attribute passed to Interfaces as


AttributeOperation.ImportExport.
If you create an attribute during a GetSchema() operation that has AttributeOperation.ImportOnly and
then this attribute is passed to a different interface, this attribute will be passed with an
AttributeOperation.ImportExport. Microsoft has confirmed that this is a known issue.

ECMA2: File‐Based Multi‐Partition Connectors are not a supported scenario.


File-Based multi-partition connectors are not a supported scenario for ECMA 2.0 based MA's. Currently,
there is nothing preventing a developer from trying to write this kind of connector. However, it is not a
supported scenario and it creation is expected to be explicitly prevented in a future release.

You might also like