Have you ever had a need to generate a large number of user accounts within Active Directory and didn’t have the time to manually create them? If you are in the business of building test environments or training environments, this scenario may have happenned to you more than once in your career.

In a recent project, we had a need to create 600+ user accounts within our Active Directory domain to support a training class. I took the challenge of producing a simple and intuitive utilty that could do it for us, since I could probably code such an application much quicker than I could manually create the accounts. The end-result is the Active Directory User Creation Utility that I’m providing as part of this article. I figured others could use this as well so I wanted to provide this as a free download. The download is at the bottom of this article – feel free to give it a spin and let me know what you think. For those programmers out there that are just looking to understand how this utility was built, I’ve also provided the full source code as a separate download at the bottom of this article as well.

Launch and configure the utility
Double-click the CreateADUsers.exe file from the downloaded ZIP file to launch the utility. This application can be run from any domain member, it doesn’t have to be run on the Domain Controller. I believe the only requirements for use are that you should log onto your machine as a domain user that has rights to create objects within Active Directory. Your machine should also have the .NET Framework 3.5 installed.

If you have ever manually created user accounts within the Active Directory Users and Computers snap-in, the utility should be pretty self-explanatory. Just in case, the instructions for how to configure the utilty are provided below:

Setting Configuration
Domain (NetBIOS Name) Specify the NetBIOS name for your domain
Domain (Distinguished Name) Specify the FQDN for your domain
User OU Specify the Active Directory OU where you want the accounts placed. Examples:
To place within the Users OU, you can specify CN=Users;DC=mydomain;DC=com
To place within the Departments/HR OU, you can specify something like OU=HR;OU=Departments;DC=mydomain;DC=com
Single Account or Multiple Accounts Specify whether you looking to create a single account or multiple accounts
Username If you specified a single account, this is the username for the account
User Prefix If you specified multiple accounts, this is the user prefix for the accounts
Starting Index If you specified multiple accounts, this is the starting index for the accounts. For example, a user prefix of User with a starting index of 1 will create the accounts User1, User2, User3,…
Quantity If you specified multiple accounts, this is the number of accounts to create
Password Never Expires Specify whether to enable/disable the Password Never Expires attribute on the user accounts
User Cannot Change Password Specify whether to enable/disable the User Cannot Change Password attribute on the user accounts
Static Password or Random Password Specify whether to define a static password across all auto-generated accounts or define a random password for each account
Password If you specified a static password, this is the user-defined password for the accounts
Password Length If you specified a random password, this is the number of characters to include in the password
Require Capital Letter If you specified a random password, this indicates whether the random password should include at least one capital letter
Require Lower Case Letter If you specified a random password, this indicates whether the random password should include at least one lower case letter
Require Number If you specified a random password, this indicates whether the random password should include at least one number
Store username and password details in text file Specify whether you want to place the generated account details within a text file. This is recommended if using random passwords since this will be the only way to get the passwords for those accounts
Text File Name Specify the name of the text file that will contain the generated account details. The default name is Accounts.txt
Text File Location Specify the location where the text file will be saved.

Run the utility
When you click the Create Users button, the utility will attempt to create the requested number of accounts in the specified OU within Active Directory. If you requested a large number of accounts, it may take a few minutes. A message box will be displayed with the status of the request. An event log entry is also created with additional details and is recommended to check if you run into any issues.

If you specified random passwords for the accounts, you will definitely need to check out the Accounts.txt file that is generated so you can get the passwords for those accounts. You should record these passwords in the proper location and delete this file for security purposes

Verify the Active Directory accounts have been created
Finally, open the Active Directory Users and Computers snap-in and verify the accounts were created in the proper OU. Feel free to modify the accounts further with profile information or other details. The accounts will be easily visible since the text Auto-generated account will be shown in the description field.

Key source code
Many of you may want to just incorporate this functionality into an existing application that you are building. I provided the full source code as a separate download at the bottom of this article. It’s a Visual Studio 2008 project. If you just wanted to skim the source code, here’s the key code snippets to look at. I’m leveraging the PrincipalContext and UserPrincipal objects provided in the .NET Framework 3.5 library to communicate with Active Directory.

Here are the includes for the project:

using <span class="code-object">System</span>;
using <span class="code-object">System</span>.Collections.Generic;
using <span class="code-object">System</span>.ComponentModel;
using <span class="code-object">System</span>.Data;
using <span class="code-object">System</span>.Drawing;
using <span class="code-object">System</span>.Linq;
using <span class="code-object">System</span>.Text;
using <span class="code-object">System</span>.Windows.Forms;
using <span class="code-object">System</span>.Diagnostics;
using <span class="code-object">System</span>.DirectoryServices;
using <span class="code-object">System</span>.DirectoryServices.AccountManagement;
using <span class="code-object">System</span>.IO;


Here is the function that creates the Active Directory accounts:

<span class="code-keyword">private</span> void btnCreateUsers_Click(object sender, EventArgs e)
{
     <span class="code-comment">//**************************************************
</span>     <span class="code-comment">//Define local variables
</span>     <span class="code-comment">//**************************************************
</span>     StreamWriter l_objWriter = <span class="code-keyword">null</span>;
     string l_strMessage = "";
     string l_strErrorAccountList = "";
     <span class="code-object">int</span> l_intSuccessAttempts = 0, l_intErrorAttempts = 0;

     <span class="code-keyword">try</span>
     {
         <span class="code-comment">//**************************************************
</span>         <span class="code-comment">//Grab values from form
</span>         <span class="code-comment">//**************************************************
</span>         string l_strDomain = txtDomain.Text.Trim();
         string l_strDomainDN = txtDomainDN.Text.Trim();
         string l_strUserOU = txtUserOU.Text.Trim();
         string l_strUserPrefix = txtUserPrefix.Text.Trim();
         string l_strUsername = <span class="code-quote">"", l_strPassword = "</span>";
         <span class="code-object">int</span> l_intQuantity = Convert.ToInt32(txtQuantity.Text.Trim());
         <span class="code-object">int</span> l_intStartingIndex = Convert.ToInt32(txtStartingIndex.Text.Trim());
         <span class="code-object">int</span> l_intPasswordLength = Convert.ToInt32(txtPasswordLength.Text.Trim());
         bool l_blnPasswordNeverExpires = chkPasswordNeverExpires.Checked;
         bool l_blnUserCannotChangePassword = chkUserCannotChangePassword.Checked;
         bool l_blnRequireCapital = chkRequireCapital.Checked;
         bool l_blnRequireLowerCase = chkRequireLowerCase.Checked;
         bool l_blnRequireNumber = chkRequireNumber.Checked;
         string l_strTextFileName = txtTextFileName.Text.Trim();
         string l_strTextFileLocation = txtTextFileLocation.Text.Trim();
         string l_strTextFileFullPath = l_strTextFileLocation + <span class="code-quote">"\\"</span> + l_strTextFileName;

         <span class="code-comment">//******************************************************************
</span>         <span class="code-comment">//Check whether to create single account or multiple accounts
</span>         <span class="code-comment">//******************************************************************
</span>         <span class="code-keyword">if</span> (rdoSingleAccount.Checked)  <span class="code-comment">//Create single account
</span>         {
             <span class="code-comment">//Create user object
</span>             PrincipalContext l_objContext = <span class="code-keyword">new</span> PrincipalContext(ContextType.Domain, l_strDomain, l_strUserOU);
             UserPrincipal l_objNewUser = <span class="code-keyword">new</span> UserPrincipal(l_objContext);

             <span class="code-comment">//Set username and password
</span>             l_strUsername = txtUsername.Text.Trim();
             <span class="code-keyword">if</span> (rdoStaticPassword.Checked) l_strPassword = txtPassword.Text.Trim();
             <span class="code-keyword">else</span> l_strPassword = <span class="code-keyword">this</span>.GenerateRandomPassword(l_intPasswordLength, l_blnRequireCapital, l_blnRequireLowerCase, l_blnRequireNumber);
             l_objNewUser.SamAccountName = l_strUsername;
             l_objNewUser.SetPassword(l_strPassword);

             <span class="code-comment">//Assign user properties
</span>             l_objNewUser.UserPrincipalName = l_strUsername + <span class="code-quote">"@"</span> + l_strDomainDN;
             l_objNewUser.DisplayName = l_strUsername;
             l_objNewUser.Description = <span class="code-quote">"Auto-generated account"</span>;
             l_objNewUser.GivenName = l_strUsername;    <span class="code-comment">//first name
</span>             l_objNewUser.PasswordNeverExpires = l_blnPasswordNeverExpires;
             l_objNewUser.UserCannotChangePassword = l_blnUserCannotChangePassword;
             l_objNewUser.Enabled = <span class="code-keyword">true</span>;

             <span class="code-comment">//Check <span class="code-keyword">if</span> user already exists within Active Directory
</span>             UserPrincipal usr = UserPrincipal.FindByIdentity(l_objContext, l_strUsername);
             <span class="code-keyword">if</span> (usr == <span class="code-keyword">null</span>)  <span class="code-comment">//User not found.  Proceed with save
</span>             {
                 <span class="code-comment">//Save user to the directory
</span>                 l_objNewUser.Save();
                 l_intSuccessAttempts++;
             }
             <span class="code-keyword">else</span> <span class="code-comment">//User already exists
</span>             {
                 <span class="code-keyword">if</span> (l_strErrorAccountList == "") l_strErrorAccountList = l_strUsername;
                 <span class="code-keyword">else</span> l_strErrorAccountList += <span class="code-quote">", "</span> + l_strUsername;
                 l_intErrorAttempts++;
             }

             <span class="code-comment">//Check <span class="code-keyword">if</span> account details should be stored in text file
</span>             <span class="code-keyword">if</span> (chkStoreInTextFile.Checked)
             {
                 <span class="code-comment">//Save account details to file
</span>                 l_objWriter = <span class="code-keyword">new</span> StreamWriter(l_strTextFileFullPath, <span class="code-keyword">false</span>);
                 l_objWriter.WriteLine(<span class="code-quote">"1,"</span> + l_strUsername + <span class="code-quote">","</span> + l_strPassword);
                 l_objWriter.Close();
             }
         }
         <span class="code-keyword">else</span>  <span class="code-comment">//Create multiple accounts
</span>         {
             <span class="code-comment">//Create the specified number of user accounts
</span>             <span class="code-keyword">for</span> (<span class="code-object">int</span> index = 1; index &lt;= l_intQuantity; index++)
             {
                 <span class="code-comment">//Create user object
</span>                 PrincipalContext l_objContext = <span class="code-keyword">new</span> PrincipalContext(ContextType.Domain, l_strDomain, l_strUserOU);
                 UserPrincipal l_objNewUser = <span class="code-keyword">new</span> UserPrincipal(l_objContext);

                 <span class="code-comment">//Set username and password
</span>                 l_strUsername = l_strUserPrefix + l_intStartingIndex.ToString();
                 <span class="code-keyword">if</span> (rdoStaticPassword.Checked) l_strPassword = txtPassword.Text.Trim();
                 <span class="code-keyword">else</span> l_strPassword = <span class="code-keyword">this</span>.GenerateRandomPassword(l_intPasswordLength, l_blnRequireCapital, l_blnRequireLowerCase, l_blnRequireNumber);
                 l_objNewUser.SamAccountName = l_strUsername;
                 l_objNewUser.SetPassword(l_strPassword);

                 <span class="code-comment">//Assign user properties
</span>                 l_objNewUser.UserPrincipalName = l_strUsername + <span class="code-quote">"@"</span> + l_strDomainDN;
                 l_objNewUser.DisplayName = l_strUsername;
                 l_objNewUser.Description = <span class="code-quote">"Auto-generated account"</span>;
                 l_objNewUser.GivenName = l_strUsername;    <span class="code-comment">//first name
</span>                 l_objNewUser.PasswordNeverExpires = l_blnPasswordNeverExpires;
                 l_objNewUser.UserCannotChangePassword = l_blnUserCannotChangePassword;
                 l_objNewUser.Enabled = <span class="code-keyword">true</span>;

                 <span class="code-comment">//Check <span class="code-keyword">if</span> user already exists within Active Directory
</span>                 UserPrincipal usr = UserPrincipal.FindByIdentity(l_objContext, l_strUsername);
                 <span class="code-keyword">if</span> (usr == <span class="code-keyword">null</span>)  <span class="code-comment">//User not found.  Proceed with save
</span>                 {
                     <span class="code-comment">//Save user to the directory
</span>                     l_objNewUser.Save();
                     l_intSuccessAttempts++;

                     <span class="code-comment">//Check <span class="code-keyword">if</span> account details should be stored in text file
</span>                     <span class="code-keyword">if</span> (chkStoreInTextFile.Checked)
                     {
                         <span class="code-comment">//Save account details to file
</span>                         <span class="code-keyword">if</span> (l_objWriter == <span class="code-keyword">null</span>) l_objWriter = <span class="code-keyword">new</span> StreamWriter(l_strTextFileFullPath, <span class="code-keyword">false</span>);
                         l_objWriter.WriteLine(index + <span class="code-quote">","</span> + l_strUsername + <span class="code-quote">","</span> + l_strPassword);
                     }
                 }
                 <span class="code-keyword">else</span> <span class="code-comment">//User already exists
</span>                 {
                     <span class="code-keyword">if</span> (l_strErrorAccountList == "") l_strErrorAccountList = l_strUsername;
                     <span class="code-keyword">else</span> l_strErrorAccountList += <span class="code-quote">", "</span> + l_strUsername;
                     l_intErrorAttempts++;
                 }

                 <span class="code-comment">//Increment starting index
</span>                 l_intStartingIndex++;

                 <span class="code-comment">//Close writer <span class="code-keyword">if</span> <span class="code-keyword">this</span> was the last check
</span>                 <span class="code-keyword">if</span> (chkStoreInTextFile.Checked &amp;&amp; index == l_intQuantity &amp;&amp; l_objWriter != <span class="code-keyword">null</span>) l_objWriter.Close();
             }
         }

         <span class="code-comment">//Write event log entry and display status to user
</span>         <span class="code-keyword">if</span> (l_intSuccessAttempts &gt; 0)
         {
             l_strMessage += <span class="code-quote">"Successfully created "</span> + l_intSuccessAttempts.ToString() + <span class="code-quote">" account(s).\r\n"</span>;
         }
         <span class="code-keyword">if</span> (l_intErrorAttempts &gt; 0)
         {
             l_strMessage += <span class="code-quote">"Could not create "</span> + l_intErrorAttempts.ToString() + <span class="code-quote">" account(s): "</span> + l_strErrorAccountList + <span class="code-quote">"\r\n\r\n"</span>;
             l_strMessage += <span class="code-quote">"If any accounts were not created, they most likely already exist within Active Directory."</span>;
         }
         <span class="code-object">System</span>.Diagnostics.EventLog.WriteEntry(<span class="code-quote">"CreateADUsers"</span>, l_strMessage, EventLogEntryType.Information, 1001);
         MessageBox.Show(<span class="code-quote">"Task Completed!\r\n"</span> + l_strMessage, <span class="code-quote">"Create Users Task"</span>);
     }
     <span class="code-keyword">catch</span> (Exception objException)
     {
         <span class="code-comment">//Write event log entry
</span>         <span class="code-object">System</span>.Diagnostics.EventLog.WriteEntry(<span class="code-quote">"CreateADUsers"</span>, <span class="code-quote">"Error creating user within application.\r\n\r\nAdditional details:\r\n"</span> + objException.Message, EventLogEntryType.Error, 1002);
         MessageBox.Show(<span class="code-quote">"Error!  Please check event viewer..."</span>, <span class="code-quote">"Create Users Task"</span>);
     }
}


Here is the function that generates the random password:

<span class="code-keyword">public</span> string GenerateRandomPassword(<span class="code-object">int</span> p_intPasswordLength, bool p_blnRequireCapital, bool p_blnRequireLowerCase, bool p_blnRequireNumber)
{
     <span class="code-comment">//Define available characters <span class="code-keyword">for</span> password
</span>     string[] l_strCapitals = { <span class="code-quote">"A"</span>,<span class="code-quote">"B"</span>,<span class="code-quote">"C"</span>,<span class="code-quote">"D"</span>,<span class="code-quote">"E"</span>,<span class="code-quote">"F"</span>,<span class="code-quote">"G"</span>,<span class="code-quote">"H"</span>,<span class="code-quote">"I"</span>,<span class="code-quote">"J"</span>,<span class="code-quote">"K"</span>,<span class="code-quote">"L"</span>,<span class="code-quote">"M"</span>,<span class="code-quote">"N"</span>,<span class="code-quote">"O"</span>,<span class="code-quote">"P"</span>,<span class="code-quote">"Q"</span>,<span class="code-quote">"R"</span>,<span class="code-quote">"S"</span>,<span class="code-quote">"T"</span>,<span class="code-quote">"U"</span>,<span class="code-quote">"V"</span>,<span class="code-quote">"W"</span>,<span class="code-quote">"X"</span>,<span class="code-quote">"Y"</span>,<span class="code-quote">"Z"</span> };
     string[] l_strLowerCases = { <span class="code-quote">"a"</span>,<span class="code-quote">"b"</span>,<span class="code-quote">"c"</span>,<span class="code-quote">"d"</span>,<span class="code-quote">"e"</span>,<span class="code-quote">"f"</span>,<span class="code-quote">"g"</span>,<span class="code-quote">"h"</span>,<span class="code-quote">"i"</span>,<span class="code-quote">"j"</span>,<span class="code-quote">"k"</span>,<span class="code-quote">"l"</span>,<span class="code-quote">"m"</span>,<span class="code-quote">"n"</span>,<span class="code-quote">"o"</span>,<span class="code-quote">"p"</span>,<span class="code-quote">"q"</span>,<span class="code-quote">"r"</span>,<span class="code-quote">"s"</span>,<span class="code-quote">"t"</span>,<span class="code-quote">"u"</span>,<span class="code-quote">"v"</span>,<span class="code-quote">"w"</span>,<span class="code-quote">"x"</span>,<span class="code-quote">"y"</span>,<span class="code-quote">"z"</span> };
     string[] l_strNumbers = { <span class="code-quote">"1"</span>,<span class="code-quote">"2"</span>,<span class="code-quote">"3"</span>,<span class="code-quote">"4"</span>,<span class="code-quote">"5"</span>,<span class="code-quote">"6"</span>,<span class="code-quote">"7"</span>,<span class="code-quote">"8"</span>,<span class="code-quote">"9"</span>,<span class="code-quote">"0"</span> };
     string[] l_strCharacters = { <span class="code-quote">"A"</span>,<span class="code-quote">"B"</span>,<span class="code-quote">"C"</span>,<span class="code-quote">"D"</span>,<span class="code-quote">"E"</span>,<span class="code-quote">"F"</span>,<span class="code-quote">"G"</span>,
                                  <span class="code-quote">"H"</span>,<span class="code-quote">"I"</span>,<span class="code-quote">"J"</span>,<span class="code-quote">"K"</span>,<span class="code-quote">"L"</span>,<span class="code-quote">"M"</span>,<span class="code-quote">"N"</span>,<span class="code-quote">"O"</span>,<span class="code-quote">"P"</span>,<span class="code-quote">"Q"</span>,<span class="code-quote">"R"</span>,<span class="code-quote">"S"</span>,<span class="code-quote">"T"</span>,<span class="code-quote">"U"</span>,<span class="code-quote">"V"</span>,<span class="code-quote">"W"</span>,<span class="code-quote">"X"</span>,<span class="code-quote">"Y"</span>,
                                  <span class="code-quote">"Z"</span>,<span class="code-quote">"1"</span>,<span class="code-quote">"2"</span>,<span class="code-quote">"3"</span>,<span class="code-quote">"4"</span>,<span class="code-quote">"5"</span>,<span class="code-quote">"6"</span>,<span class="code-quote">"7"</span>,<span class="code-quote">"8"</span>,<span class="code-quote">"9"</span>,<span class="code-quote">"0"</span>,<span class="code-quote">"a"</span>,<span class="code-quote">"b"</span>,<span class="code-quote">"c"</span>,<span class="code-quote">"d"</span>,<span class="code-quote">"e"</span>,<span class="code-quote">"f"</span>,<span class="code-quote">"g"</span>,<span class="code-quote">"h"</span>,
                                  <span class="code-quote">"i"</span>,<span class="code-quote">"j"</span>,<span class="code-quote">"k"</span>,<span class="code-quote">"l"</span>,<span class="code-quote">"m"</span>,<span class="code-quote">"n"</span>,<span class="code-quote">"o"</span>,<span class="code-quote">"p"</span>,<span class="code-quote">"q"</span>,<span class="code-quote">"r"</span>,<span class="code-quote">"s"</span>,<span class="code-quote">"t"</span>,<span class="code-quote">"u"</span>,<span class="code-quote">"v"</span>,<span class="code-quote">"w"</span>,<span class="code-quote">"x"</span>,<span class="code-quote">"y"</span>,<span class="code-quote">"z"</span>};

     <span class="code-comment">//Define additional variables
</span>     string l_strPassword = "";
     Random rGen = <span class="code-keyword">new</span> Random();
     <span class="code-object">int</span> l_intPosition = 0;

     <span class="code-comment">//Check <span class="code-keyword">if</span> capital is required...
</span>     <span class="code-keyword">if</span> (p_blnRequireCapital &amp;&amp; p_intPasswordLength &gt; 0)
     {
         l_intPosition = rGen.Next(0, 25);
         l_strPassword += l_strCapitals[l_intPosition];
         p_intPasswordLength = p_intPasswordLength - 1;  <span class="code-comment">//Adjust remaining password length
</span>     }

     <span class="code-comment">//Check <span class="code-keyword">if</span> lower <span class="code-keyword">case</span> is required...
</span>     <span class="code-keyword">if</span> (p_blnRequireLowerCase &amp;&amp; p_intPasswordLength &gt; 0)
     {
         l_intPosition = rGen.Next(0, 25);
         l_strPassword += l_strLowerCases[l_intPosition];
         p_intPasswordLength = p_intPasswordLength - 1;  <span class="code-comment">//Adjust remaining password length
</span>     }

     <span class="code-comment">//Check <span class="code-keyword">if</span> number is required...
</span>     <span class="code-keyword">if</span> (p_blnRequireNumber &amp;&amp; p_intPasswordLength &gt; 0)
     {
         l_intPosition = rGen.Next(0, 9);
         l_strPassword += l_strNumbers[l_intPosition];
         p_intPasswordLength = p_intPasswordLength - 1;  <span class="code-comment">//Adjust remaining password length
</span>     }

     <span class="code-comment">//Generate remaining password characters
</span>     <span class="code-keyword">for</span> (<span class="code-object">int</span> i = 1; i &lt;= p_intPasswordLength; i++)
     {
         l_intPosition = rGen.Next(0, 60);
         l_strPassword += l_strCharacters[l_intPosition];
     }

     <span class="code-comment">//Return password
</span>     <span class="code-keyword">return</span> l_strPassword;
}


Download the utility and full source code
The utility and full source code are provided below:

Ed York – Architect – Worldwide Technical Readiness
Ask-the-Architect Site: http://community.citrix.com/p/product-automation#home
Follow Me on twitter: http://twitter.com/citrixedy