Create AD User In C# - c#

I'm trying to create an AD using with C# and have been getting this error every time
System.DirectoryServices.DirectoryServicesCOMException: 'The specified directory service attribute or value does not exist.
I can't seem to figure out why I'm getting this
private void ccNewHire_Button_Click(object sender, EventArgs e)
{
new Thread(() =>
{
String Password = passwordLabel.Text;
String First = newHireFirstName_TextBox.Text;
String Last = newHireLastName_TextBox.Text;
String Cnname = newHireFirstName_TextBox.Text + " " + newHireLastName_TextBox.Text;
String Username = newHireFirstName_TextBox.Text + "." + newHireLastName_TextBox.Text;
String Ldap = PathtoOURedacted;
DirectoryEntry newUser = new DirectoryEntry("LDAP://PathtoOURedacted");
DirectoryEntry childEntry = newUser.Children.Add("CN=" + Cnname, "user");
newUser.Properties["sAMAccountName"].Value = Username;
newUser.Properties["givenName"].Value = First; // first name
newUser.Properties["sn"].Value = Last; // surname = last name
newUser.Properties["displayName"].Value = Cnname;
newUser.Properties["password"].Value = Password;
newUser.Properties["userAccountControl"].Value = 512;
newUser.CommitChanges();
}).Start();
}

This is your problem:
DirectoryEntry newUser = new DirectoryEntry("LDAP://PathtoOURedacted");
DirectoryEntry childEntry = newUser.Children.Add("CN=" + Cnname, "user");
You're calling the variable newUser, but you're setting it to the OU. So you end up changing the attributes on the OU, not on the actual new user object. Just rename those variables:
DirectoryEntry ou = new DirectoryEntry("LDAP://PathtoOURedacted");
DirectoryEntry newUser = ou.Children.Add("CN=" + Cnname, "user");
Also, this won't work:
newUser.Properties["password"].Value = Password;
The password attribute is unicodePwd, but it has to be set in a very specific way, which the documentation describes. In C#, that looks like this:
newUser.Properties["unicodePwd"].Value = Encoding.Unicode.GetBytes($"\"{Password}\"");

Related

How can I retrieve the phone number from Active Directory using User Principal Context

This code works perfectly to get the phone number from Active Directory using the username and password
public string GetPhone(string domain, string username, string pwd)
{
_path = "LDAP://" + domain;
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
string telephoneNumber = string.Empty;
try
{
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
SearchResult result = search.FindOne();
var myEntry = result.GetDirectoryEntry();
telephoneNumber = myEntry.Properties["telephoneNumber"].Value.ToString();
}
catch (Exception ex)
{
throw new Exception("Error obtaining phone number. " + ex.Message);
}
return telephoneNumber;
}
However, I have access to the user password only on the login page. I do have the User context being generated though that is accessible from anywhere within the application (Context.User which is of System.Security.Principal.IPrincipal type)
Thus, how can I get the phone from Active Directory using an already available Context.User object?
Thank you very much in advance
The User object you get will have the SID of the user. With that, you can use the SID binding LDAP path in DirectoryEntry: LDAP://<SID=XXXXX>
var user = new DirectoryEntry(
$"LDAP://<SID={((WindowsIdentity) HttpContext.User.Identity).User.Value}>");
user.RefreshCache(new [] { "telephoneNumber" });
var telephoneNumber = user.Properties["telephoneNumber"]?.Value as string;
The use of RefreshCache is to load only the telephoneNumber attribute. Otherwise, when you first use .Properties, it will retrieve every attribute, which is a waste of time and bandwidth.
Looks like I overcomplicated everything and solution is quite simple
private void SetPhone()
{
DirectoryEntry entryDomain = new DirectoryEntry("LDAP://" + domain);
DirectorySearcher ds = new DirectorySearcher(entryDomain);
string lastName = Context.User.Identity.Name.Split(' ')[Context.User.Identity.Name.Split(' ').Length - 1];
ds.Filter = "(sn=" + lastName + ")";
SearchResult sr = ds.FindOne();
string telephoneNumber = sr.Properties["telephoneNumber"][0].ToString();
telephoneNumber = telephoneNumber.Insert(0, "(").Insert(4, ")").Insert(5, " ").Insert(9, "-");
Session["UserPhone"] = String.Format("{0:(###) ###-####}", telephoneNumber); ;
}

Query Active Directory using DistinguishedName

I have an application that uses Windows authentication and I am trying to get logged in users info using their domain IDs.
Part of the data returned is the user's manager's DN (in manager property). I need to query AD again to get manager's info (domain id, email, name, etc.).
I searched and can't find any hint of what I have to use in my filter.
This is what I am using and I always get null returned:
private static DirectoryEntry GetUserDEByDN(string sDN)
{
using (HostingEnvironment.Impersonate())
{
PrincipalContext pc = new PrincipalContext(ContextType.Domain, adUSADomain, adUSAContainer);
//UserPrincipal up = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, UserID);
UserPrincipal qbeUser = new UserPrincipal(pc);
//qbeUser.SamAccountName = UserID.Trim().ToUpper();
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
PrincipalSearchResult<Principal> psr = srch.FindAll();
string sDomain = ConfigurationManager.AppSettings["Domain"].ToString();
string adPath = ConfigurationManager.AppSettings["ADPath"].ToString();
DirectoryEntry de = new DirectoryEntry(adPath);
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = de;
deSearch.Filter = "(&(objectClass=user)(| (cn = " + sDN + ")(dn = " + sDN + ")))";
//deSearch.Filter = "(&(objectClass=user)(SAMAccountName=" + UserID + "))";
deSearch.SearchScope = SearchScope.Subtree;
SearchResult results = deSearch.FindOne();
if (null != results)
{
de = new DirectoryEntry(results.Path);
return de;
}
else
{
return null;
}
}
}
Is it possible to search Active Directory by DN? If so, what I am doing wrong?
This is what worked for me. However, I believe it is supposed to work with objectClass=user but I kept getting null returned. When I changed to distinguishedName = sDN, it worked.
The whole point of this code
DirectoryEntry de = new DirectoryEntry(adPath + "/" + sDN);
is to start the directory search at the user object; there shouldn’t be the need for the additional search of saying which distinguishedName.
private static DirectoryEntry GetUserDEByDN(string sDN)
{
string adPath = ConfigurationManager.AppSettings["ADPath"].ToString();
DirectoryEntry de = new DirectoryEntry(adPath + "/" + sDN);
DirectoryEntry deManager = null;
using (DirectorySearcher Search = new DirectorySearcher())
{
Search.SearchRoot = de;
Search.Filter = "(&(distinguishedName=" + sDN + "))";
//Search.Filter = "(objectClass = user)";
Search.SearchScope = SearchScope.Base;
SearchResult Result = Search.FindOne();
if (null != Result)
deManager = Result.GetDirectoryEntry();
}
return deManager;
}

Unable to connect to LDAP

I read in this question that a common answer to the 0x error also covered in that aforesaid question is often to specify an account under which to search.
I realised that I'm already doing this - in fact, I'm trying to use active directory to authenticate a user to my application. Originally I thought the issue with my error related to how I was formulating my search parameter:
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
Where _path returned the URI. domainAndUsername returned the following:
"domain.com\\usrname"
So I changed the instantiation to:
DirectoryEntry entry = new DirectoryEntry(_path, username, pwd);
returning "usr#domain.com" (I substituted "#domain.com" because my implementation will require the user to enter their email address and NT password).
That led me to consider the slight possibility that my test might be being performed under an account not privileged enough to perform directory searches - but I've already performed a search for the same data with another implementation - in the same project, no less.
So why does my implementation fall over with 0x80005000 whenever I try to use object obj = entry.NativeObject;?
More importantly, why does this implementation fall over when my original one doesn't? For reference, this implementation is using the same format as demonstrated by Microsoft here, and my original (working) implementation is below:
public class dirSearch
{
public bool searchSuccessful;
public string errStr;
List<string> resList = new List<string>();
public void getEmpDetails(string filStr, string varStr)
{
string strServerDNS = "ldap.domain.com:389";
string strSearchBaseDN = "ou=People,o=domain.com";
string strLDAPPath = "LDAP://" + strServerDNS + "/" + strSearchBaseDN;
DirectoryEntry objDirEntry = new DirectoryEntry(strLDAPPath, null, null, AuthenticationTypes.Anonymous);
DirectorySearcher searcher = new DirectorySearcher(objDirEntry);
SearchResultCollection results;
searcher.Filter = "(uid=" + filStr + ")";
//make sure the order of the search is like so:
//UID
//empnum
//full name
searcher.PropertiesToLoad.Add(varStr);
try
{
results = searcher.FindAll();
foreach (SearchResult result in results)
{
string temStr = result.Properties[varStr][0].ToString();
resList.Add(temStr);
searchSuccessful = true;
}
}
catch (Exception e)
{
errStr = e.ToString();
searchSuccessful = false;
}
}
}
Further information:
I pulled the error code from the exception, which happened to be -2147463168. This corresponds to ADS_BAD_PATHNAME. This is somewhat confusing, as LDAP://ldap.domain.com:389/ is correct. My theory is that I should be using a Distinguished Name, in which case I need to return to my original working method and pull the full name of the staff member, before then formulating the name with that information.
I noticed you're using anonymous authentication to login to your AD, I think you want to use username and password, then see if the user exists. Try the code below:
public bool IsAuthenticated(string username, string pwd)
{
string strLDAPServerAndPort = "ldap.domain.com:389";
string strLDAPContainer = "CN=Users,DC=domain,DC=com";
string strLDAPPath = "LDAP://" + strLDAPServerAndPort + "/" + strLDAPContainer;
DirectoryEntry objDirEntry = new DirectoryEntry(strLDAPPath, username, pwd);
try
{
//Bind to the native AdsObject to force authentication.
object obj = objDirEntry.NativeObject;
DirectorySearcher search = new DirectorySearcher(objDirEntry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
}

C# Add User to Active Directory - The attribute syntax specified to the directory service is invalid

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.
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;
}
}
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.

Permissions required for adding an Active Directory account through code

I'm attempting to write a program that would automatically create active directory accounts using data from an external data source. The problem that I am running into is that I am always getting an UnAuthorizedAccessException but I for the life of me can't think of what permissions to apply. I've even gone all the way to the root object and given my own account full control which doesn't seem to make any difference. I know that I can access the server since the organizationUnit and de objects are populated correctly.
DirectoryEntry de = new DirectoryEntry("LDAP://MYLOCALADDRESS");
de.Password = "thePassword";
de.Username = "theUserName";
de.AuthenticationType = AuthenticationTypes.Secure ;
DirectoryEntry organizationalUnit = de.Parent;
DirectoryEntry newUser = organizationalUnit.Children.Add("TESTADD ", de.SchemaClassName);
//Exception happens on this line
newUser.CommitChanges();
Any help would be appreciated!
At a glance I'd say your "TESTADD " needs to start with "CN="
For active directory I get all my samples from this codeproject:
public string CreateUserAccount(string ldapPath, string userName,
string userPassword)
{
try
{
string oGUID = string.Empty;
string connectionPrefix = "LDAP://" + ldapPath;
DirectoryEntry dirEntry = new DirectoryEntry(connectionPrefix);
DirectoryEntry newUser = dirEntry.Children.Add
("CN=" + userName, "user");
newUser.Properties["samAccountName"].Value = userName;
newUser.CommitChanges();
oGUID = newUser.Guid.ToString();
newUser.Invoke("SetPassword", new object[] { userPassword });
newUser.CommitChanges();
dirEntry.Close();
newUser.Close();
}
catch (System.DirectoryServices.DirectoryServicesCOMException E)
{
//DoSomethingwith --> E.Message.ToString();
}
return oGUID;
}

Categories

Resources