Click here to Skip to main content
15,922,155 members
Articles / Programming Languages / C#
Article

How to add a new user using DirectoryServices?

Rate me:
Please Sign up or sign in to vote.
3.93/5 (12 votes)
19 Feb 20032 min read 281.1K   43   40
An article describing the use of DirectoryServices namespace classes in .NET on how to add a new user in a machine or a domain.

Introduction

In our previous article, How to get full name of logged in user, we showed how you can get full name of a user in a given domain or machine. You can extend that idea to obtain any information you want for a given user. This artcile will describe how you can add a new user account into your domain or machine using .Net DirectoryServices classes. We will be using WinNT provider for illustrations in this article. But you can extend the examples to use LDAP or AD providers.

Details

Here are the key steps that you will need to perform to create new account.

  • Create a new DirectoryEntry object and specify the machine name as the path.
  • User accounts are created as nodes corrresponding to User schema class in Active Directory. Therefore we will add a new DirectoryEntry object in Children collection of the machine. The key thing to rememeber will be that when you add new entry, make sure that the schema class name is User.
  • When you add a new node in Children collection, it will return you the newly created object. At this stage the information has not been added to your machine or active directory tree.
  • Now you can set all the values that you need to set for a given account. Following is the list of property names that you can set for the account.
    • UserFlags
    • MaxStorage
    • PasswordAge
    • PasswordExpired
    • LoginHours
    • FullName
    • Description
    • BadPasswordAttempts
    • LastLogin
    • HomeDirectory
    • LoginScript
    • Profile
    • HomeDirDrive
    • Parameters
    • PrimaryGroupID
    • Name
    • MinPasswordLength
    • MaxPasswordAge
    • MinPasswordAge
    • PasswordHistoryLength
    • AutoUnlockInterval
    • LockoutObservationInterval
    • MaxBadPasswordsAllowed
    • RasPermissions
    • objectSid

    For more information on these properties please read Active Directory Services Interface(ADSI) section in Microsoft Platform SDK.

  • You must have noticed from the above list that there is no property to set or get user password value. Operating system does not give access to clear text password value. So we can't expect and property or method to get it. In ADSI, IAdsUser interface provides SetPassword method to set a user's password. This is where Invoke method of DirectoryEntry class comes handy. So we call Invoke to set the password value. The Invoke method can be used to call native methods on underlying active directory objects. There is one important thing to remeber when you set a user's password value. If you are using LDAP provider, then the user account should already have been created in the system by calling CommitChanges or SetInfo method. But WinNT provider does not have this restriction. You can set password value without commiting the changes first.
  • The last step would be to actually create the account in the machine or domain. This is done by calling CommitChanges method on newly added DirectoryEntry object.

The following code demonstrates all the steps that we described above.

C#
private void AddUser(string strDoamin, string strLogin, string strPwd)
{
    DirectoryEntry obDirEntry = null;
    try
    {
        obDirEntry = new DirectoryEntry("WinNT://" + strDoamin);
        DirectoryEntries entries = obDirEntry.Children;
        DirectoryEntry obUser = entries.Add(strLogin, "User");
        obUser.Properties["FullName"].Add("Amigo");
        object obRet = obUser.Invoke("SetPassword", strPwd);
        obUser.CommitChanges();
    }
    catch (Exception ex)
    {
        Trace.Warn(ex.Message);
    }
}

Please feel free to send your suggestions directly to us at softomatix@pardesiservices.com.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
To learn more about us, Please visit us at http://www.netomatix.com

Comments and Discussions

 
GeneralThis code won't always work! Try this... Pin
Brendan Tompkins26-Feb-03 3:48
Brendan Tompkins26-Feb-03 3:48 
I'm sure this article's code works for some, but creating a user this way gives me all kinds of errors, like "The server is unwilling to process the request." and "The server said it'll think about it and get back to you"... I just spent a couple of days dealing with this, and finally came up with the following C# helper class, based on other pieces of code I gleaned from the newsgroups. Anyhow, hope this helps, it works for me.

-Brendan Tompkins
brendan_f_tompkins@hotmail.com

using System;
using System.DirectoryServices;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Configuration;

namespace Util.ActiveDirectory
{
public class ADHelper
{
#region Private Variables

/// Path to your AD -LDAP://machine.domain.org/DC=machine,DC=domain,DC=org
private static string ADPath = ConfigurationSettings.AppSettings[StringConstants.Configuration.AD_PATH_KEY] ;

/// Path to your AD Container where you want to create users - LDAP://machine.domain.org/OU=container,DC=machine,DC=domain,DC=org
private static string ADContainerPath = ConfigurationSettings.AppSettings[StringConstants.Configuration.AD_CONAINER_PATH_KEY] ;

/// Path to your AD Server -machine.domain.org
private static string ADServer = ConfigurationSettings.AppSettings[StringConstants.Configuration.AD_SERVER_KEY] ;

#endregion

#region Enumerations
public enum ADAccountOptions
{
UF_TEMP_DUPLICATE_ACCOUNT = 0x0100,
UF_NORMAL_ACCOUNT =0x0200,
UF_INTERDOMAIN_TRUST_ACCOUNT =0x0800,
UF_WORKSTATION_TRUST_ACCOUNT = 0x1000,
UF_SERVER_TRUST_ACCOUNT =0x2000,
UF_DONT_EXPIRE_PASSWD=0x10000,
UF_SCRIPT =0x0001,
UF_ACCOUNTDISABLE=0x0002,
UF_HOMEDIR_REQUIRED =0x0008,
UF_LOCKOUT=0x0010,
UF_PASSWD_NOTREQD=0x0020,
UF_PASSWD_CANT_CHANGE=0x0040,
UF_ACCOUNT_LOCKOUT=0x0010,
UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED=0x0080,
}

public enum ADErrors : uint
{
ERR_PROPERTY_NOT_FOUND = 0x80020006
}

public enum LoginResult
{
LOGIN_OK=0,
LOGIN_USER_DOESNT_EXIST,
LOGIN_USER_ACCOUNT_INACTIVE
}
#endregion

/// Given a username, return bool if exists
public static bool UserExists(string UserName)
{
//create an instance of the DirectoryEntry
DirectoryEntry de = GetDirectoryObject(ADContainerPath);

//create instance fo the direcory searcher
DirectorySearcher deSearch = new DirectorySearcher();

//set the search filter
deSearch.SearchRoot = de;
deSearch.Filter = "(SAMAccountName=" + UserName +")";

//find the first instance
SearchResultCollection results= deSearch.FindAll();

//if the username and password do match, then this implies a valid login
//if so then return the DirectoryEntry object
if(results.Count == 0) return false;
return true;
}

/// Creates a new user
public static DirectoryEntry CreateNewUser(ActiveDirectoryUser user)
{

DirectoryEntry container = GetDirectoryObject(ADContainerPath);

DirectoryEntry userEntry = AddUserAccount(container, user);
SetPassword(userEntry, user.password);
EnableUserAccount(userEntry);

return userEntry;
}

/// Add Account to the given container
public static DirectoryEntry AddUserAccount(DirectoryEntry container, ActiveDirectoryUser user)
{

DirectoryEntry newUser = container.Children.Add("CN=" + user.userName, "User");

// Add mandatory properties
newUser.Properties["sAMAccountName"].Add(user.userName);

//Optional properties
newUser.Properties["mail"].Add(user.mail);
newUser.Properties["givenName"].Add(user.givenName);
newUser.Properties["sn"].Add(user.sn);

// // add some more obscure properties
// object[] objAddress = new object[] {"Somestreet","PB", "SomeCity", "PROV","PB0000", "Country"};
//
// //Set the Address and Province
// newUser.Properties["postalAddress"].AddRange(objAddress);

newUser.CommitChanges();

return newUser;
}

/// Enable the user account after it has been created and after it's password has been set.
private static void EnableUserAccount(DirectoryEntry newUser)
{
int currentAccountControl = (int)newUser.Properties["userAccountControl"].Value;

int acctControlFlags = currentAccountControl - (int)ADAccountOptions.UF_PASSWD_NOTREQD - (int)ADAccountOptions.UF_ACCOUNTDISABLE;
acctControlFlags = acctControlFlags | (int)ADAccountOptions.UF_NORMAL_ACCOUNT | (int)ADAccountOptions.UF_PASSWD_CANT_CHANGE | (int)ADAccountOptions.UF_DONT_EXPIRE_PASSWD;

newUser.Properties["userAccountControl"].Clear();
newUser.Properties["userAccountControl"].Add(acctControlFlags);

newUser.CommitChanges();
}

/// Set the password of a user entry
private static void SetPassword(DirectoryEntry userEntry, string password)
{
object[] oPassword = new object[] {password};
object ret = userEntry.Invoke("SetPassword", oPassword );
userEntry.CommitChanges();
}


/// Gets a directory object given an LDAP path
private static DirectoryEntry GetDirectoryObject(string path)
{
DirectoryEntry de;

string _ADPath = path;

de = new DirectoryEntry(_ADPath);
return de;
}


/// Gets a directory object, given a path, username, ans password.
private static DirectoryEntry GetDirectoryObject(string path, string UserName, string Password)
{
DirectoryEntry de;

de = new DirectoryEntry(path,UserName,Password,AuthenticationTypes.Secure);

return de;
}


/// Gets a property givent a directory entry and property name
public static string GetProperty(DirectoryEntry de, string PropertyName)
{
if(de.Properties.Contains(PropertyName))
{
return de.Properties[PropertyName][0].ToString() ;
}
else
{
return string.Empty;
}
}


/// Gets a formatted LDAP domain string given a servername
private static string GetLDAPDomain(string servername)
{
StringBuilder LDAPDomain = new StringBuilder();
string[] LDAPDC = servername.Split('.');

for(int i=0;i < LDAPDC.GetUpperBound(0)+1;i++)
{
LDAPDomain.Append("DC="+LDAPDC[i]);
if(i <LDAPDC.GetUpperBound(0))
{
LDAPDomain.Append(",");
}
}

return LDAPDomain.ToString();
}

/// Write out all of the properties of a given directory entry
public static void WriteProperties(DirectoryEntry dirEntry)
{

string[] propNames = new string[dirEntry.Properties.Count];
dirEntry.Properties.PropertyNames.CopyTo(propNames,0);
for (int propertyIndex = 0; propertyIndex < dirEntry.Properties.Count; propertyIndex++)
{
System.Diagnostics.Trace.WriteLine("Name: {0}", propNames[propertyIndex]);
int valueCount = dirEntry.Properties[propNames[propertyIndex]].Count;
for (int valueIndex = 0; valueIndex < valueCount; valueIndex++)
System.Diagnostics.Trace.WriteLine(String.Format(" Value {0}: {1}", valueIndex, dirEntry.Properties[propNames[propertyIndex]][valueIndex]));
}
}
}

[Serializable()]
public struct ActiveDirectoryUser
{

private string _userName;
private string _password;
private string _description;
private string _displayName;
private string _mail;
private string _givenName;
private string _sn;

public ActiveDirectoryUser(string userName, string password)
{
_userName = userName;
_password = password;
_description = "";
_displayName = "";
_mail = "";
_givenName = "";
_sn = "";

}

public string userName
{
get { return _userName; }
set { _userName = value; }
}

public string password
{
get { return _password; }
set { _password = value; }
}

public string description
{
get { return _description; }
set { _description = value; }
}

public string displayName
{
get { return _displayName; }
set { _displayName = value; }
}

public string mail
{
get { return _mail; }
set { _mail = value; }
}

public string givenName
{
get { return _givenName; }
set { _givenName = value; }
}

public string sn
{
get { return _sn; }
set { _sn = value; }
}
}
}
GeneralRe: This code won't always work! Try this... Pin
Softomatix26-Feb-03 3:57
Softomatix26-Feb-03 3:57 
GeneralRe: This code won't always work! Try this... Pin
Mark DiGiovanni28-Feb-03 5:31
Mark DiGiovanni28-Feb-03 5:31 
GeneralRe: This code won't always work! Try this... Pin
Rishi Tikai6-Dec-04 22:11
Rishi Tikai6-Dec-04 22:11 
GeneralRe: This code won't always work! Try this... Pin
dragomir14-Sep-05 7:18
dragomir14-Sep-05 7:18 
GeneralRe: This code won't always work! Try this... Pin
netyale27-Nov-05 16:41
netyale27-Nov-05 16:41 
QuestionWhen are people going to stop using obsolete stuff?? Pin
Marc Scheuner25-Feb-03 21:17
professionalMarc Scheuner25-Feb-03 21:17 
AnswerRe: When are people going to stop using obsolete stuff?? Pin
schmig26-Feb-03 2:28
schmig26-Feb-03 2:28 
GeneralRe: When are people going to stop using obsolete stuff?? Pin
Softomatix26-Feb-03 3:54
Softomatix26-Feb-03 3:54 
QuestionCode? Pin
kgrubbs21-Feb-03 4:47
kgrubbs21-Feb-03 4:47 
AnswerRe: Code? Pin
Softomatix21-Feb-03 5:18
Softomatix21-Feb-03 5:18 
GeneralCreate new user with password policies... Pin
kgrubbs21-Feb-03 8:11
kgrubbs21-Feb-03 8:11 
Generalnice Pin
Sonu Kapoor20-Feb-03 23:23
Sonu Kapoor20-Feb-03 23:23 
GeneralRe: nice Pin
Softomatix21-Feb-03 0:52
Softomatix21-Feb-03 0:52 
GeneralRe: nice Pin
Sonu Kapoor21-Feb-03 1:04
Sonu Kapoor21-Feb-03 1:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.