MIM avoid creating duplicate users in Active Directory

Madhu Menon 96 Reputation points
2020-12-16T09:16:57.293+00:00

Hello Everybody,

I have MIM 2016 Version 4.5.412 (on-premise)
I have 4 Agents

CRM MA(Extensible Connectivity 2.0), FIM MA (FIM Service Man. ag.), DMZ MA (Extensible Connectivity 2.0),AD MA (Active Directory Domain Services)
I use MVExtension.dll
I have extension dll on CRM MA and DMZ MA

I create users in my CRM, CRM MA, handles the request, and creates users in my DMZ MA (Active Directory-OU CRM Users).
The above function works fine.

There are other applications that create users in DMZ AD (OU External Users)(same AD & Domain as above)
Now if CRM User creates user in CRM an existing user with same (firstname,lastname,email) that exists in OU External Users, then i want to perform a JOIN, and move that user from OU External Users to OU CRM Users.

Solutions i tried

  1. When existing External user is created in CRM. i check in MVExtension.cs, if user exists move the user from OU External Users to OU CRM Users. csentry.CommitNewConnector is in try catch(objectAlreadyExistsException) has //nothing. But on DMZ MA it fails with "export failed while applying to Active Directory". The error occurs because user already exists in AD
  2. I modified DMZ MA Extension, csentrychange.objectModificationType from Add to Update. Got error "System.InvalidOperation". ObjectModificationType already exists.

I have tried several ways with failure, i hope the community will be able to guide me a proper way of doing it.
I would appreciate any input from any of you experts on MIM.

Regards,
Madhu

Microsoft Identity Manager
Microsoft Identity Manager
A family of Microsoft products that manage a user's digital identity using identity synchronization, certificate management, and user provisioning.
707 questions
{count} votes

Accepted answer
  1. Madhu Menon 96 Reputation points
    2020-12-22T13:45:57.437+00:00

    Hello Everybody,

    I found half solution to my above problem.
    As i have mentioned above, when my DMZ AD ECMA2- Export no-limit runs, i get error "export failed while applying to Active Directory". I created NEW JOIN RULES on my DMZ AD ECMA management agent. The rules had 2 joins.

    1. givenName =>FirstName
    2. sn=> LastName
    3. samAccountName=>accountName

    When my DMZ AD ECMA2 - Full Import and Full Synchronization runs, it finds the exists AD User in DMZ Active Directory and makes the JOIN. I have an error, but finally the JOIN happens, and all records are connected properly.

    I hope this post will help others in Future.

    Best Regards,
    Madhu

    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. Madhu Menon 96 Reputation points
    2020-12-17T12:50:39.247+00:00

    Hello,

    Sharing MVExtension.cs
    49194-image.png

        void ProvisionExternalUser(string maName, MVEntry mventry)  
        {  
              
            //this method updates the dn and populates the desired attributes on first synchronization  
            var managementAgent = mventry.ConnectedMAs[maName];  
            var connectors = managementAgent.Connectors.Count;  
    
            //Has the object been provisioned?  
            if (connectors == 0)  
            {  
                //Is the object ready for provisioning? / includes a case when the AD account was accidentally deleted in AD (re-provisions the user, password is re-generated in ADMA extension)  
                if (mventry["initialPassword"].IsPresent  
                    && mventry["employeeType"].IsPresent && mventry["employeeType"].StringValue.Equals("External", StringComparison.InvariantCultureIgnoreCase)  
                    && mventry["employeeStatus"].IsPresent && mventry["employeeStatus"].StringValue.Equals("Active", StringComparison.InvariantCultureIgnoreCase)  
                    && mventry["crmSyncStatus"].IsPresent && (mventry["crmSyncStatus"].StringValue.Equals("NonEffective", StringComparison.InvariantCultureIgnoreCase) || mventry["crmSyncStatus"].StringValue.Equals("Effective", StringComparison.InvariantCultureIgnoreCase))  
                    )  
                {  
                    var isGeneric = mventry["credentialType"].IsPresent && mventry["credentialType"].StringValue.Equals("generic", StringComparison.InvariantCultureIgnoreCase);  
    
                    //Provision object  
                    var csentry = managementAgent.Connectors.StartNewConnector("user");  
    
                    //perpare firstName, lastName and cn  
                    var lastname = (mventry["lastName"].IsPresent) ? mventry["lastName"].StringValue : "";  
                    var firstname = (mventry["firstName"].IsPresent) ? mventry["firstName"].StringValue : "";  
    
                    if (!String.IsNullOrEmpty(firstname))  
                    {  
                        var ti = new CultureInfo("en-US", false).TextInfo;  
                        firstname = ti.ToTitleCase(firstname);  
                    }  
    
                    var strB = new StringBuilder();  
                    if (!String.IsNullOrEmpty(firstname))  
                    {  
                        strB.AppendFormat("{0} ", firstname);  
                    }  
                    strB.Append(lastname.ToUpperInvariant());  
    
                    if (isGeneric)  
                    {  
                        strB.AppendFormat(" {0}", GenericSuffix);  
                    }  
    
                    var cnString = strB.ToString();  
    
                    //initialize common attributes  
    
                    csentry["userAccountControl"].IntegerValue = 0x200; //Normal Account  
                    csentry["pwdLastSet"].Value = "0"; //Change Password at first logon  
                    csentry["unicodePwd"].Value = mventry["initialPassword"].StringValue; //set initial password  
                      
    
                    if (ProvisionUniqueId)  
                    {  
                        csentry["objectGUID"].BinaryValue = Guid.NewGuid().ToByteArray();  
                    }  
                      
                    //initialize provisioning dependent on DN and samAccountName  
                    //the advanced logic is there for cases when more than one user is provisioned  
                    //to AD at the same time - the existence check is done in AD-GetAdId and then in MV-try,catch on commmit  
    
                    var provisioned = false;  
                    var iteration = 0;  
                    bool userExists = false;      
                    while (!provisioned)  
                    {  
                        //Get unique samAccountName  
                        var samAccountNameVal = this.GetAdId(firstname, lastname, isGeneric, ref iteration, ref userExists);  
    
                        var cn = String.Concat(cnString, " (", samAccountNameVal, ")");  
                        var rdn = String.Concat("CN=", cn);  
    
                        //Fill attributes of csentry  
                        csentry["cn"].Value = cn;  
                        csentry["sAMAccountName"].Value = samAccountNameVal;  
                        csentry["userPrincipalName"].Value = String.Concat(samAccountNameVal, "@", UpnSuffix);  
                        //set the DN  
                        csentry.DN = csentry.MA.EscapeDNComponent(rdn).Concat(RootOu);  
                        if (userExists)  
                            csentry["description"].Value = "userExistsInAD";  
                        try  
                        {  
                            //Finish new connector   
                            csentry.CommitNewConnector();                
                            provisioned = true;  
                        }  
                        catch (ObjectAlreadyExistsException)  
                        {  
                            //if (iteration > MaxProvisioningAttempts)  
                            //{  
                            //    //exceeded number of attempts  
                            //    throw;  
                            //}  
                        }  
                    }  
                }  
            }  
    
            else if (connectors > 1)  
            {  
                throw new UnexpectedDataException("Too many connectors on Management Agent " + maName);  
            }  
        }  
    

    DMZ MA Extension
    DMZ ECMA2
    49175-image.png

    ActiveDirectoryContext.cs
    public IEnumerable<CSEntryChangeResult> PutExportCsObjects(IEnumerable<CSEntryChange> changes)
    {
    var csEntryChangeResults = new List<CSEntryChangeResult>();

            foreach (var csEntryChange in changes)  
            {  
                var exportObject = exportObjectFactory.CreateExportObject(csEntryChange);  
    
                csEntryChangeResults.Add(this.writer.Write(exportObject));  
            }  
    
            return csEntryChangeResults;  
        }  
    

    ExportObjectFactory.cs

        public ActiveDirectoryExportObject CreateExportObject(CSEntryChange csEntry)  
        {  
            switch (csEntry.ObjectType.ToLowerInvariant())  
            {  
                case "user":  
                    return new UserExportObject(csEntry, connection, mapper);  
                case "group":  
                    return new GroupExportObject(csEntry, connection, mapper);  
                case "foreignsecurityprincipal":  
                    return new FspExportObject(csEntry, connection, mapper, FspDecoyGroupDn);  
                default:  
                    return new ActiveDirectoryExportObject(csEntry, connection, mapper);  
            }  
        }  
    

    class UserExportObject : ActiveDirectoryExportObject
    {
    public UserExportObject(CSEntryChange csEntry, IActiveDirectoryConnection connection, ActiveDirectoryObjectMapper mapper)
    : base(csEntry, connection, mapper)
    {

        }  
    
        public override byte[] Create()  
        {  
            using (var parentDirEntry = this.connection.GetParentDirectoryEntryFromDn(this.Dn))  
            {  
                using (var newObj = parentDirEntry.Children.Add(this.Rdn, this.ObjectType).Wrap())  
                {  
    
                    mapper.MapActiveDirectoryExportObjectToExistingDirectoryEntry(this, newObj);  
    
                    newObj.CommitChanges();  
    
                    mapper.MapActiveDirectoryExportObjectToExistingDirectoryEntrySecondPass(this, newObj);  
    
                    return newObj.Guid.ToByteArray();  
                }  
            }  
        }  
    
        public override byte[] Update()  
        {  
            if (!this.connection.DirectoryEntryExistsFromDn(this.Dn))  
            {  
                throw new ObjectMissingException(String.Format("Object {0} does not exist in Active Directory.", this.Dn));  
            }  
    
            using (var adObject = this.connection.GetDirectoryEntryFromDn(this.Dn))  
            {  
                if (this.IsRenamed)  
                {  
                    string attributeDNValue = this.GetSingleValuedAttributeValue("DN") as string;  
                    adObject.Rename(Utils.GetRdnFromDn(attributeDNValue));  
    
                    if (attributeDNValue.Contains("DisabledAccounts"))  
                    {  
                        var adnewObject = this.connection.GetDirectoryEntryFromDn(attributeDNValue.Substring(attributeDNValue.IndexOf(",") + 1));  
    
                        adObject.MoveTo(adnewObject);  
                    }  
                }  
    
                mapper.MapActiveDirectoryExportObjectToExistingDirectoryEntry(this, adObject);  
    
                adObject.CommitChanges();  
    
                mapper.MapActiveDirectoryExportObjectToExistingDirectoryEntrySecondPass(this, adObject);  
    
                return adObject.Guid.ToByteArray();  
            }  
        }  
    }  
    

    With the above code, MIM Export throws below error.
    49184-image.png

    49125-image.png
    I am not sure how much this code helps you understand my project. Please feel free to contact me for more details.
    I don't know if its normal, but if you would like a Teams meeting, i can arrange that to show you my code in detail.

    Kind Regards,
    Madhu

    0 comments No comments

  2. Vicky Wang 2,731 Reputation points
    2020-12-22T07:04:10.647+00:00

    Hi,
    Thank you for posting in our forum.
    According to your description, this problem seems to have nothing to do with ADDS. I can want to delete this tag. You can seek help from identity.
    Thank you for your understanding and support
    Best wishes
    Vicky

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.