Creating a SOAP XML Webservice and a FIM 2010 R2/MIM 2016 ECMA2 connector

You know I wanted to create a Workday or PeopleSoft or SAP MIM ECMA2 connector recently. These are all Webservice systems, the OOB MIM Webservice connector is best described as “intriguing” but I won’t hang my coat on it so best you create your own connector. I faced a couple of challenges

  1. Workday dev environment is only for customers and a subscription is a minimum $200k!
  2. PeopleSoft/SAP do not have a free dev environment or download (thank you Microsoft and the trial software) and I don’t even know how to set this up
  3. There is very little information out there on how to setup a FIM/MIM ECMA2 connector to a Webservice system especially since you don’t control the dev environment.

So the goal for me was

  1. Setup my own Webservice system where i can control the data and the way it works
  2. The Webservice system will read data from a SQL DB at the backend and return the data as a dataset which is really what PeopleSoft/SAP/Workday does.
  3. Setup an ECMA2 connector to consume the data
  4. Share it with you!

Let’s begin

Overall Test environment

I have a Windows 2012 R2 server which is going to host the Webservice. I would advise that you don’t put the Webservice on the same IIS server that you have the FIM Portal else you will spend hours trying to decipher why it isn’t working. The root cause has to do with the fact that FIM Portal takes port 80 and causes conflicts. My FIM 2010 R2 server is on a windows 2008 R2 server, my Server is on DotNet 4.0. My SQL 2008 R2 database is on my FIM server.

Creating the Contoso database

  1. Go to SQL and create a database called “CONTOSO”
  2. Create a table called HR, you can use this script in the query window to create it.

USE [CONTOSO]
GO
/****** Object: Table [dbo].[HR] Script Date: 11/13/2016 7:43:45 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[HR](
[FirstName] [char](20) NULL,
[LastName] [char](30) NULL,
[EMail] [nchar](30) NULL,
[EmployeeID] [nchar](10) NULL,
[FullName] [char](50) NULL,
[AccountName] [char](20) NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO

  1. Populate the table with some data, you can go to SQL Studio, select Edit the table and add the two records below

FirstName,LastName,EMail,EmployeeID,FullName,AccountName
John,Smith,jsmith@corp.contoso.com,11255,John Smith,jsmith
Jane,Doe,jdoe@contoso.com,11257,Jane Doe,jdoe

Creating the Webservice

  1. Add IIS Role to your Windows 2012 R2 server. My IIS is 8.5. Make sure the following specifications are added, go to Add Roles and features and select

Roles\Security
-Basic Authentication
Roles\Application Development
-Application Initialization
-ASP
-ASP.NET 3.5
-ASP.NET 4.5
Features\.NET Framework 4.5\WCF Services
-Http Activation

  1. Create a directory under your C:\Inetpub\www.root named WebServices
  2. Create a file in the WebServices folder called “DemoContosoDB.asmx”. Copy this code into the file. Change the Data Source to your SQL server name

<%@ WebService Language=’c#’ Class=”DEMOContosoDB” %>
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Services;
public class DEMOContosoDB:WebService
{
public SqlConnection connection = new SqlConnection(“Data Source=tlkfim1;Integrated Security=SSPI;Initial Catalog=Contoso”);
[WebMethod]
public DataSet GetEmployees()
{
SqlDataAdapter adapter = new SqlDataAdapter(
“SELECT Firstname,Lastname,Email,EmployeeID,Fullname,AccountName FROM HR”, connection);
DataSet EmployeeDS = new DataSet();
adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
adapter.Fill(EmployeeDS, “Employees”);
return EmployeeDS;
}
}

  1. Create a file in C:\Inetpub\wwwroot called web.config. Copy this into the file

<configuration>
<system.web>
<webServices>
<protocols>
<add name=”HttpGet”/>
<add name=”HttpPost”/>
</protocols>
</webServices>
</system.web>
</configuration>

  1. Verify that the Webservice is working by opening a browser and entering the following address. Change the server name to yours.

http://tlkadconnect1/WebServices/DEMOContosoDB.asmx/GetEmployees

You will get this out put

Creating the ECMA2 Dll file

  1. In the Synchronization Manager, at the top, select Management Agents.
  2. At the top, select Actions, select Create Extension Projects, and choose Extensible Connectivity 2.0 Extension. This will open a Create Extension Project dialog box.
  3. In the Create Extension Project dialog box, next to Programming Language: select Visual C#
  4. In the Create Extension Project dialog box, next to Visual Studio Version: select Visual Studio 2010.
  5. In the Create Extension Project dialog box, next to Project Name: enter WSC1_ECMA2.
  6. Click OK. This will launch Visual Studio 2010.
  7. On the right, at the top, under the Solutions Explorer, double-click WSC1_ECMA2. This will open the file with code that has already been written.
  8. Copy and paste this code (overwrite what is there)

using System;
using System.Data;
using System.IO;
using System.Xml;
using System.Text;
using System.Collections.Specialized;
using Microsoft.MetadirectoryServices;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Net;
namespace FimSync_Ezma
{
public class EzmaExtension :
//IMAExtensible2CallExport,
IMAExtensible2CallImport,
//IMAExtensible2FileImport,
//IMAExtensible2FileExport,
//IMAExtensible2GetHierarchy,
IMAExtensible2GetSchema,
IMAExtensible2GetCapabilities,
IMAExtensible2GetParameters
//IMAExtensible2GetPartitions
{
private int m_importDefaultPageSize = 1200;
private int m_importMaxPageSize = 1500;
public string Password;
public string Username;
public string path;
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
//
}
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 int ImportDefaultPageSize
{
get
{
return m_importDefaultPageSize;
}
}
public int ImportMaxPageSize
{
get
{
return m_importMaxPageSize;
}
}
public CloseImportConnectionResults CloseImportConnection(CloseImportConnectionRunStep importRunStep)
{
return new CloseImportConnectionResults();
}
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(“path”, “”));
configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(“Username”, “”));
configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(“Password”, “”));
break;
//case ConfigParameterPage.Global:
//    break;
//case ConfigParameterPage.Partition:
//    break;
//case ConfigParameterPage.RunStep:
//    break;
}
return configParametersDefinitions;
}
public GetImportEntriesResults GetImportEntries(GetImportEntriesRunStep importRunStep)
{
List<CSEntryChange> csentries = new List<CSEntryChange>();
GetImportEntriesResults importReturnInfo;
// Create the web request
HttpWebRequest request = WebRequest.Create(this.path) as HttpWebRequest;
// Get response
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
//load data into dataset
DataSet da = new DataSet();
da.ReadXml(response.GetResponseStream());
for (int i = 0; i <= da.Tables[“Employees”].Rows.Count – 1; i++)
{
myFirst = da.Tables[“Employees”].Rows[i].ItemArray.GetValue(0).ToString().Trim();
myLast = da.Tables[“Employees”].Rows[i].ItemArray.GetValue(1).ToString().Trim();
myEmail = da.Tables[“Employees”].Rows[i].ItemArray.GetValue(2).ToString().Trim();
myEmpID = da.Tables[“Employees”].Rows[i].ItemArray.GetValue(3).ToString().Trim();
myFull = da.Tables[“Employees”].Rows[i].ItemArray.GetValue(4).ToString().Trim();
myAccount = da.Tables[“Employees”].Rows[i].ItemArray.GetValue(5).ToString().Trim();
CSEntryChange csentry1 = CSEntryChange.Create();
csentry1.ObjectModificationType = ObjectModificationType.Add;
csentry1.ObjectType = “Person”;
csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(“Firstname”, myFirst));
csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(“Lastname”, myLast));
csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(“Email”, myEmail));
csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(“EmployeeID”, myEmpID));
csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(“Fullname”, myFull));
csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(“AccountName”, myAccount));
csentries.Add(csentry1);
}
}
importReturnInfo = new GetImportEntriesResults();
importReturnInfo.MoreToImport = false;
importReturnInfo.CSEntries = csentries;
return importReturnInfo;
}
public Schema GetSchema(KeyedCollection<string, ConfigParameter> configParameters)
{
Microsoft.MetadirectoryServices.SchemaType personType = Microsoft.MetadirectoryServices.SchemaType.Create(“Person”, false);
//myname = configParameters[“myname”].Value;
string myattribute = “Firstname”;
string myattributes = “Email”;
string myattribute2 = “Lastname”;
string myattribute3 = “AccountName”;
string myattribute4 = “EmployeeID”;
string myattribute5 = “Fullname”;
if (myattribute == “Firstname”)
{
personType.Attributes.Add(SchemaAttribute.CreateAnchorAttribute(myattribute, AttributeType.String));
}
if (myattributes == “Email”)
{
personType.Attributes.Add(SchemaAttribute.CreateAnchorAttribute(myattributes, AttributeType.String));
}
if (myattribute2 == “Lastname”)
{
personType.Attributes.Add(SchemaAttribute.CreateAnchorAttribute(myattribute2, AttributeType.String));
}
if (myattribute3 == “AccountName”)
{
personType.Attributes.Add(SchemaAttribute.CreateAnchorAttribute(myattribute3, AttributeType.String));
}
if (myattribute4 == “EmployeeID”)
{
personType.Attributes.Add(SchemaAttribute.CreateAnchorAttribute(myattribute4, AttributeType.String));
}
if (myattribute5 == “Fullname”)
{
personType.Attributes.Add(SchemaAttribute.CreateAnchorAttribute(myattribute5, AttributeType.String));
}
Schema schema = Schema.Create();
schema.Types.Add(personType);
return schema;
}
public OpenImportConnectionResults OpenImportConnection(KeyedCollection<string, ConfigParameter> configParameters, Schema types, OpenImportConnectionRunStep importRunStep)
{
this.path = configParameters[“path”].Value;
this.Username = configParameters[“Username”].Value;
this.Password = configParameters[“Password”].Value;
return new OpenImportConnectionResults();
}
public ParameterValidationResult ValidateConfigParameters(KeyedCollection<string, ConfigParameter> configParameters, ConfigParameterPage page)
{
ParameterValidationResult myResults = new ParameterValidationResult();
return myResults;
}
};
}

  1. At the top, select Build and choose Build Solution from the drop-down. At the bottom, in the Output window you should see Build: 1 succeeded.

Create the Webservice ECMA2 connector

  1. In the Synchronization Manager, click Management Agents
  2. In the Synchronization Service, at the top, select Management Agents and over on the right, under Actions, select Create. This will open a Create Management Agent wizard.
  3. On the Create Management Agent screen, next to Management Agent for: select Extensible Connectivity 2
  4. On the Create Management Agent screen, next to Name: enter WSC1_ECMA2

  1. Remove the check from Run this management agent in a separate process. This will allow for debugging should the need arise.
  2. Click Next.
  3. On the Select Extension DLL screen, click Browse and select WSC_ECMA2.dll. Click OK.
  4. On the Select Extension DLL screen, click Refresh interfaces. This will populate the box below. It should support Import

  1. Click Next.
  2. On the Connectivity screen, next to path enter: the Webservice url you have already tested
  3. On the Connectivity screen, next to username enter: the id that you tested with
  4. On the Connectivity screen, next to password enter: password

  1. Click Next
  2. On the Configure Partitions and Hierarchies screen, leave the defaults.

  1. Click Next.
  2. On the Select Object Types screen, select Person.

  1. Click Next.
  2. On the Select Attributes screen, select all six.

  1. Click Next.
  2. On the Configure Anchors screen, click Specify Anchor. This will open a Set Anchor dialog box.
  3. On the Set Anchor dialog box, you may see several attributes there, click specify anchor and remove all attributes except employeeid and click Add>. Click OK.

  1. Click Next.
  2. On the Configure Connector Filter screen, click Next.

  1. On the Configure Join and Projection Rules screen, click New Projection Rule. This will open a Projection dialog box.
  2. On the Projection dialog box, verify Declared is selected.
  3. On the Projection dialog box, verify Person is in the box next to Metaverse object type.
  4. Click OK.
  5. On the Configure Join and Projection Rules screen, click New Join Rule. This will open a Join Rule for Person dialog box.
  6. Under Data source attribute select EmployeeID.
  7. Under Metaverse attribute select employeeID.
  8. Click OK. This will bring up a dialog box that states you are attempting to join a non-indexed metaverse attribute. Click OK. Click OK.

  1. Click Next
  2. Configure Attribute flow as shown

  1. Click Next
  2. On the Configure Deprovisioning screen, select “Make them disconnectors”

  1. Click Next
  2. On the Configure Extensions screen click finish.

  1. Configure Full Import run profile
  2. Run the Import, it should be successful and you can test sync via the preview screen to see that the person is provisioned to the MV.

Advertisements

3 thoughts on “Creating a SOAP XML Webservice and a FIM 2010 R2/MIM 2016 ECMA2 connector

  1. Pingback: Creating a SOAP XML Webservice to update HR DB via Export from FIM 2010 R2/MIM 2016 ECMA2 connector | tlktechidentitythoughts

  2. Pingback: Setting up SAP Server and publishing functions via Webservice | TLKTECHSAPTHOUGHTS

  3. Pingback: Consume SAP Webservice data with DotNet C# ECMA2 MIM Connector | tlktechidentitythoughts

Leave a Reply

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

WordPress.com Logo

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

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s