Rules Extensions – Basic MV Extensions
In continuation of our Rules Extensions series we will begin to look at the MV Extension or rather the Metaverse Extension. no matter how you get the attributes into the Metaverse, either Direct Attribute Flows, via on the MA itself or using Sync Rules or even more complex attribute flows using an MA rules extension or once again sync rules and in some cases if you were using the portal and sync rules you might even be using workflows within the portal to manipulate data to be pushed to the Metaverse. I had a colleague that use to call this process “massaging the data”.
This post like the most of the postings on this blog are not to say one method is better than the other but rather provide insight into options.
For this post we will begin building a basic MV Extension with very little complexity, in short the following code looks at the sAMAccountName of the source AD Object to determine what type of account it is. This is done with the following helper file
private static string getSuffix(MVEntry mventry)
{
string suffix = "";
string AccountName = mventry["accountName"].StringValue;
string[] tokens = AccountName.Split('-');
switch (tokens.Length)
{
case 0: suffix = "Standard"; break;
case 1: suffix = tokens[1]; break;
default: suffix = "OTHER"; break;
}
return suffix.ToUpper();
The above helper function returns the suffix which is a generic term I use in a lot of my code which is usually the last segment of an account name after a specified character in this example I'm using “-“
The next piece of the code i use to set up the object to be provisioned is a bool variable I call “shouldProvision”
if (CManagementAgent.Connectors.Count == 1 && FManagementAgent.Connectors.Count == 0)
{
if (suffix.Equals("STANDARD") ||
suffix.Equals("ADMIN") ||
suffix.Equals("SEC") ||
suffix.Equals("DEV") ||
suffix.Equals("TEST"))
{
shouldProvision = true;
}
}
The code looks at the value of the variable “suffix” that we previously defined and if it matches one of the conditions, the “shouldProvision” variable is set to “true”
--- Example Code below ---
using System;
using System.Diagnostics;
using System.Globalization;
using System.Security.Principal;
using Microsoft.MetadirectoryServices;
namespace Mms_Metaverse
{
/// <summary>
/// Summary description for MVExtensionObject.
/// </summary>
public class MVExtensionObject : IMVSynchronization
{
public MVExtensionObject()
{
//
// TODO: Add constructor logic here
//
}
void IMVSynchronization.Initialize()
{
//
// TODO: Add initialization logic here
//
}
void IMVSynchronization.Terminate()
{
//
// TODO: Add termination logic here
//
}
void IMVSynchronization.Provision(MVEntry mventry)
{
//Trace.WriteLine("******************************* at top of provision");
// Change Strings below to match the name of your MAs in your Synchronization Service, Comment any extra variables.
string ADMA1 = "Contoso ADMA";
string ADMA2 = "Fabrikam ADMA";
string ADMA3 = "Fabrikam SPMA";
//string ADMA3 = "RepoDB";
//string FIMMA = "FIMMA";
string user = "user";
string group = "group";
string contact = "contact";
string fsp = "foreignSecurityPrincipal";
int Connectors;
ReferenceValue DN;
string RDN;
CSEntry connectedObject;
string parentOU;
//CSEntry csentry;
string CN;
ConnectedMA CManagementAgent = mventry.ConnectedMAs[ADMA1];
ConnectedMA FManagementAgent = mventry.ConnectedMAs[ADMA2];
ConnectedMA SPFManagementAgent = mventry.ConnectedMAs[ADMA3];
CSEntry CcsentryU = CManagementAgent.Connectors.StartNewConnector(user);
CSEntry CcsentryG = CManagementAgent.Connectors.StartNewConnector(group);
CSEntry CcsentryC = CManagementAgent.Connectors.StartNewConnector(contact);
CSEntry FcsentryU = FManagementAgent.Connectors.StartNewConnector(user);
CSEntry FcsentryG = FManagementAgent.Connectors.StartNewConnector(group);
CSEntry FcsentryC = FManagementAgent.Connectors.StartNewConnector(contact);
CSEntry SPFcsentryU = SPFManagementAgent.Connectors.StartNewConnector(user);
CSEntry SPFcsentryG = SPFManagementAgent.Connectors.StartNewConnector(group);
CSEntry SPFcsentryF = SPFManagementAgent.Connectors.StartNewConnector(fsp);
switch (mventry.ObjectType.ToLower())
{
#region case "person":
case "person":
{
string suffix = getSuffix(mventry);
//string suffix = "STANDARD";
// Disconnect Person Object from MV when todays date is greater or equal to the deprovisionDate set in the MV.
// deprovisionDate is a custom attribute that needs to be created in the MV
if (mventry.ConnectedMAs[ADMA1].Connectors.Count == 0 && mventry["deprovisionDate"].IsPresent)
{
DateTime myDate = DateTime.Parse(mventry["deprovisionDate"].Value);
if (myDate <= DateTime.Now)
{
FManagementAgent.Connectors.DeprovisionAll();
SPFManagementAgent.Connectors.DeprovisionAll();
}
}
// Provision disabled User Object to Managment Agent ADMA2 ("Fabrikam ADMA")
bool shouldProvision = false;
if (CManagementAgent.Connectors.Count == 1 && FManagementAgent.Connectors.Count == 0)
{
if (suffix.Equals("STANDARD") ||
suffix.Equals("ADMIN") ||
suffix.Equals("SEC") ||
suffix.Equals("DEV") ||
suffix.Equals("TEST"))
{
shouldProvision = true;
}
}
Connectors = FManagementAgent.Connectors.Count;
parentOU = "CN=Users";
string SAOU = "OU=MIMUsers,OU=MIMObjects";
string ADMINOU = "OU=Admins";
string TESTERSOU = "OU=Test Accounts";
string domainDN = "DC=Fabrikam,DC=com";
//string SPGroups = "SP Groups";
string fspOU = "CN=ForeignSecurityPrincipals";
string provDN;
string AccountName = mventry["accountName"].StringValue;
if (suffix == "STANDARD")
{
parentOU = SAOU;
}
else if (suffix == "ADMIN" || suffix == "SEC")
{
parentOU = ADMINOU;
}
else if (suffix == "TEST" || suffix == "DEV")
{
parentOU = TESTERSOU;
}
provDN = parentOU + "," + domainDN;
if (mventry["cn"].IsPresent)
{
CN = mventry["cn"].Value;
}
else
{
CN = mventry["accountName"].Value;
}
RDN = "CN=" + CN;
DN = FManagementAgent.EscapeDNComponent(RDN).Concat(provDN);
FcsentryU.DN = DN;
if (FManagementAgent.Connectors.Count == 0 && shouldProvision == true)
{
//Create a new connector space entry for the ADMA2 Managment Agent ("Fabrikam ADMA")
//setCSEntry(FcsentryU, Destination MA Attribute, MV Attribute);
setCSEntry(FcsentryU, "samAccountName", AccountName);
// Add additional initial attributes here
FcsentryU.CommitNewConnector();
}
else if (FManagementAgent.Connectors.Count == 1)
{
//Determine if Person Object needs to be renamed
connectedObject = FManagementAgent.Connectors.ByIndex[0];
if (!connectedObject.DN.ToString().Equals(DN.ToString()))
{
connectedObject.DN = DN;
}
}
// Provision fsp Objects to Management Agent ADMA3 (the name of your MAs in your Synchronization Service, Comment any extra variables.
Connectors = SPFManagementAgent.Connectors.Count;
// Requires custom string attribute "objectSidString"
if (mventry["objectSidString"].IsPresent)
{
CN = mventry["objectSidString"].Value;
}
else
{
byte[] objectSid = CcsentryU["objectSid"].BinaryValue;
CN = ConvertSidToString(objectSid);
}
provDN = fspOU + "," + domainDN;
RDN = "CN=" + CN;
DN = SPFManagementAgent.EscapeDNComponent(RDN).Concat(provDN);
SPFcsentryF.DN = DN;
if (CManagementAgent.Connectors.Count == 1)
{
if (SPFManagementAgent.Connectors.Count == 0)
{
setCSEntry(SPFcsentryF, "cn", CN);
SPFcsentryF.CommitNewConnector();
}
}
break;
}
#endregion case "person"
//case "group":
//{
//}
//break;
}
}
bool IMVSynchronization.ShouldDeleteFromMV(CSEntry csentry, MVEntry mventry)
{
//
// TODO: Add MV deletion logic here
//
throw new EntryPointNotImplementedException();
}
private void setCSEntry(CSEntry csentry, string attribName, string value)
{
if (value != null)
csentry[attribName].Value = value;
}
private string getDateString(DateTime date)
{
return date.ToString("yyy-MM-dd HH:mm:ss");
}
private static string ConvertSidToString(byte[] objectSid)
{
string objectSidString = "";
SecurityIdentifier SI = new SecurityIdentifier(objectSid, 0);
objectSidString = SI.ToString();
return objectSidString;
}
private static string getSuffix(MVEntry mventry)
{
string suffix = "";
string AccountName = mventry["accountName"].StringValue;
string[] tokens = AccountName.Split('-');
switch (tokens.Length)
{
case 1: suffix = "Standard"; break;
case 2: suffix = tokens[1]; break;
default: suffix = "OTHER"; break;
}
return suffix.ToUpper();
}
}
}
Questions? Comments? Love FIM/MIM so much you can’t even stand it?
>WE WANT TO HEAR FROM YOU<