5

I am having an issue when attempting to create a new user in active directory. I followed the steps provided in this link for using PrincipalContext (with the exception that I am only doing one user at a time when they are hired and entered into the system and not multiple so no loop is required). I am also using a UserPrincipal Extender.

Here is the code that I have:

protected void CreateUserPE()
{
    try
    {
        PrincipalContext userCtx = new PrincipalContext(ContextType.Domain, DomainFQDN, DomainFull);
        string UserName = txtFirstName.Text.ToLower() + " " + txtLastName.Text.ToLower();
        string password = "superSecretPassword";

        UserPrincipalsEx newUser = new UserPrincipalsEx(userCtx, UserName, password, true);
        newUser.SamAccountName = txtFirstName.Text.ToLower() + "." + txtLastName.Text.ToLower();
        newUser.UserPrincipalName = txtFirstName.Text.ToLower() + "." + txtLastName.Text.ToLower() + "@rasm.com";
        newUser.EmployeeId = txtEmpID.Text;
        newUser.LastName = txtLastName.Text;
        newUser.GivenName = txtFirstName.Text;
        newUser.DisplayName = txtFirstName.Text + " " + txtLastName.Text;
        newUser.Name = txtFirstName.Text + " " + txtLastName.Text;
        newUser.SetPassword(password);
        newUser.HomePostalAddress = txtAddress.Text + ", " + txtCity.Text + ", " + txtState.Text + ", " + txtZip.Text;
        newUser.CountryName = txtCountry.Text;
        newUser.HomePhone = txtHomePhone.Text;
        newUser.MobilePhone = txtMobilePhone.Text;
        newUser.DateOfBirth = txtDOB.Text;
        newUser.EmergencyContact = txtEmergencyCnt.Text;
        newUser.EmergencyPhone = txtContactPhone.Text;
        newUser.Relationship = ddlRelationship1.SelectedItem.ToString();
        newUser.EmergencyContact2 = txtEmergencyCnt2.Text;
        newUser.EmergencyPhone2 = txtContactPhone2.Text;
        newUser.Relationship2 = ddlRelationship2.SelectedItem.ToString();
        newUser.EyeColor = ddlEyeColor.SelectedItem.ToString();
        newUser.HairColor = ddlHairColor.SelectedItem.ToString();
        newUser.Height = txtHeight.Text;
        newUser.Weight = txtWeight.Text;
        newUser.Gender = ddlGender.SelectedItem.ToString();
        newUser.PersonalEmail = txtPersonalEmail.Text;
        newUser.PassportExpires = txtPassportExp.Text;
        newUser.HomeBase = ddlHomeStation.SelectedItem.ToString();
        newUser.WorkLocation = txtWorkLocation.Text;
        newUser.PID = txtPID.Text;
        newUser.Team = txtTeam.Text;
        newUser.Manager = "CN=" + txtSupervisor.Text + "," + DomainFull;
        newUser.Title = ddlJobTitle.SelectedItem.ToString();
        newUser.JobCode = txtJobCode.Text;
        newUser.PLC = txtPLC.Text;
        newUser.BPLC = txtBPLC.Text;
        newUser.Specialty = txtSpecialty.Text;
        newUser.Position = txtPosition.Text;
        newUser.DateOfHire = txtDOH.Text;
        newUser.DateOnContract = txtDOC.Text;
        newUser.TaskOrder = ddlTaskOrder.SelectedItem.ToString();
        newUser.Classification = ddlClass.SelectedIndex.ToString();
        newUser.Section = txtSection.Text;
        newUser.GatePass = txtGatePass.Text;
        newUser.GatePassExpires = txtGatePassExp.Text;
        newUser.WorkPhone = txtWorkPhone.Text;
        newUser.CompanyEmail = txtCompEmail.Text;
        newUser.aKOEmail = txtMilEmail.Text;
        newUser.aKOSponsor = txtMilEmailSp.Text;
        newUser.CACSponsor = txtCacSponsor.Text;
        newUser.CACSponsorEmail = txtCacSponsorEmail.Text;
        newUser.CacCardExpires = txtCacExpires.Text;
        newUser.Enabled = true;
        newUser.ExpirePasswordNow();
        newUser.Save();
        newUser.Dispose();
    }
    catch
    {

    }
}

The program goes all the way to newUser.Save() and then throws the following error in the catch statement:

System.DirectoryServices.AccountManagement.PrincipalOperationException was caught
  HResult=-2146233087
  Message=The attribute syntax specified to the directory service is invalid.

  Source=System.DirectoryServices.AccountManagement
  ErrorCode=-2147016693
  StackTrace:
   at System.DirectoryServices.AccountManagement.ADStoreCtx.Insert(Principal p)
   at System.DirectoryServices.AccountManagement.Principal.Save()
   at Personnel_Employee.CreateUserPE() in c:\inetpub\wwwroot\TestingFolder\Personnel\Add\Employee.aspx.cs:line 263
   InnerException: System.DirectoryServices.DirectoryServicesCOMException
   HResult=-2147016693
   Message=The attribute syntax specified to the directory service is invalid.

   Source=System.DirectoryServices
   ErrorCode=-2147016693
   ExtendedError=87
   ExtendedErrorMessage=00000057: LdapErr: DSID-0C090D11, comment: Error in attribute conversion operation, data 0, v23f0
   StackTrace:
        at System.DirectoryServices.DirectoryEntry.CommitChanges()
        at System.DirectoryServices.AccountManagement.SDSUtils.ApplyChangesToDirectory(Principal p, StoreCtx storeCtx, GroupMembershipUpdater updateGroupMembership, NetCred credentials, AuthenticationTypes authTypes)
   InnerException: 

Where am I going wrong.

Red_Phoenix
  • 482
  • 6
  • 22
  • The exception is thrown on newUser.Save() ? It's a bit boring, but can you provide the lines you replace by '... More Similar Code ...'. I suspect that the trouble comes from an atribute syntax. – JPBlanc Jun 13 '14 at 13:39
  • I performed the edit as you requested. Most of the attributes are custom added to Active Directory through MMC.EXE. I set all my custom ones to "Unicode String" for syntax. I also created a class called UserPrincipalsEx.cs to extend all the attributes that are not available by default." – Red_Phoenix Jun 13 '14 at 13:51
  • I think I understand. UserPrincipalsEx is declared : `class UserPrincipalsEx : UserPrincipal` ? – JPBlanc Jun 13 '14 at 14:11
  • public class UserPrincipalsEx : UserPrincipal { public UserPrincipalsEx(PrincipalContext context) : base(context) { } public UserPrincipalsEx(PrincipalContext context, string samAccountName, string password, bool enabled) : base(context, samAccountName, password, enabled) { } – Red_Phoenix Jun 13 '14 at 14:12
  • Are you aware of the fact that you have to map, your class properties with the the Active-Directory attributes, using attributes ? – JPBlanc Jun 13 '14 at 14:16
  • [DirectoryProperty("aKOEmail")] public string aKOEmail { get { if (ExtensionGet("aKOEmail").Length != 1) return null; return (string)ExtensionGet("aKOEmail")[0]; } set { this.ExtensionSet("aKOEmail", value); } } – Red_Phoenix Jun 13 '14 at 14:17
  • Is this what you mean by mapping? – Red_Phoenix Jun 13 '14 at 14:18
  • You have not enough reputation to chat – JPBlanc Jun 13 '14 at 14:21
  • [DirectoryProperty("aKOEmail")]public string aKOEmai ... means that you map you property aKOEmai to aKOEmai attribute in your Active-Directory. "aKOEmai" is not a standard attribute do you add it in your Active-Directory ? – JPBlanc Jun 13 '14 at 14:23
  • I created a new attribute in MMC.EXE after adding the Schema snap-in, created a new OID with the script provided by Microsoft, and added the attribute to the User class. – Red_Phoenix Jun 13 '14 at 14:25
  • Nice and you do so for each new attributes ? When you create the attribute in the schema do take care of the syntax you use ? – JPBlanc Jun 13 '14 at 14:28
  • Yes, all custom attributes are created (took a while) and they are all "Unicode string" – Red_Phoenix Jun 13 '14 at 14:31
  • Ok, after that you assign all the new attributes to an abstract class, and add the abstract class to user class ? do you take time to reload the schema ? – JPBlanc Jun 13 '14 at 14:33
  • I am not sure that I have done that. By now I have posted all the code I have that pertains to inserting into Active Directory. Could this be what I am missing? – Red_Phoenix Jun 13 '14 at 14:36
  • Il always modify Directories (Active-Directory, OpenLDAP ...) using LDIF script (never using UI to deploy) And I always use Abstract classes to group my own attributs. Sorry to tell you that, but you met a trouble with one attribute. If I were you I would use a dichotomizing approch, testing again with an extention class using half attributes, and so on till I found the attribute, with the bad spelling or syntax. – JPBlanc Jun 13 '14 at 14:44
  • So I am reading this as putting the "newUser.Save()" after each attribute to see where it fails??? – Red_Phoenix Jun 13 '14 at 14:53
  • So ? Do you find the problem ? – JPBlanc Jun 13 '14 at 15:06
  • I added save() after my first entry (SamAccountName) and got an error of "The specified directory service attribute or value does not exist." I commented it out, ran again, and go this on (UserPrincipalName) "SamAccountName or Name must be assigned to a newly-created Principal object in this store prior to saving." Does this mean that you have to add the properties in a certain order??? – Red_Phoenix Jun 13 '14 at 15:14
  • There is no order, just some attributes are mandatory. – JPBlanc Jun 13 '14 at 16:19
  • [Here](http://stackoverflow.com/a/5857811/608772) is an example of extending UserPrincipal. – JPBlanc Jun 13 '14 at 16:23

2 Answers2

8

You can not update an attribute with null or empty. I personaly dislike solutions with dummy values. If you are using the context principle just simply check for null or empty and dont update if its the case like:

if (!string.IsNullOrEmpty(txtbox.Text)){ newUser.attributeName = txtbox.Text}

If you are using an directory entry instead of an usercontext you can do something like this:

string adPath = "LDAP://server.domain.com/CN=John,CN=Users,dc=domain,dc=com";
DirectoryEntry userEntry = new DirectoryEntry(adPath);
if (txtBox.text == "")
{
    userEntry.Properties["proppertyName"].Clear();
}
else if (!string.IsNullOrEmpty(txtBox.text))
{
    userEntry.Properties[attribute.Key].Value = txtBox.text;
}
// dont do a thing when txtBox.Text is empty

It looks like more code but its much easier to make a foreachloop for it if you have a list with all attribute like:

private void UpdateEntryAttributes(DirectoryEntry entry, Dictionary<string, string> attributes)
{
    foreach (KeyValuePair<string, string> attribute in attributes)
    {
    entry.Properties[attribute.Key].Value = attribute.Value;

    if (attribute.Value == "")
    {
        entry.Properties[attribute.Key].Clear();
    }
    else if (!string.IsNullOrEmpty(attribute.Value))
    {
        entry.Properties[attribute.Key].Value = attribute.Value;
    }
}
Pieter
  • 458
  • 5
  • 16
3

This can happen when attempting to write either a null or empty string to an AD field that prohibits it. An easy way to check whether this is the problem is to temporarily replace all such values with a dummy string (length > 0) and then run the code again. If that works, you can attempt a fix by changing the offending value--with AD, sometimes if null doesn't work, then an empty string will work, or vice versa.

Tawab Wakil
  • 1,737
  • 18
  • 33
  • Arrrrgh, the attribute was null or empty string. Thank you for posting this! It's so frustrating trying to figure out the cryptic AD error messages. Upvoted! – Lews Therin May 03 '19 at 20:02
  • It doesn't mention what attribute, very helpful messages indeed... – live-love Jan 10 '20 at 18:10