extensible connectivity 2.0 management agent · pdf fileextensible connectivity 2.0 management...

54
Extensible Connectivity 2.0 Management Agent

Upload: dodan

Post on 29-Mar-2018

221 views

Category:

Documents


1 download

TRANSCRIPT

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

CreatingtheECMAProject

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.

Callbasedvs.FileBasedECMA

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.

CodeExamples:CallBasedECMA2.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.CreateStringParameter("File", "")); 

                        WriteException.WriteExceptionToFile("GetConfigParameters: File"); 

                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter("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("ValidateConfigParameters: 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("AccessUser", 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).ToString().Trim(); 

                    string myattrib = SQLSchema[i];                      if (myattrib == "EmployeeID")                     { 

                        personType.Attributes.Add(SchemaAttribute.CreateAnchorAttribute(myattrib, AttributeType.String)); 

                        WriteException.WriteExceptionToFile("in EmployeeID Check: " + myattrib); 

                    }                      else                     {                         

personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute(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].ColumnName); 

                        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.CreateAttributeAdd(da.Tables["Employees"].Columns[j].ColumnName, da.Tables["Employees"].Rows[i].ItemArray.GetValue(j).ToString().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].Value.ToString(); 

                                        break;                                      case "EMail":                                         myEmail = 

csentryChange.AttributeChanges["EMail"].ValueChanges[0].Value.ToString(); 

                                        break;                                      case "AccountName":                                         myAccount = 

csentryChange.AttributeChanges["AccountName"].ValueChanges[0].Value.ToString(); 

                                        break;  

                                    case "FullName":                                         myFull = 

csentryChange.AttributeChanges["FullName"].ValueChanges[0].Value.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].Value.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].Value.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].Value.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(newPassword); 

                    string newPasswordStr = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(StringPointer); 

                    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(NewPassword); 

                    string newPasswordStr = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(StringPointerNew); 

                    IntPtr StringPointerOld = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(OldPassword); 

                    string oldPasswordStr = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(StringPointerOld); 

                    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");         }     } }  

CodeExamples:FileBasedECMA2.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.CreateStringParameter("Full Import File", "")); 

                    configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter("Delta Import File", "")); 

                    configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter("Export File", "")); 

                    configParametersDefinitions.Add(ConfigParameterDefinition.CreateDropDownParameter("Export Encoding", "ASCII,Unicode,UTF8", false, "UTF8")); 

                    break;                  case ConfigParameterPage.Partition:                     break;                  case ConfigParameterPage.RunStep:                     

configParametersDefinitions.Add(ConfigParameterDefinition.CreateCheckBoxParameter("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(newPassword); 

                    string newPasswordStr = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(StringPointer); 

                    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(NewPassword); 

                    string newPasswordStr = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(StringPointerNew); 

                    IntPtr StringPointerOld = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(OldPassword); 

                    string oldPasswordStr = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(StringPointerOld); 

                    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.

Settinganchoranddnvalues

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.

ImportchangestoanchorandDN

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.

ExportErrors,SpecialActions

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) convert-update-to-add

Change object from update to add.

(cd-existing-object-sa) convert-add-to-update

Change object from add to update.

(dn-attribute-failure-sa) next-pass-retry

Put the object in a second export pass.

(non-existing-parent-sa) provision-parent

Re-provision parent hierarchy

ExportErrors,Objectlevelerrors

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.

Discoveryerrors,continuerun

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.

Discoveryerrors,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.

Connectionerrors,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.

Refreshpartitionsafterimportingaserverconfiguration.

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.

Attributenamescanonlycontainthea‐z,A‐Z,0‐9,and‘‐’

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

WhendoingaFullExportyoumayseeAddorReplace.

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

LDAPstyleDN'sonFilebasedMA's,willnotutilizetheGetHeirarchy()interface.

Known Issues The following is a list of known issues.

CancreateanImportrunprofileforanExportOnlyMAandrunwithouterror.

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.

DeleteExportoncall‐basedattribute‐updateexportsonanchorswillresultinsuperfluousinformationbeingreturned

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.

UnabletogetpastGlobalParameterspageifConnectivityParametersisnotdefined.

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.UpdateandAttributeModificationType.Addcombinationnotsupported.

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 asAttributeOperation.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‐BasedMulti‐PartitionConnectorsarenotasupportedscenario.

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.