Step 7: Create the AD_ECMA2 DLL

Creating and configuring the AD_ECMA2 Extensible Connectivity 2.0 Management Agent for the test lab consists of the following:

  • Create the AD_ECMA2 DLL

Create the AD_ECMA2 DLL

Now we will create our AD_ECMA2 DLL for our AD_ECMA2 management agent. This will be done using the code provided. A full copy of the code is provided in Appendix C: AD_ECMA2 Source Code.

To Create the AD_ECMA2 DLL

  1. Log on to FIM1 as CORP\Administrator.

  2. Click Start, select All Programs, select Microsoft Forefront Identity Manager and double-click Synchronization Service.

  3. In the Synchronization Service, at the top, select Management Agents.

  4. 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.

  5. In the Create Extension Project dialog box, next to Programming Language: select Visual C#

  6. In the Create Extension Project dialog box, next to Visual Studio Version: select Visual Studio 2010.

  7. In the Create Extension Project dialog box, next to Project Name: enter AD_ECMA2.

  8. Click OK. This will launch Visual Studio 2010.

    Note

    If this is the first time running Visual Studio you will receive a Choose Default Environmental Settings Dialog box. Select General Development Settings and click Start Visual Studio.

  9. On the right, at the top, under the Solutions Explorer, double-click AD_ECMA2. This will open the file with code that has already been written.

  10. In the code, at the top, add using System.Collections.Generic, using System.Collections.ObjectModel, using System.Data.SqlClient and using System.Data. The code should look similar to below:

    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.DirectoryServices;
    using Microsoft.MetadirectoryServices;
    using System;
    
    namespace FimSync_Ezma
    
  11. In the code, at the top, un-remark the following interfaces: IMAExtensible2CallImport, IMAExtensible2CallExport, IMAExtensible2GetHierarchy, IMAExtensible2GetSchema, IMAExtensible2GetCapabilities, IMAExtensible2GetParameters, and IMAExtensible2GetPartitions. The code should look similar to the code below:

    namespace FimSync_Ezma
    {
        public class EzmaExtension :
        IMAExtensible2CallExport,
        IMAExtensible2CallImport,
            //IMAExtensible2FileImport,
            //IMAExtensible2FileExport,
        IMAExtensible2GetHierarchy,
        IMAExtensible2GetSchema,
        IMAExtensible2GetCapabilities,
        IMAExtensible2GetParameters,
        IMAExtensible2GetPartitions
        {
    
  12. Now below our interfaces we will declare some constants that will be used throughout our code. Add the following code:

        IMAExtensible2GetPartitions
        {
    
            private int m_importDefaultPageSize = 12;
            private int m_importMaxPageSize = 50;
            OperationType m_importOperation;
            OperationType m_exportOperation;
            private int m_exportDefaultPageSize = 10;
            private int m_exportMaxPageSize = 20;
            SearchResultCollection results;
            DirectoryEntry dirEntry;
            public string userName;
            public string domain;
            public string dc;
            public string password;
            ADHelper myADHelper;
            public string myADpath;
            public string isSingle;
            public string ADDataType;
            public string myEmpID;
    
  13. Now, down below the constructor, add the following code to implement the Management Agents capabilities. This will define what are management agent is capable of.

            public EzmaExtension()
            {
                //
                // TODO: Add constructor logic here
                //
            }
    
            public MACapabilities Capabilities
            {
                get
                {
                    MACapabilities myCapabilities = new MACapabilities();
    
                    myCapabilities.ObjectConfirmation = MAObjectConfirmation.Normal;
                    myCapabilities.ExportType = MAExportType.AttributeReplace;
                    myCapabilities.NoReferenceValuesInFirstExport = true;
                    myCapabilities.FullExport = true;
                    myCapabilities.ConcurrentOperation = true;
                    myCapabilities.ObjectRename = false;
                    myCapabilities.DeleteAddAsReplace = true;
                    myCapabilities.DeltaImport = false;
                    myCapabilities.DistinguishedNameStyle = MADistinguishedNameStyle.Ldap;
                    myCapabilities.NoReferenceValuesInFirstExport = false;
                    myCapabilities.Normalizations = MANormalizations.None;
    
                    return myCapabilities;
                }
            }
    
  14. Now, below the management agent’s capabilities, add the following method to implement the parameters of our management agent. This will allow us to enter a user name and password to use for connecting to our domain.

    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("User Name", ""));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateEncryptedStringParameter("Password", ""));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter("Domain", ""));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter("Domain FQDN", ""));
    
    
    
    
                        break;
    
    
                    case ConfigParameterPage.Global:
    
    
                        break;
    
                    case ConfigParameterPage.Partition:
                        break;
    
                    case ConfigParameterPage.RunStep:
    
                        break;
                }
    
                return configParametersDefinitions;
            }
    
  15. Now, below the GetConfigParameter method, add the following code to implement the ValidateConfigParameters method. This is the method where we would configure code to validate the configuration parameters. For purposes of this test lab we don’t validate the parameters but we include this method because it is required to implement IMAExtensible2GetParameters.

    public ParameterValidationResult ValidateConfigParameters(KeyedCollection<string, ConfigParameter> configParameters,
                                                                       ConfigParameterPage page)
            {
    
    
                ParameterValidationResult myResults = new ParameterValidationResult();
    
                return myResults;
    
            }
    
  16. Now below the ValidateConfigParameters method, add the following code to implement the GetSchema method. This will be used to discover our schema while the management agent is being setup. The schema discovery in this management agent is limited to just users in AD. It uses the helper class that is created at the end of this section. The code first creates a schematype for a user. Then the code makes a directory entry to the AD schema for the user object and then looks up all of the attributes associated with the user object. Then it determines what type of attribute it is. For example is it a string, a boolean, or an integer. Then it creates the attributes based on this information and adds them to our user schemtype. Finally, it returns the schema.

    public Schema GetSchema(KeyedCollection<string, ConfigParameter> configParameters)
            {
    
    
                userName = configParameters["User Name"].Value;
                password = configParameters["Password"].Value;
                domain = configParameters["Domain"].Value;
                dc = configParameters["Domain FQDN"].Value;
    
    
    
    
                SchemaType userType = SchemaType.Create("user", false);
    
                string DN = (@"CN=User,CN=Schema,CN=Configuration,DC=corp,DC=contoso,DC=com");
    
                dirEntry = new DirectoryEntry(@"LDAP://" + dc + @"/" + DN, userName, password);
                myADHelper = new ADHelper();
                #region ForLoop
                List<string> myAttributeList = new List<string>();
    
                foreach (var prop in dirEntry.Properties.PropertyNames)
                {
    
    
                    #region MayContain
                    //Loop threw the values in mayContain
    
                    if (prop.ToString() == "mayContain")
                    {
    
                        List<string> mayContain = myADHelper.GetMayContain(dirEntry, prop.ToString());
    
                        foreach (string s in mayContain)
                        {
                            if (myAttributeList.Contains(s))
                            {
                            }
                            else
                            {
                                myAttributeList.Add(s);
                            }
                        }
    
                    }
    
                    #endregion
                    #region SystemMayContain
                    else if (prop.ToString() == "systemMayContain")
                    {
                        List<string> systemMayContain = myADHelper.GetSystemMayContain(dirEntry, prop.ToString());
    
                        foreach (string s in systemMayContain)
                        {
                            if (myAttributeList.Contains(s))
                            {
                            }
                            else
                            {
                                myAttributeList.Add(s);
                            }
                        }
    
    
                    }
    
    
                    #endregion
                    #region Else
                    else
                    {
    
                        string propName = prop.ToString();
                        if (myAttributeList.Contains(propName))
                        {
                        }
                        else
                        {
                            myAttributeList.Add(propName);
                        }
    
    
                    }
    
    
                    #endregion
    
    
                }
    
                #region SecurityPrincipal - systemMayContain
                List<string> secPalSystemMayContain = myADHelper.GetSecurityPrincipalSystemMayContain(dc, userName, password);
                foreach (string s in secPalSystemMayContain)
                {
                    if (myAttributeList.Contains(s))
                    {
                    }
                    else
                    {
                        myAttributeList.Add(s);
                    }
                }
    
                # endregion
                #region SecurityPrincipal - systemMustContain
                List<string> secPalSystemMustContain = myADHelper.GetSecurityPrincipalSystemMustContain(dc, userName, password);
                foreach (string s in secPalSystemMustContain)
                {
                    if (myAttributeList.Contains(s))
                    {
                    }
                    else
                    {
                        myAttributeList.Add(s);
                    }
                }
    
                # endregion
    
                #region MailRecipient - MayContain
                List<string> MailRecMayContain = myADHelper.GetMailRecipientMayContain(dc, userName, password);
                foreach (string s in MailRecMayContain)
                {
                    if (myAttributeList.Contains(s))
                    {
                    }
                    else
                    {
                        myAttributeList.Add(s);
                    }
                }
    
                # endregion
    
                #region MailRecipient - systemMayContain
                List<string> MailRecSystemMayContain = myADHelper.GetMailRecipientSystemMayContain(dc, userName, password);
                foreach (string s in MailRecSystemMayContain)
                {
                    if (myAttributeList.Contains(s))
                    {
                    }
                    else
                    {
                        myAttributeList.Add(s);
                    }
                }
    
                # endregion
    
                #region organizationalPerson - MayContain
                List<string> orgPersonMayContain = myADHelper.GetOrganizationalPersonMayContain(dc, userName, password);
                foreach (string s in orgPersonMayContain)
                {
                    if (myAttributeList.Contains(s))
                    {
                    }
                    else
                    {
                        myAttributeList.Add(s);
                    }
                }
    
                # endregion
    
                #region organizationalPerson - SystemMayContain
                List<string> orgPersonSystemMayContain = myADHelper.GetOrganizationalPersonSystemMayContain(dc, userName, password);
                foreach (string s in orgPersonSystemMayContain)
                {
                    if (myAttributeList.Contains(s))
                    {
                    }
                    else
                    {
                        myAttributeList.Add(s);
                    }
                }
    
                # endregion
    
                #region Person - SystemMayContain
                List<string> PersonSystemMayContain = myADHelper.GetPersonSystemMayContain(dc, userName, password);
                foreach (string s in PersonSystemMayContain)
                {
                    if (myAttributeList.Contains(s))
                    {
                    }
                    else
                    {
                        myAttributeList.Add(s);
                    }
                }
    
                # endregion
    
    
                #region AddToSchema
                foreach (string attrib in myAttributeList)
                {
                    myADpath = myADHelper.GetSchemaObjectPath(attrib);
                    isSingle = myADHelper.GetIsSingleValued(myADpath);
                    ADDataType = myADHelper.GetDataType(myADpath);
    
                    #region AddtoSchema6
    
                    if (userType.Attributes.Contains(attrib))
                    {
    
                    }
                    else
                    {
                        #region isSingle
                        if (isSingle == "True")
                        {
                            switch (ADDataType)
                            {
    
                                case "Boolean":
                                    userType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute(attrib, AttributeType.Boolean));
                                    break;
    
                                case "String":
                                    userType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute(attrib, AttributeType.String));
                                    break;
    
                                case "Object":
                                    userType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute(attrib, AttributeType.Reference));
                                    break;
    
                                case "Integer":
                                    userType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute(attrib, AttributeType.Integer));
                                    break;
    
                                default:
                                    break;
                            }
    
                        }
                        #endregion
                        #region isSingle_False
                        else if (isSingle == "False")
                        {
                            switch (ADDataType)
                            {
    
                                case "Boolean":
                                    userType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute(attrib, AttributeType.Boolean));
                                    break;
    
                                case "String":
                                    userType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute(attrib, AttributeType.String));
                                    break;
    
                                case "Object":
                                    userType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute(attrib, AttributeType.Reference));
                                    break;
    
                                case "Integer":
                                    userType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute(attrib, AttributeType.Integer));
                                    break;
    
                                default:
                                    break;
                            }
                        }
                        #endregion
                    }
                    #endregion
                }
                #endregion
    
    
                #endregion
    
                Schema schema = Schema.Create();
                schema.Types.Add(userType);
                return schema;
            }
    
  17. Now below the GetSchema method, add the following code to implement the GetHierarchy method. This method builds our Hierarchy and adds our ECMA2 container.

    public IList<HierarchyNode> GetHierarchy(KeyedCollection<string, ConfigParameter> configParameters, HierarchyNode parent)
            {
    
    
                IList<HierarchyNode> children = new List<HierarchyNode>();
    
                if ("DC=corp,DC=contoso,DC=com" == parent.DN)
                {
    
                    children.Add(HierarchyNode.Create("OU=ECMA2,DC=corp,DC=contoso,DC=com", "ECMA2"));
    
                }
    
                return children;
            }
    
  18. Now below the GetHierarchy method, add the following code to implement the GetPartitions method.

    public IList<Partition> GetPartitions(KeyedCollection<string, ConfigParameter> configParameters)
            {
    
                List<Partition> partitionList = new List<Partition>();
    
                partitionList.Add(Partition.Create(Guid.NewGuid(), "DC=corp,DC=contoso,DC=com"));
    
                return partitionList;
            }
    
  19. Now, below the GetPartitions method add the following code to implement the OpenImportConnectionResults method. This will initialize our connection information with Active Directory.

      public OpenImportConnectionResults OpenImportConnection(
                                                   KeyedCollection<string, ConfigParameter> configParameters,
                                                   Schema types,
                                                   OpenImportConnectionRunStep importRunStep)
            {
                string DN = (@"OU=ECMA2,DC=corp,DC=contoso,DC=com");
                string userName = configParameters["User Name"].Value;
                string password = configParameters["Password"].Value;
                string dc = configParameters["Domain FQDN"].Value;
                dirEntry = new DirectoryEntry(@"LDAP://" + dc + @"/" + DN, userName, password);
                m_importOperation = importRunStep.ImportType;
    
    
                return new OpenImportConnectionResults();
    
            }
    
  20. Now, below OpenImportConnectionResults and the following code to implement GetImportEntriesResults. This is the workhorse of a call-based MA with regard to importing. This code searches our ECMA2 OU for all users and then creates csentries and returns these.

       public GetImportEntriesResults GetImportEntries(GetImportEntriesRunStep importRunStep)
            {
    
                DirectorySearcher mySearcher = new DirectorySearcher(dirEntry);
                mySearcher.Filter = "(&(ObjectCategory=user))";
                results = mySearcher.FindAll();
    
                GetImportEntriesResults importReturnInfo;
    
                List<CSEntryChange> csentries = new List<CSEntryChange>();
    
                // Full Import.
    
                if (OperationType.Full == m_importOperation)
                {
    
                    foreach (SearchResult myResult in results)
                    {
    
    
                        string myDN = myResult.Properties["distinguishedName"][0].ToString();
                        string myLastName = myResult.Properties["sn"][0].ToString();
                        string myEmpID = myResult.Properties["employeeID"][0].ToString();
                        string mySamAcct = myResult.Properties["sAMAccountName"][0].ToString();
                        string myMail = myResult.Properties["mail"][0].ToString();
                        string myFirstName = myResult.Properties["givenName"][0].ToString();
                        string myFullName = myResult.Properties["displayName"][0].ToString();
                        CSEntryChange csentry1 = CSEntryChange.Create();
    
                        csentry1.ObjectModificationType = ObjectModificationType.Add;
                        csentry1.ObjectType = "user";
                        csentry1.DN = myDN;
    
    
                        csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("givenName", myFirstName));
                        csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("mail", myMail));
                        csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("sn", myLastName));
                        csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("sAMAccountName", mySamAcct));
                        csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("employeeID", myEmpID));
                        csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("displayName", myFullName));
                        csentries.Add(csentry1);
                    }
                }
    
                if (OperationType.Delta == m_importOperation)
                {
                }
    
    
    
    
    
                // Create the result
                importReturnInfo = new GetImportEntriesResults();
                importReturnInfo.MoreToImport = false;
                importReturnInfo.CSEntries = csentries;
    
    
                return importReturnInfo;
            }
    
  21. Now, below GetImportEntriesResults add the following code to implement CloseImportConnectionResults. This will close our connection.

    public CloseImportConnectionResults CloseImportConnection(CloseImportConnectionRunStep importRunStepInfo)
            {
    
                return new CloseImportConnectionResults();
            }
    
  22. Now, below CloseImportConnectionResults add the following code to implement ImportMaxPageSize and ImportDefaultPageSize.

        public int ImportMaxPageSize
            {
                get
                {
                    return m_importMaxPageSize;
                }
            }
    
            public int ImportDefaultPageSize
            {
                get
                {
                    return m_importDefaultPageSize;
                }
            }
    
  23. Below the ImportDefaultPageSize add the following code to implement the OpenExportConnection method. This method will initialize our connection for exports.

    public void OpenExportConnection(KeyedCollection<string, ConfigParameter> configParameters,
                                  Schema types,
                                  OpenExportConnectionRunStep exportRunStep)
            {
                string DN = (@"OU=ECMA2,DC=corp,DC=contoso,DC=com");
                userName = configParameters["User Name"].Value;
                password = configParameters["Password"].Value;
                dc = configParameters["Domain FQDN"].Value;
                dirEntry = new DirectoryEntry(@"LDAP://" + dc + @"/" + DN, userName, password);
    
            }
    
  24. Now, below OpenExportConnection and the following code to implement PutExportEntriesResults. This is the workhorse of a call-based MA with regard to exporting. This method is responsible for writing our information to AD. This method is responsible for writing our information to AD. This is done by taking the csentries that are passed in by the Synchronization Service and depending on the type of modification, whether it is object or attribute, a corresponding LDAP command is built and then executed on AD.

       public PutExportEntriesResults PutExportEntries(IList<CSEntryChange> csentries)
            {
                int i = 0;
    
                foreach (CSEntryChange csentryChange in csentries)
                {
                    myEmpID = csentries[i].AnchorAttributes[0].Value.ToString();
    
                    if (csentryChange.ObjectType == "user")
                    {
    
                        #region Add
                        if (csentryChange.ObjectModificationType == ObjectModificationType.Add)
                        {
                            DirectoryEntry newUser = dirEntry.Children.Add("CN=" + csentryChange.AttributeChanges["displayName"].ValueChanges[0].Value.ToString(), "user");
    
                            foreach (string attrib in csentryChange.ChangedAttributeNames)
                            {
    
                                newUser.Properties[attrib].Value = csentryChange.AttributeChanges[attrib].ValueChanges[0].Value.ToString();
    
                            }
                            newUser.Properties["employeeID"].Value = myEmpID;
                            newUser.CommitChanges();
                        }
                        #endregion
                        #region delete
                        else if (csentryChange.ObjectModificationType == ObjectModificationType.Delete)
                        {
                            // DirectoryEntry deleteEntry = null;
                            DirectoryEntry child = new DirectoryEntry("LDAP://" + csentryChange.DN.ToString());
                            dirEntry.Children.Remove(child);
                        }
                        #endregion
                        #region update
                        else if (csentryChange.ObjectModificationType == ObjectModificationType.Update)
                        {
                            DirectoryEntry updateUser = new DirectoryEntry("LDAP://" + csentryChange.DN.ToString());
    
                            if (updateUser != null)
                            {
                                foreach (string attribName in csentryChange.ChangedAttributeNames)
                                {
    
    
                                    if (csentryChange.AttributeChanges[attribName].ModificationType == AttributeModificationType.Add)
                                    {
                                        updateUser.Properties[attribName].Value = csentryChange.AttributeChanges[attribName].ValueChanges[0].Value.ToString();
                                    }
                                    else if (csentryChange.AttributeChanges[attribName].ModificationType == AttributeModificationType.Delete)
                                    {
                                        updateUser.Properties[attribName].Value = null;
                                    }
                                    else if (csentryChange.AttributeChanges[attribName].ModificationType == AttributeModificationType.Replace)
                                    {
                                        updateUser.Properties[attribName].Value = csentryChange.AttributeChanges[attribName].ValueChanges[0].Value.ToString();
                                    }
                                    else if (csentryChange.AttributeChanges[attribName].ModificationType == AttributeModificationType.Update)
                                    {
                                        updateUser.Properties[attribName].Value = csentryChange.AttributeChanges[attribName].ValueChanges[0].Value.ToString();
                                    }
    
    
    
    
                                }
    
                               updateUser.CommitChanges();
    
                            }
    
    
                        }
                        #endregion
    
                    }
    
                    i++;
    
                }
    
    
                PutExportEntriesResults exportEntriesResults = new PutExportEntriesResults();
    
                return exportEntriesResults;
            }
    
  25. Now, below PutExportEntriesResults add the following code to implement CloseExportConnection. This will close our connection.

      public void CloseExportConnection(CloseExportConnectionRunStep exportRunStep)
            {
    
            }
    
  26. Now below CloseExportConnection add the following code to implement ExportDefaultPageSize and ExportMaxPageSize.

            public int ExportDefaultPageSize
            {
                get
                {
                    return m_exportDefaultPageSize;
                }
                set
                {
                    m_exportDefaultPageSize = value;
                }
            }
    
            public int ExportMaxPageSize
            {
                get
                {
                    return m_exportMaxPageSize;
                }
                set
                {
                    m_exportMaxPageSize = value;
                }
            }
    
    
        };
    }
    
  27. Now we are going to add an additional class to our project. This class is call ADHelper and this class is a used to help with our AD lookups and discovery particularly around schema discovery.

    To add this class, in Visual Studio, on the right in the Solution Explorer, right-click the top of the AD_ECMA2 project and select Add, New Item. Select class, rename it to ADHelper and click Add. There will now be a new file in the AD_ECMA2 project.

    In Visual Studio, on the right in the Solution Explorer, double click the file and add the code below to it.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.DirectoryServices;
    
    namespace FimSync_Ezma
    {
        class ADHelper
        {
            SearchResultCollection results;
            SearchResult result;
            DirectoryEntry entry;
            DirectorySearcher mySearcher;
            public string DN;
            List<string> Attributes;
    
            public ADHelper()
            {
            }
    
            public string GetSchemaObjectPath(string displayName)
            {
                DN = (@"LDAP://CN=Schema,CN=Configuration,DC=corp,DC=contoso,DC=com");
                entry = new DirectoryEntry(DN);
                mySearcher = new DirectorySearcher(entry);
                mySearcher.Filter = "(&(lDAPDisplayName=" + displayName + "))";
                result = mySearcher.FindOne();
                string path = result.Path.ToString();
                return path;
            }
    
            public string GetIsSingleValued(string mypath)
            {
                DN = mypath;
                entry = new DirectoryEntry(DN);
                mySearcher = new DirectorySearcher(entry);
                string isSingle = entry.Properties["isSingleValued"].Value.ToString();
                return isSingle;
            }
    
            public string GetDataType(string mypath)
            {
                DN = mypath;
                string dtype = "string";
                entry = new DirectoryEntry(DN);
                mySearcher = new DirectorySearcher(entry);
    
                int dataType = Int32.Parse(entry.Properties["oMSyntax"].Value.ToString());
    
                switch (dataType)
                {
                    case 1:
                        dtype = "Boolean";
                        break;
    
                    case 2:
                        dtype = "Integer";
                        break;
    
                    case 4:
                        dtype = "String";
                        break;
    
                    case 6:
                        dtype = "Boolean";
                        break;
    
                    case 10:
                        dtype = "Integer";
                        break;
    
                    case 18:
                        dtype = "String";
                        break;
    
                    case 20:
                        dtype = "String";
                        break;
    
                    case 27:
                        dtype = "String";
                        break;
    
                    case 19:
                        dtype = "String";
                        break;
    
                    case 22:
                        dtype = "String";
                        break;
    
                    case 23:
                        dtype = "String";
                        break;
    
                    case 24:
                        dtype = "String";
                        break;
    
                    case 64:
                        dtype = "String";
                        break;
    
                    case 65:
                        dtype = "Integer";
                        break;
    
                    case 66:
                        dtype = "String";
                        break;
    
                    case 127:
                        dtype = "Object";
                        break;
    
                    default:
                        break;
    
                }
    
                return dtype;
    
    
            }
    
            public List<string> GetMayContain(DirectoryEntry dirEntry, string propName)
            {
                List<string> mayContain = new List<string>();
                var propValue = dirEntry.Properties[propName].Value;
                for (int i = 0; i <= dirEntry.Properties[propName].Count - 1; i++)
                {
                    mayContain.Add(((object[])(propValue))[i].ToString());
                }
    
                return mayContain;
            }
    
            public List<string> GetSystemMayContain(DirectoryEntry dirEntry, string propName)
            {
                List<string> maySystemContain = new List<string>();
                var propValue = dirEntry.Properties[propName].Value;
                for (int i = 0; i <= dirEntry.Properties[propName].Count - 1; i++)
                {
                    maySystemContain.Add(((object[])(propValue))[i].ToString());
                }
    
                return maySystemContain;
            }
    
            public List<string> GetSystemMustContain(DirectoryEntry dirEntry, string propName)
            {
                List<string> mustSystemContain = new List<string>();
    
                var propValue = dirEntry.Properties[propName].Value;
                for (int i = 0; i <= dirEntry.Properties[propName].Count - 1; i++)
                {
                    mustSystemContain.Add(((object[])(propValue))[i].ToString());
                }
    
                return mustSystemContain;
            }
    
            public List<string> GetSecurityPrincipalSystemMayContain(string dc, string userName, string password)
            {
                DN = (@"CN=Security-Principal,CN=Schema,CN=Configuration,DC=corp,DC=contoso,DC=com");
                entry = new DirectoryEntry(@"LDAP://" + dc + @"/" + DN, userName, password);
                Attributes = new List<string>();
    
                var propValue = entry.Properties["systemMayContain"].Value;
                for (int i = 0; i <= entry.Properties["systemMayContain"].Count - 1; i++)
                {
                    Attributes.Add(((object[])(propValue))[i].ToString());
                }
    
                return Attributes;
            }
    
            public List<string> GetSecurityPrincipalSystemMustContain(string dc, string userName, string password)
            {
                DN = (@"CN=Security-Principal,CN=Schema,CN=Configuration,DC=corp,DC=contoso,DC=com");
                entry = new DirectoryEntry(@"LDAP://" + dc + @"/" + DN, userName, password);
                Attributes = new List<string>();
    
                var propValue = entry.Properties["systemMustContain"].Value;
                for (int i = 0; i <= entry.Properties["systemMustContain"].Count - 1; i++)
                {
                    Attributes.Add(((object[])(propValue))[i].ToString());
                }
    
                return Attributes;
            }
    
            public List<string> GetMailRecipientMayContain(string dc, string userName, string password)
            {
                DN = (@"CN=Mail-Recipient,CN=Schema,CN=Configuration,DC=corp,DC=contoso,DC=com");
                entry = new DirectoryEntry(@"LDAP://" + dc + @"/" + DN, userName, password);
                Attributes = new List<string>();
    
                var propValue = entry.Properties["mayContain"].Value;
                for (int i = 0; i <= entry.Properties["mayContain"].Count - 1; i++)
                {
                    Attributes.Add(((object[])(propValue))[i].ToString());
                }
    
                return Attributes;
            }
    
            public List<string> GetMailRecipientSystemMayContain(string dc, string userName, string password)
            {
                DN = (@"CN=Mail-Recipient,CN=Schema,CN=Configuration,DC=corp,DC=contoso,DC=com");
                entry = new DirectoryEntry(@"LDAP://" + dc + @"/" + DN, userName, password);
                Attributes = new List<string>();
    
                var propValue = entry.Properties["systemMayContain"].Value;
                for (int i = 0; i <= entry.Properties["systemMayContain"].Count - 1; i++)
                {
                    Attributes.Add(((object[])(propValue))[i].ToString());
                }
    
                return Attributes;
            }
    
            public List<string> GetOrganizationalPersonMayContain(string dc, string userName, string password)
            {
                DN = (@"CN=Organizational-Person,CN=Schema,CN=Configuration,DC=corp,DC=contoso,DC=com");
                entry = new DirectoryEntry(@"LDAP://" + dc + @"/" + DN, userName, password);
                Attributes = new List<string>();
    
                var propValue = entry.Properties["mayContain"].Value;
                for (int i = 0; i <= entry.Properties["mayContain"].Count - 1; i++)
                {
                    Attributes.Add(((object[])(propValue))[i].ToString());
                }
    
                return Attributes;
            }
    
            public List<string> GetOrganizationalPersonSystemMayContain(string dc, string userName, string password)
            {
                DN = (@"CN=Organizational-Person,CN=Schema,CN=Configuration,DC=corp,DC=contoso,DC=com");
                entry = new DirectoryEntry(@"LDAP://" + dc + @"/" + DN, userName, password);
                Attributes = new List<string>();
    
                var propValue = entry.Properties["systemMayContain"].Value;
                for (int i = 0; i <= entry.Properties["systemMayContain"].Count - 1; i++)
                {
                    Attributes.Add(((object[])(propValue))[i].ToString());
                }
    
                return Attributes;
            }
    
            public List<string> GetPersonSystemMayContain(string dc, string userName, string password)
            {
                DN = (@"CN=Person,CN=Schema,CN=Configuration,DC=corp,DC=contoso,DC=com");
                entry = new DirectoryEntry(@"LDAP://" + dc + @"/" + DN, userName, password);
                Attributes = new List<string>();
    
                var propValue = entry.Properties["systemMayContain"].Value;
                for (int i = 0; i <= entry.Properties["systemMayContain"].Count - 1; i++)
                {
                    Attributes.Add(((object[])(propValue))[i].ToString());
                }
    
                return Attributes;
            }
    
    
    
    
        }
    
    
  28. 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.

    Extension Code