By David Wiseman (Administrator)Created 13 May 2012, Modified 20 May 2012
My Rating:
Vote
Rating:
Not Rated
Views:9432
Downloads:45
Source:

Creating a large number of test Active Directory users with PowerShell

Language:  PowerShell

Compatibility

Windows XP Unknown Windows 2003 Unknown
Windows 2000 No Windows NT No
Vista Unknown Windows 2008 Yes
Description

How to create a large number of AD user accounts using PowerShell.


Notes
After re-building my test Active Directory domain I needed a way to create a large number of user accounts.  Previously I would have used VBScript or my Account Management Spreadsheet, but this time I decided to use PowerShell - more as a learning exercise for myself than anything else.  
 
The requirements for the script is that it must be able to generate an arbitrary number of user accounts in a specified organizational unit.  The usernames are generated in the format firstname.surname with a number used as a uniquefier in the case of a name collision.
 
To generate accounts using real names instead of a more synthetic name like P000123, we need a data source that we can use to generate random names from.  After a quick google search I came up with:
http://www.census.gov/genealogy/names/dist.male.first
http://www.census.gov/genealogy/names/dist.female.first
http://www.census.gov/genealogy/names/dist.all.last
 
To get started, copy the contents of the above URLs into files called "MaleFirstNames.txt", "FemaleFirstNames.txt" and "LastNames.txt".  This will give us a source of male & female first names and surnames that we can use to generate fictitious user names.  The script will process these files to extract only the names.
 
To create the user accounts, save the script as "CreateUsers.ps1" in the same folder as the text files created earlier.  You can then create user accounts using the following command (Change the bold text as required):
 
./CreateUsers -Path "ou=Sales,ou=All Users,dc=wisesoft,dc=local", -NumUsers 1000
 
So we can create an arbitrary number of user accounts in any particular OU with realistic names, but what if we want to populate our domain with user accounts in a number of different organizational units?
 
I decided to create a second script that will take a comma-separated list of OUs, department names and number of users to create.  The CSV file might look similar to this:
 
Path,NumUsers,Department
"ou=Sales,ou=MyUsers,dc=wisesoft,dc=local",500,"Sales"
"ou=Marketing,ou=MyUsers,dc=wisesoft,dc=local",50,"Marketing"
 
The sample above would be used to create 500 users in the Sales OU and 50 users in the Marketing OU.  If the Sales and Marketing OUs didn't exist, the script would attempt to create them.  Simply save the csv file as "ADImport.csv" and run the "Script2" powershell script below.  Note: You will need to save the other script as "CreateUsers.ps1" and create the MaleFirstNames.txt, FemaleFirstNames.txt and LastNames.txt text files as described above.
 
Script2:
  1. # Function to create OU if it doesn't already exist  
  2. function createOU($path){  
  3.     if ([ADSI]::Exists("LDAP://$path")){  
  4.         return  
  5.     }  
  6.     else  
  7.     {  
  8.         # Convert path to ou.    
  9.         # e.g. "ou=Sales,ou=my users,dc=wisesoft,dc=local" to "ou=Sales"  
  10.         $ou = $path.Substring(0,$path.IndexOf(","))  
  11.         # Get parent ou/container e.g. "ou=my users,dc=wisesoft,dc=local"  
  12.         $parent = $path.Substring($ou.Length+1,$path.Length-$ou.Length-1)  
  13.         # Check if parent container exists and create it if required  
  14.         if ([ADSI]::Exists("LDAP://$parent") -eq $false){  
  15.             createOU($parent)  
  16.         }  
  17.         "Creating $ou in $parent"  
  18.         $objParent = [ADSI]"LDAP://$parent"  
  19.         #Create OU  
  20.         $objOU = $objParent.Create("OrganizationalUnit",$ou)  
  21.         $objOU.SetInfo()  
  22.     }  
  23. }  
  24. <#  Expects CSV file called "ADImport.csv" with "Path", NumUsers" and "Department" headers.  
  25.  e.g.  
  26. Path,NumUsers,Department  
  27. "ou=Sales,ou=MyUsers,dc=wisesoft,dc=local",500,"Sales"  
  28. "ou=Marketing,ou=MyUsers,dc=wisesoft,dc=local",50,"Marketing"  
  29.  
  30. #>  
  31. Import-Csv ADImport.csv | ForEach{  
  32.     $path = $_.Path  
  33.     $NumUsers = $_.NumUsers  
  34.     $Department = $_.Department  
  35.     createOU($path)  
  36.     "Creating $NumUsers in path $path..."  
  37.     ./CreateUsers -Path $path -NumUsers $NumUsers -Department $Department  
  38. }  

* Requires CreateUsers.ps1 script below.

 
Code

Line Numbers: On  Off      Plain Text
<#
Example:
./CreateUsers -Path ou=Sales,ou=My Users,dc=wisesoft,dc=local -NumUsers 500

Requirements:
MaleFirstNames.txt
	- Text file containing a list of male first names
	http://www.census.gov/genealogy/names/dist.male.first
FemaleFirstNames.txt
	- Text file containing a list of female first names
	http://www.census.gov/genealogy/names/dist.female.first
LastNames.txt
	- Text file containing a list of surnames
	http://www.census.gov/genealogy/names/dist.all.last


#>
param(
	[Parameter(Mandatory=$true)]
	[int]$numusers,
	[Parameter(Mandatory=$true)]
	[string]$path,
	[string]$Department,
	[System.DateTime]$AccountExpirationDate,
	[string]$AccountPassword,
	[bool] $ChangePasswordAtLogon=$true,
	[string]$City,
	[string]$Company,
	[string]$Country,
	[string]$Description,
	[string]$Division,
	[bool]$Enabled=$true,
	[string]$Office,
	[string]$Organization
)
Import-Module ActiveDirectory
[Reflection.Assembly]::LoadWithPartialName("System.Web") 
Add-Type -AssemblyName Microsoft.VisualBasic

# Load a list of male names.  Remove all text after first space and convert to proper case
Write-Host "Loading Male first names...."
$MaleNames = Get-Content "MaleFirstNames.txt" | ForEach-Object{ 
	$name = $_
	If ($name.IndexOf(" ") -ge 0) {
		$name = $name.Substring(0,($name.IndexOf(" ")))
	}
	$name = [Microsoft.VisualBasic.Strings]::StrConv($name,'ProperCase')
	$name
}
# Load a list of female names.  Remove all text after first space and convert to proper 
Write-Host "Loading Female first names...."
$FemaleNames = Get-Content "FemaleFirstNames.txt" | ForEach-Object{ 
	$name = $_
	if ($name.IndexOf(" ") -ge 0) {
		$name = $name.Substring(0,($name.IndexOf(" ")))
	}
	$name = [Microsoft.VisualBasic.Strings]::StrConv($name,'ProperCase')
	$name
}
# Load a list of surnames.  Remove all text after first space and convert to proper 
Write-Host "Loading Surnames names...."
$Surnames = Get-Content "LastNames.txt" | ForEach-Object{ 
	$name = $_
	if ($name.IndexOf(" ") -ge 0) {
		$name = $_.Substring(0,($name.IndexOf(" ")))
	}
	$name = [Microsoft.VisualBasic.Strings]::StrConv($name,'ProperCase')
	$name
}
# Function to generate a random female name
Function GetRandomFemaleName(){
	$random = Get-Random -Minimum 0 -Maximum ($FemaleNames.Count)
	return $FemaleNames.GetValue($random)
}
# Function to generate a random male name
Function GetRandomMaleName(){
	$random = Get-Random -Minimum 0 -Maximum ($MaleNames.Count)
	return $MaleNames.GetValue($random) 
}
# Function to generate a random surname
Function GetRandomSurname() {
	$random = Get-Random -Minimum 0 -Maximum ($Surnames.Count)
	return $Surnames.GetValue($random)
}
# Password supplied via command line
if ($AccountPassword.Length -gt 0){
	$password=$AccountPassword
	#Password needs to be converted to a secure string type for New-ADUser
	$passwordss = ConvertTo-SecureString -AsPlainText $password -Force
}

for ($i = 0; $i -lt $numusers; $i++) {
	# Even distribution of male/female names are used
	if (($i % 2) -eq 0){
		$givenName = GetRandomMaleName
	}
	else {
		$givenName = GetRandomFemaleName
	}
	$sn = GetRandomSurname
	$displayName="$givenName $sn"
	$sAMAccountName="$givenname.$sn"
	$sAMAccountName = $sAMAccountName.Substring(0,[System.Math]::Min($sAMAccountName.Length,20))
	# Generate a random password if not supplied via the command line
	if ($AccountPassword.Length -eq 0)
	{
		$password = [System.Web.Security.Membership]::GeneratePassword(20,2)
		#Password needs to be converted to a secure string type for New-ADUser
		$passwordss = ConvertTo-SecureString -AsPlainText $password -Force
	}
	
	$retryCount =0
	do{
		# Loop will be exited unless an account with the same name already exists (in which it will retry with a uniquefier)
		$completed=$true
		$cn = $sAMAccountName
		$email = "$sAMAccountName@$env:USERDNSDOMAIN"
		Write-Host "Creating user"($i+1)"of $numusers.....$sAMAccountName | Password: $password"
		
		try{
			New-ADUser -sAMAccountName $sAMAccountName -Name $cn -DisplayName $displayName -givenName $givenName -Surname $sn `
						-Path $path -Enable $Enabled -AccountPassword $passwordss -Department $department `
						-AccountExpirationDate $AccountExpirationDate -ChangePasswordAtLogon $ChangePasswordAtLogon `
						-City $City -Company $Company -Country $Country -Description $Description -Division $Division `
						-EmailAddress $email -Office $Office -Organization $Organization
		}
		catch [Microsoft.ActiveDirectory.Management.ADIdentityAlreadyExistsException]
		{
			# Failed to create user because an account with the same name already exists.
			# Add a number to the account name to make it unique, increasing the number with each retry
			"Unable to create user '$sAMAccountName' as the user already exists. "
			$sAMAccountName=$sAMAccountNameOriginal
			$retryCount +=1
			# Ensure new length won't exceed maximum account name length of 20.
			if (($sAMAccountName.Length + ([string]$retryCount).Length) -gt 20) {
				$sAMAccountName = $sAMAccountName.Substring(0,$sAMAccountName.Length-([string]$retryCount).Length)
			}
			# append sAMAccountName with a uniquefier 
			$sAMAccountName = $sAMAccountName + $retryCount
			$completed=$false #Don't exit loop
			"Retrying with $sAMAccountName..."
		}
	} until ($completed)
	
}

 


Got a useful script? Click here to upload!


 

  Post Comment
Order By:  
User Comments
      
Be the first to post a comment!