Automating Active Directory account creation with Powershell

Earlier this year I set about simplifying the user account creation process for the company I work for. Previously when someone joined the company the user account creation was a manual process. Some of the tasks we manually undertook were:

I chose Powershell as my tool of choice, it’s extremely flexible for stuff like this and most of the management functionality comes out the box with Exchange 2007 and later. I initially used Glen Scales’ EWS module for setting permissions and other bits but as we moved to Exchange 2010 the need for EWS disappeared (Thanks to functionality like Set-MailboxFolderPermissions).

The only third-party software I now use is Quest’s ActiveRoles Management Shell for Active Directory as there doesn’t seem to be a decent way to set custom attributes on a user account. Short of writing my own managed code to do it, or calling another application to make the change Quest’s tool seems to be the best choice.

I eventually came up with the following quick and dirty script to create users, based on the contents of a CSV file it will:

The format of the CSV file is as follows:

John,Doe,MyFirstPassword123!,London,Test Account

And finally, the PowerShell script itself:

# Create user accounts in AD, Exchange and OCS.
# This script will read a CSV file and create user accounts based on that information.
# Requirements:
#  [+] http://www.quest.com/powershell/activeroles-server.aspx (For setting
#      attributes on an AD DS account)
#  [+] Exchange Management Console installed (for Powershell stuff, new-mailbox,
#      Get-MailboxDatabase etc).
#import-module activedirectory;
Add-PSSnapin Quest.ActiveRoles.ADManagement

$cfgTab = [char]9 
$cfgCompany = "NW Traders";
$cfgOCSHomeServer = "CN=LC Services,CN=Microsoft,CN=OCSSRV01,CN=Pools,CN=RTC Service,CN=Services,CN=Configuration,DC=nwtraders,DC=msft"; # OCS Server
$cfgMailDomain = "@nwtraders.msft"; #E-Mail Domain

# A series of hash tables for office information.
$cfgLondon = @{
  "Address" = "10 Downing Street, London, SW1A 2AA";
  "Telephone" = "+44 2079 250 918";
  "OU" = "OU=London,OU=Europe,OU=Users,DC=NWTRADERS,DC=MSFT";
  "DC" = "LONDON" };

$cfgWashington = @{
  "Address" = "The White House, 1600 Pennsylvania Avenue NW, Washington, DC 20500";
  "Telephone" = "+1 202 456 1414";
   "OU" = "OU=Washington,OU=US,OU=Users,DC=NWTRADERS,DC=MSFT";
  "DC" = "WASHINGTON" };

# Creates an array of the above hash tables.
$cfgOffices = @{
  "London" = $cfgLondon;
  "Washington" = $cfgWashington;

# Displays a select file dialog box, returning the path to a CSV file.
function chooseCSVfile
	param([string]$Title,[string]$Directory,[string]$Filter="CSV Files (*.csv)|*.csv")
	[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
	$openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
	$openFileDialog.InitialDirectory = $Directory
	$openFileDialog.Filter = $Filter
	$openFileDialog.Title = $Title
	$openFileDialog.ShowHelp = $true
	$Show = $openFileDialog.ShowDialog()
	If ($Show -eq "OK")
		Return $openFileDialog.FileName

# Generate welcome e-mail.
Function generateIntroLetter
	param( [string]$GivenName, [string]$samAccountName, [string]$Password, [string]$DisplayName, [string]$DomainController )

	$tmpServer = get-mailbox $DisplayName -DomainController $DomainController | select servername
	$tmpOWA = get-OutlookAnywhere -Server $tmpServer.ServerName -DomainController $DomainController -ADPropertiesOnly | select ExternalHostname
	$tmpOWA = "https://" + $tmpOWA.ExternalHostname + "/owa/"

	$rtfBuilder = new-object system.text.stringbuilder 

	# Append RTF header 
	$null = $rtfBuilder.Append("{\rtf1\ansi\ansicpg1252\deff0\deflang2057{\fonttbl{\f0\fswiss\fprq2\fcharset0 Calibri;}{\f1\fnil\fcharset0 Calibri;}}") 
	$null = $rtfBuilder.Append("`r`n") 

	# Append RTF color table which will contain all Powershell console colors. 
	#$null = $rtfBuilder.Append('{\colortbl ;\red0\green0\blue255;}')
	#$null = $rtfBuilder.Append("`r`n") 

	$null = $rtfBuilder.Append("{\*\generator Msftedit;}\viewkind4\uc1\pard\sa200\sl276\slmult1\f0\fs22 Dear $GivenName,\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("Please find the below access credentials for the NW Traders network. These are the only credentials you should need at NW Traders and will log you into your computer, e-mail account and other systems. As such, they should be kept confidential.\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("\tab Username\tab\tab $samAccountName\line\tab Password\tab\tab $PlainPassword\line\tab Domain\tab\tab\tab NWTRADERS\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("It is highly recommended that you change your password the first time you login. You should be able to use Outlook both in the office and at home, if for some reason that is not available you can access webmail using the following address:\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("\pard\tab{\field{\*\fldinst{HYPERLINK `$tmpOWA`}}{\fldrslt{\ul\cf1 h$tmpOWA}}}\f0\fs22\pard\par")
	$null = $rtfBuilder.Append("\pard\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("If you require support please contact the IT Support team via e-mail, {\field{\*\fldinst{HYPERLINK `mailto:it@nwtraders.msft` }}{\fldrslt{\cf1\ul it@nwtraders.msft}}}\cf0\ulnone\f0\fs22 . Alternatively an increasing number of self-service resources are available on our intranet:\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("\pard\fi720\par")
	$null = $rtfBuilder.Append("\pard\fi720{\field{\*\fldinst{HYPERLINK `http://intranet/`}}{\fldrslt{\ul\cf1 http://intranet/}}}\f0\fs22\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("\pard\sa200\sl276\slmult1\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("Kind Regards,\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("IT Support.\lang9\f1\par")
	$null = $rtfBuilder.Append("`r`n") 
	$null = $rtfBuilder.Append("}")

	# Save as RTF File.
	echo $rtfBuilder.ToString() | out-file -Encoding Ascii "$samAccountName.rtf"

# Displays a list of mailbox databases, which the user needs to choose from.
Function chooseMailboxDatabase()
	$MbDbase = Get-MailboxDatabase
	$NumOfDB = $MbDbase.Count
	$Number = 0
	$Choice = 0
	If ($NumOfDB -eq $Null)
		Write-Host $MbDbase.Identity
		return $MbDbase.Identity
		foreach ($mbxDB in $MbDbase)
			Write-Host "$Number . " $MbxDB.Identity
			$Number ++
		Write-Host ""
		$Choice = Read-Host "Mailbox Database"
		return $MbDbase[$Choice].Identity

# Opening user detail list"
$FileName = chooseCSVfile -Title "Import an CSV file" -Directory "c:\"
$UserInformation = Import-Csv $FileName

# Do some logic about our environment.
Foreach ($User in $UserInformation)
	$SurName = $User.Surname
	$GivenName = $User.givenName
	$samAccountName = $GivenName + " " + $SurName
	$DisplayName = $SurName + ", " + $GivenName
	$PlainPassword = $User.Password
	$userPrincipalName = $samAccountName + $cfgMailDomain
	#Exchange Specific
	$strMailAddress = $samAccountName -replace " ", ".";
	$strMailAddress += $cfgMailDomain;
	$strMailAlias = $samAccountName -replace " ", ".";
	# Attributes.
	$strOffice = $User.PhisicalDeliveryLocation;
	$strTitle = $user.JobTitle;
	$strOU	= $cfgOffices.Get_Item( $strOffice ).Get_Item("OU")
	$strOAddress = $cfgOffices.Get_Item( $strOffice ).Get_Item("Address");
	$strOTel = $cfgOffices.Get_Item( $strOffice ).Get_Item("Telephone");

Write-Host -Foreground Gray "---------------------------------------------------------------"
Write-Host -Foreground Red " "$DisplayName
Write-Host -Foreground Gray "---------------------------------------------------------------"
	Write-Host " Username:"$cfgTab$samAccountName;
	Write-Host " Password:"$cfgTab$PlainPassword;
	Write-Host " Job Title:"$cfgTab$strTitle;
	Write-Host " OU:"$cfgTab$cfgTab$strOU;
	Write-Host " E-Mail:"$cfgTab$strMailAddress
Write-Host -Foreground Gray "---------------------------------------------------------------"
Write-Host ""

	# Choose a mailbox database for this account.
	$mbDatabase = chooseMailboxDatabase
	Write-Host ""
	Write-Host -Foreground Gray "---------------------------------------------------------------"
	# Lets actually create the account now.
	$Password = ConvertTo-SecureString $PlainPassword -AsPlainText -Force

	# Create Exchange mailbox.
	New-Mailbox -Name $DisplayName -Alias $strMailAlias -OrganizationalUnit $strOU -UserPrincipalName $userPrincipalName -SamAccountName $samAccountName -FirstName $GivenName -Initials '' -LastName $SurName -Password $Password -ResetPasswordOnNextLogon $false -Database $mbDatabase -DomainController $cfgOffices.Get_Item( $strOffice ).Get_Item("DC")  | out-null
	# Set Quest Active Directory stuff to use a DC in the local site (mostly).
	# Otherwise trying to make exchange changes in a remote site using a local
	# DC is going to fail, since the account doesn't exist yet.
	Connect-QADService -service $cfgOffices.Get_Item( $strOffice ).Get_Item("DC") | out-null

	# Set attributes on AD DS account.	
	Get-QADUser $DisplayName | set-qaduser -oa @{'Office'=$strOffice; 'StreetAddress'=$strOAddress; 'OfficePhone'=$strOTel; 'Company'=$cfgCompany; 'Title'=$strTitle } | out-null

	# Set calendar permissions.
	Set-MailboxFolderPermission -Identity $strMailAddress`:\Calendar -User Default -AccessRights Reviewer -DomainController $cfgOffices.Get_Item( $strOffice ).Get_Item("DC")  | out-null
	# Create the OCS Account.
	Get-QADUser $DisplayName | set-qaduser -oa @{'msRTCSIP-ArchivingEnabled'=0; 'msRTCSIP-FederationEnabled'=$true; 'msRTCSIP-InternetAccessEnabled'=$true; 'msRTCSIP-OptionFlags'=257; 'msRTCSIP-UserEnabled'=$true; 'msRTCSIP-PrimaryHomeServer'=$cfgOCSHomeServer; 'msRTCSIP-PrimaryUserAddress'=("sip:" + $strMailAddress ).ToString() } | out-null

	# Disconnect QADService.

	# Generate Welcome e-mail.
	generateIntroLetter -GivenName $GivenName -samAccountName $samAccountName -Password $PlainPassword -DisplayName $DisplayName -DomainController $cfgOffices.Get_Item( $strOffice ).Get_Item("DC")

You can expect outlook similar to the below:

  Doe, John
 Username:      John Doe
 Password:      MyFirstPassword123!
 Company:       NW Traders
 Job Title:     Test Account
 OU:            OU=London,OU=Europe,OU=Users,DC=NWTRADERS,DC=MSFT
 E-Mail:        John.Doe@nwtraders.msft


Mailbox Database: 0

You can then send the resulting RTF document to the persons line manager, or whoever should have it for when the employee starts on their first day.

I should note, Andy Grogan (Exchange MVP) has a very similar tool which you might like to take a look at. I gleaned the Open-CSV idea and plagiarised the choose mailbox database code from him.