|
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; }
}
}
}
|
|
|
|
|
Brenden,
thanks for sharing your code with us. We faced issues with other way round. LDAP provider was giving a lot of problems where as WinNT provider worked like charm. I think it more to do with how the domains are set up. and security policies.
thanks,
Softomatix
---
Softomatix
http://www.pardesifashions.com/Softomatix/default.aspx
|
|
|
|
|
Brendan Pumpkins, you are the man even if your still using that crusty old WinNT provider, but should start using LDAP instead. LDAP will be around alot longer than WinNT.
;P ;)
|
|
|
|
|
Brendan,
You saved me a lot time by this ADHelper.
Thanks a lot!
|
|
|
|
|
Could you please explain what "ADContainerPath" is and how it should be set up? I always get the error "The server is not operational"
|
|
|
|
|
Dear Sir,
I compiled it and show that StringConstants can't find its namespace..
What should I using in this code?
Thanks a lot...
|
|
|
|
|
Why on earth do people still insist on using the obsolete WinNT provider??? People: start using the LDAP:// provider - it's your future!
WinNT is on the way out - why keep publishing stuff about how to use it? We should start educating every programmer on how to understand and make good use of LDAP.
What if I want to create my user in a given OU? WinNT won't help - it's totally agnostic to the OU structure which is one of the main benefits of AD.
Please - if you publish stuff today, make it use LDAP - it's inevitable everyone will have to use it some day! And for good reason, too.....
|
|
|
|
|
A very valid opinion, assuming everyone has AD implemented. Still plenty of people using NT4, or small orgs who simply haven't yet undertaken the headache of an AD implementation. Planning for the future is great, but you still have to function in the present
"Old technology" <> "obsolete"...check out COBOL.NET from Fujitsu.
|
|
|
|
|
We still have a huge customer base that uses WinNt domains. you will be suprised to know that they are not small organizations. They are in the category of Fortune 100. They don't want to move to AD. In theory it is all good and dandy to say AD is future. But its not real world. Schmig you are right about "function in reality" which I would call the REAL WORLD.
---
Softomatix
http://www.pardesifashions.com/Softomatix/default.aspx
|
|
|
|
|
Are you going to post any downloadable code? Neat article... been looking for a way to script adding users to AD without going down to the server.
Thanks
|
|
|
|
|
We will post a complete sample on our web site. Please check tomorow at
http://www.pardesifashions.com/Softomatix/AddNewUser.aspx
Thanks.
---
Softomatix
http://www.pardesifashions.com/Softomatix/default.aspx
|
|
|
|
|
Ok so I've messed with this some, and I am using LDAP provider instead of WinNT provider... but maybe you had this same problem. The local security policy on our domain server has a password policy on user accounts, very basic, that they have at least 7 characters.
So, if you can't create a password on a DirectoryEntry until committing changes, but then the provider denies the request to add because it violates the security policy, then how the heck to you add a new user?!?!
Baffled...
kgrubbs
P.S> I tried adding a "TemplateUser" to the server, and then used the CopyTo() method to create a copy of it... only to get the System.NotImplementedException. What the heck?!
Then a read the MSDN documentation... "The Lightweight Directory Access Protocol (LDAP) provider does not currently support the CopyTo method." DOH!
|
|
|
|
|
Hey nice class man. Is this also possible with VC6 ?? If yes, how ??
da sonu
|
|
|
|
|
You can either use ADSI interfaces or use Net API
NetUserSetInfo
NetUserDel
---
Softomatix
http://www.pardesiservices.com/Softomatix.asp
|
|
|
|
|
thanks man! Thats what i was looking for.
da sonu
|
|
|
|