Blue Theme Orange Theme Green Theme Red Theme
 
Home | Forums | Videos | Photos | Downloads | Blogs | E-Books | Interviews | Jobs | Beginners | Training
 | Consulting  
Submit an Article Submit a Blog 
 Login Close
User Id:
Password:
 
Forgot Password
Forgot Username
Why Register
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
 Resources  
Close
 Our Network  
Close
Search :       Advanced Search »
Home » Active Directory » Active Directory and .NET

Active Directory and .NET

Microsoft Technet offers a script repository to work with AD User Accounts; however, I needed to work with .NET and I could not find samples for all the tasks I needed to program. I promised to myself that one day I would publish the code samples I found and created to help other developers who are working with Directory Services.

Technologies: Active Directory,Visual C# .NET
Total downloads : 2547
Total page views :  111618
Rating :
 4.75/5
This article has been rated :  12 times
   Print Read/Post comments Post a comment  Rate  
   Email to a friend  Bookmark  Similar Articles  Author's other articles  
Download Files:
SetPassword.zip | MailBoxScripts.zip
 
Become a Sponsor



Some time ago I was working in a project where I had to design and develop a service to synchronize a human resources database with an Active Directory (AD). To accomplish my goal, I created a service that used .NET Directory Services, and after some months, the project succeeded. I had to invest a ton of extra hours to the project because I had a hard time finding sample .NET code and documentation related to specific tasks needed to work with AD User Accounts.

Microsoft Technet offers a script repository to work with AD User Accounts; however, I needed to work with .NET and I could not find samples for all the tasks I needed to program. I promised to myself that one day I would publish the code samples I found and created to help other developers who are working with Directory Services. So, I wish you a happy AD.NET programming and I hope my work saves you some time. The code samples I provide are written in C#.

1. Create a connection to Active Directory

/// <summary>
///
Method used to create an entry to the AD.
///
Replace the path, username, and password.
///
</summary>
/// <returns>DirectoryEntry
</returns>
public static
DirectoryEntry GetDirectoryEntry()
{
DirectoryEntry de =
new
DirectoryEntry();
de.Path = LDAP://192.168.1.1/CN=Users;DC=Yourdomain;
de.Username = @"yourdomain\sampleuser";
de.Password = "samplepassword";
return
de;
}

2. Create a secure connection to Active Directory

To connect to the AD, you need a user account that belongs to the domain you want to connect to. Most user accounts have permissions to search the AD; however, to modify the AD, you need a user account that is a member of the group of Domain Administrators (DomainAdmin). An account that belongs to this group has high privileges and hardcoding the user and password of this account in your code can compromise the security of the AD. I don't recommend you to create directory entries where usernames and passwords are hardcoded. Try to connect to the AD using a secure connection.

/// <summary>
///
Method used to create an entry to the AD using a secure connection.
///
Replace the path.
///
</summary>
/// <returns>DirectoryEntry
</returns>
public static
DirectoryEntry GetDirectoryEntry()
{
DirectoryEntry de =
new
DirectoryEntry();
de.Path = LDAP://192.168.1.1/CN=Users;DC=Yourdomain;
de.AuthenticationType = AuthenticationTypes.Secure;
return
de;
}

To connect to the AD using a secure connection, you need to delegate the permissions of a user account with DomainAdmin permissions to the thread that is running a program. For instance, I created an exe and I ran the program using the Run As command to start a program. I delegated the user's principal identity and culture to the current thread that runs the program. To delegate the principal identity and culture to the current thread, I used the following code:

/// <summary>
///
Establish identity (principal) and culture for a thread.
///
</summary>
public static void
SetCultureAndIdentity()
{
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
WindowsPrincipal principal = (WindowsPrincipal)Thread.CurrentPrincipal;
WindowsIdentity identity = (WindowsIdentity)principal.Identity;
System.Threading.Thread.CurrentThread.CurrentCulture =
new
CultureInfo("en-US");
}

3. Validate if a user exists

/// <summary>
///
Method to validate if a user exists in the AD.
///
</summary>
///
<param name="UserName"></param>
///
<returns></returns>
public bool UserExists(string
UserName)
{
DirectoryEntry de = ADHelper.GetDirectoryEntry();
DirectorySearcher deSearch =
new
DirectorySearcher();
deSearch.SearchRoot =de;
deSearch.Filter = "(&(objectClass=user) (cn=" + UserName +"))";
SearchResultCollection results = deSearch.FindAll();
if
(results.Count == 0)
{
return false
;
}
else
{
return true
;
}
}

4. Set user's properties

/// <summary>
///
Helper method that sets properties for AD users.
///
</summary>
///
<param name="de"></param>
///
<param name="PropertyName"></param>
///
<param name="PropertyValue"></param>
public static void SetProperty(DirectoryEntry de, string PropertyName, string
PropertyValue)
{
if(PropertyValue!=null
)
{
if
(de.Properties.Contains(PropertyName))
{
de.Properties[PropertyName][0]=PropertyValue;
}
else
{
de.Properties[PropertyName].Add(PropertyValue);
}
}

5. Set user's country

To set the country property for a user was one of the tasks that took me some time to figure out. After some hours of research I realized that you need to know the ISO 3166 Codes for countries and set three properties to define a user's country: c, co, and countryCode.

// Set the co property using the name of the country.
SetProperty(newuser,"co","MEXICO");
// Set the c property using the two-letter country code (ISO 3166 A 2).
SetProperty(newuser,"c","MX");
// Set the countryCode property using the numeric value (ISO 3166 Number) of the country.
SetProperty(newuser,"countryCode","484");
}

6. Set user's password

Setting the password for a user requires some work. I will walk you through the steps I followed to set a password for a user:

a) Create or download a helper class that generates random passwords that comply with the strong password rules. I was short of time and couldn't develop one, so I downloaded the RandomPassword class created by Obviex.
b) Create a method that consumes the RandomPassword helper class

/// <summary>
///
Method that consumes a helper class library
///
to generate random passwords.
///
</summary>
///
<returns></returns>
public string
SetSecurePassword()
{
RandomPassword rp =
new
RandomPassword();
return
rp.Generate(8,8);
}

c) Set the password property using the usr.Invoke method.

/// <summary>
///
Method to set a user's password
///
<param name="path"></param>
public void SetPassword(string
path)
{
DirectoryEntry usr =
new
DirectoryEntry();
usr.Path = path;
usr.AuthenticationType = AuthenticationTypes.Secure;
object[] password = new object
[] {SetSecurePassword()};
object
ret = usr.Invoke("SetPassword", password );
usr.CommitChanges();
usr.Close();
}

The usr.Invoke method can be called once within the same AppDomain, otherwise your program will crash. If you place a call to the usr.Invoke method inside a for construct, the first run will be succesful, but the second one will crash the compiler. I created a workaround that helped me to solve this problem. I made a separate console application (SetPassword.exe) and I called and started the process programatically from the SetPassword method.

  • Download the SetPassword project.
  • Copy the SetPassword.exe file in your application.
  • Call and start SetPassword.exe from your application.

/// </summary>
///
Method that calls and starts SetPassword.exe
///
<param name="path"></param>
///
<param name="password"></param>
public void SetPassword(string path, string
password)
{
StringBuilder args =
new
StringBuilder();
args.Append(path);
args.Append(" ");
args.Append(password);
ProcessStartInfo startInfo =
new
ProcessStartInfo("SetPassword.exe",args.ToString());
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(startInfo);
}

7. Enable a user account

/// <summary>
///
Method to enable a user account in the AD.
///
</summary>
///
<param name="de"></param>
private static void
EnableAccount(DirectoryEntry de)
{
//UF_DONT_EXPIRE_PASSWD 0x10000
int exp = (int
) de.Properties["userAccountControl"].Value;
de.Properties["userAccountControl"].Value = exp | 0x0001;
de.CommitChanges();
//UF_ACCOUNTDISABLE 0x0002
int val = (int
) de.Properties["userAccountControl"].Value;
de.Properties["userAccountControl"].Value = val & ~0x0002;
de.CommitChanges();
}

8. Add a user to a group

/// <summary>
///
Method to add a user to a group
///
</summary>
///
<param name="de"></param>
///
<param name="deUser"></param>
///
<param name="GroupName"></param>
public static void AddUserToGroup(DirectoryEntry de, DirectoryEntry deUser, string
GroupName)
{
DirectorySearcher deSearch =
new
DirectorySearcher();
deSearch.SearchRoot = de;
deSearch.Filter = "(&(objectClass=group) (cn=" + GroupName +"))";
SearchResultCollection results = deSearch.FindAll();
bool isGroupMember = false
;
if
(results.Count>0)
{
DirectoryEntry group = GetDirectoryEntry(results[0].Path);
object members = group.Invoke("Members",null
);
foreach ( object member in
(IEnumerable) members)
{
DirectoryEntry x =
new
DirectoryEntry(member);
if
(x.Name! = deUser.Name)
{
isGroupMember =
false
;
}
else
{
isGroupMember =
true
;
break
;
}
}
if
(!isGroupMember)
{
group.Invoke("Add",
new object
[] {deUser.Path.ToString()});
}
group.Close();
}
return
;
}

9. Generate a mailbox for a user in Microsoft Exchange Server

You might need to create a mailbox for a user in Microsoft Exchange Server. Network configuration and server architecture can add complexity to the process of programmatically creating mailboxes for users, but you know, there's always a workaround. You can invoke a script that creates mailboxes from a remote machine. I will walk you through the steps I followed to create a mailbox for a user in Microsoft Exchange Server.

  • On the Domain Controller server, create the directory C:\TestRemoteMailbox.
  • Download the scripts MailBox.vbs and WSHControl.vbs.
  • Copy the script Mailbox.vbs to the directory C:\TestRemoteMailbox.
  • Note: Mailbox.vbs is a script that creates MailBoxes in Microsoft Exchange.
  • Copy the script WSHControl.vbs to your application directory.
  • Note: WSHControl.vbs is a script that invokes the MailBox.vbs script on a remote machine (Domain Controller)
  • From your application, call and start WSHControl.vbs.

/// <summary>
///
Method that calls and starts a WSHControl.vbs
///
</summary>
///
<param name="userAlias"></param>
public void GenerateMailBox(string
userAlias)
{
StringBuilder mailargs =
new
StringBuilder();
mailargs.Append("WSHControl.vbs");
mailargs.Append(" ");
mailargs.Append(userAlias);
ProcessStartInfo sInfo =
new
ProcessStartInfo("Wscript.exe",mailargs.ToString());
sInfo.WindowStyle = ProcessWindowStyle.Hidden;;
Process.Start(sInfo);
}

10. Create a user account

/// <summary>
///
Method that creates a new user account
///
</summary>
///
<param name="employeeID"></param>
///
<param name="name"></param>
///
<param name="login"></param>
///
<param name="email"></param>
///
<param name="group"></param>
public void CreateNewUser(string employeeID, string name, string login, string email, string
group)
{
Catalog catalog =
new
Catalog();
DirectoryEntry de = ADHelper.GetDirectoryEntry();
///
1. Create user account
DirectoryEntries users = de.Children;
DirectoryEntry newuser = users.Add("CN=" + login, "user");
///
2. Set properties
SetProperty(newuser,"employeeID", employeeID);
SetProperty(newuser,"givenname", name);
SetProperty(newuser,"SAMAccountName", login);
SetProperty(newuser,"userPrincipalName", login);
SetProperty(newuser,"mail", email);
newuser.CommitChanges();
///
3. Set password
SetPassword(newuser.Path);
newuser.CommitChanges();
///
4. Enable account
EnableAccount(newuser);
///
5. Add user account to groups
AddUserToGroup(de,newuser,group);
///
6. Create a mailbox in Microsoft Exchange
GenerateMailBox(login);
newuser.Close();
de.Close();
}

11. Disable a user account

/// <summary>
///
Method that disables a user account in the AD and hides user's email from Exchange address lists.
///
</summary>
///
<param name="EmployeeID"></param>
public void DisableAccount(string
EmployeeID)
{
DirectoryEntry de = GetDirectoryEntry();
DirectorySearcher ds =
new
DirectorySearcher(de);
ds.Filter = "(&(objectCategory=Person)(objectClass=user)(employeeID=" + EmployeeID + "))";
ds.SearchScope = SearchScope.Subtree;
SearchResult results = ds.FindOne();
if(results != null
)
{
DirectoryEntry dey = GetDirectoryEntry(results.Path);
int val = (int
)dey.Properties["userAccountControl"].Value;
dey.Properties["userAccountControl"].Value = val | 0x0002;
dey.Properties["msExchHideFromAddressLists"].Value = "TRUE";
dey.CommitChanges();
dey.Close();
}
de.Close();
}

12. Update user account

/// <summary>
///
Method that updates user's properties
///
</summary>
///
<param name="employeeID"></param>
///
<param name="department"></param>
///
<param name="title"></param>
///
<param name="company"></param>
public void ModifyUser(string employeeID, string department, string title, string
company)
{
DirectoryEntry de = GetDirectoryEntry();
DirectorySearcher ds =
new
DirectorySearcher(de);
ds.Filter = "(&(objectCategory=Person)(objectClass=user)(employeeID=" + employeeID + "))";
ds.SearchScope = SearchScope.Subtree;
SearchResult results = ds.FindOne();
if(results!=null
)
{
DirectoryEntry dey = GetDirectoryEntry(results.Path);
SetProperty(dey, "department", department);
SetProperty(dey, "title", title);
SetProperty(dey, "company", company);
dey.CommitChanges();
dey.Close();
}
de.Close();
}

13. Validate if a string has a correct email pattern.

/// <summary>
///
Method that validates if a string has an email pattern.
///
</summary>
///
<param name="mail"></param>
///
<returns></returns>
public bool IsEmail(string
mail)
{
Regex mailPattern =
new
Regex(@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*");
return
mailPattern.IsMatch(mail);
}

14. Extract a user alias from an email account.

/// <summary>
///
Method to extract the alias from an email account.
///
dada una cuenta de correo electrónico
///
</summary>
///
<param name="mailAddress"></param>
///
<returns></returns>
public string GetAlias(string
mailAddress)
{
if
(IsEmail(mailAddress))
{
return
mailAddress.Substring(0,mailAddress.IndexOf("@"));
}
else
{
return
"";
}
}

15. Format dates to AD date format (AAAAMMDDMMSSSS.0Z)

/// <summary>
///
Method that formats a date in the required format
///
needed (AAAAMMDDMMSSSS.0Z) to compare dates in AD.
///
</summary>
///
<param name="date"></param>
/// <returns>Date in valid format for AD
</returns>
public string
ToADDateString(DateTime date)
{
string
year = date.Year.ToString();
int
month = date.Month;
int
day = date.Day;
StringBuilder sb =
new
StringBuilder();
sb.Append(year);
if
(month <10)
{
sb.Append("0");
}
sb.Append(month.ToString());
if
(day <10)
{
sb.Append("0");
}
sb.Append(day.ToString());
sb.Append("000000.0Z");
return
sb.ToString();
}

16. Search users

When you use Directory Services, you can accomplish many interesting tasks such as searching and filtering users. The DirectorySearcher object allows you to query the AD. The following sample code queries the AD to search all the user accounts that were modified from a given date. The results are stored in a DataTable, so you can easily databind them.

/// <summary>
///
Method that returns a DataTable with a list of users modified from a given date.
///
</summary>
///
<param name="fromdate"></param>
public
DataTable GetModifiedUsers(DateTime fromdate)
{
DataTable dt =
new
DataTable();
dt.Columns.Add("EmployeeID");
dt.Columns.Add("Name");
dt.Columns.Add("Email");
DirectoryEntry de = GetDirectoryEntry();
DirectorySearcher ds =
new
DirectorySearcher(de);
StringBuilder filter =
new
StringBuilder();
filter.Append("(&(objectCategory=Person)(objectClass=user)(whenChanged>=");
filter.Append(date.ToADDateString());
filter.Append("))");
ds.Filter=filter.ToString();
ds.SearchScope = SearchScope.Subtree;
SearchResultCollection results= ds.FindAll();
foreach(SearchResult result in
results)
{
DataRow dr = dt.NewRow();
DirectoryEntry dey = GetDirectoryEntry(result.Path);
dr["EmployeeID"] = dey.Properties["employeeID"].Value;
dr["Name"] = dey.Properties["givenname"].Value;
dr["Email"] = dey.Properties["mail"].Value;
dt.Rows.Add(dr);
dey.Close();
}
de.Close();
return
dt;
}


Login to add your contents and source code to this article
 [Top] Rate this article
 About the author
 
Erika Ehrli
I am an Information Systems Engineer and I am currently enrolled to the UW Technical Writing and Editing Certificate Program. I love coding and writing, so this space is the combination of my two passions. I worked full-time in Microsoft Mexico as a Development Consultant and became specialized in Web Application Development.
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Go.NET
Build custom interactive diagrams, network, workflow editors, flowcharts, or software design tools. Includes many predefined kinds of nodes, links, and basic shapes. Supports layers, scrolling, zooming, selection, drag-and-drop, clipboard, in-place editing, tooltips, grids, printing, overview window, palette. 100% implemented in C# as a managed .NET Control. Document/View/Tool architecture with many properties&events. Optional automatic layout.
Dundas Software
Dundas Chart for .NET is the most advanced .NET charting package available today.  With an extremely complete feature set, elegant architecture and easy implementation, Dundas Chart can quickly add advanced Charting functionality to enhance and transform ASP.NET and Windows Forms applications.  Whether you are implementing charting into internal projects, or building applications for clients, Dundas Chart offers advanced technology and advanced results to get the most out of data.
Clickatell's SMS Gateway
Clickatell's Developer Solutions allow you to SMS enable any website or application via a range of API's. Learn More about our API connections.
Free access to .NET Memory Management video
Everything you need to know about Garbage Collection, Temporary Objects, Fragmentation, Finalization and common causes of memory leaks in .NET. Watch the video here.
Microsoft Visual Studio 2010
Microsoft Visual Studio 2010 offers more to developers than any other Visual Studio release. Work more productively and collaboratively-with greater control over your work at every step. The Beta 2 can give you a head start on achieving efficiency.
 
   Print Read/Post comments Post a comment  Rate  
   Email to a friend  Bookmark  Similar Articles  Author's other articles  
Download Files:
SetPassword.zip | MailBoxScripts.zip
 
 Post a Feedback, Comment, or Question about this article
Subject:  
Comment:  
Become a Sponsor
 Comments
ActiveDirectory .invoke problem by Chirag On November 16, 2006

Hi

I am using the active directory for one of our application.

I got a lot of help from u r article.

I am able to connect to active directory and create user and enable it but when i

try to set password it prompts with the following error.

"Exception has been thrown by the target of an invocation."

The inner exception:

{"You were not connected because a duplicate name exists on the network. Go to System in Control Panel to change the computer name and try again. (Exception from HRESULT: 0x80070034)"}

The code i have used is same as the one in u r article.

 

Can u please let me know the probable cause for the error .

Thanks and Regards

Chirag Shah

Reply | Email | Delete | Modify | 
cannot get ADHelper class by Subhra On April 19, 2007
Boss How to get ADHelper class It doesnot seem to be a microsoft class. which namespace is to be included to get it. I am using framework 2.0 and VS2005 90 days version.
Reply | Email | Delete | Modify | 
cannot get ADHelper class by Subhra On April 19, 2007
Boss How to get ADHelper class It doesnot seem to be a microsoft class. which namespace is to be included to get it. I am using framework 2.0 and VS2005 90 days version.
Reply | Email | Delete | Modify | 
Re: cannot get ADHelper class by Ka On May 3, 2007

Hi

ADHelper is a class which Erika has written for demonstrating the code. It is NOT a Microsoft prebuilt class.. You will have to see Erika's code and implement one on your own as per you need.

KayS

Reply | Email | Delete | Modify | 
Set User Account Profile? by G On July 18, 2007
Hi I create a user account in active directory and set the profile value (fill the "homedirectory" and "homedrive" properties and update user account). When I do this steps in "Active Directory Users and Computers", a folder was created and user access the folder. When I do this with program (mentioned above), properties value is set in active directory but the user folder does not create.Can you help me?
Reply | Email | Delete | Modify | 
thanks - please suggest by jeet On July 28, 2009
Hi Erika,
thanks for your neat and clean explanation to each section. It s a great help. however, can you suggest what should I code - to get the list of Active Directory fields of a given domain?
because in one of my Apps I wrote them static (givenname,telephone etc.) - but in target domain such fields are not there or with new names or with other fields. I want dynamically all the list of fields of AD. Please help  thanks - jeet   havejeet@gmail.com
Reply | Email | Delete | Modify | 

 Hosted by MaximumASP  |  Found a broken link?  |  Contact Us  |  Terms & conditions  |  Privacy Policy  |  Site Map  |  Suggest an Idea  |  Media Kit
Current Version: 5.2009.6.2
 © 1999 - 2009  Mindcracker LLC. All Rights Reserved