Script: Create-Mailbox.ps1 – Copy an existing user and create a mailbox

8/22/14 Update: I’ve made some improvements to the script, make sure you get the latest version from:

One of the most common tasks for Exchange admin is creating mailboxes. This can easily be done with the GUI (EMC\EAC) but some settings can’t be set using the creation wizard or in the GUI at all. So I always recommend my clients automate the creation of mailboxes to ensure that they are created with the correct attributes. So I’ve decided to share my mailbox creation script with everyone!

This script will not only create a mailbox, but it will also copy an existing user and some of their attribute (using New-ADUser -Instance $objSourceUser) and optionally send them a “Welcome” e-mail. When creating the mailbox the script will get the smallest database, by size, that matches a filter provided and finally set the retention and arching policy on it.

The latest source of this script should always be here:

Usage example: .\Create-Mailbox.ps1 -NewUserID “JoeSales” -GivenName “Joe” -Surname “Sales” -SourceUserID “_SalesTemplate” -CustomAttrib “Team 1”

The example above will create a new user account for “Joe Sales” based on the “_SalesTemplate” account. By default it will copy the following attributes from the template: MemberOf, Description, Company, Department, Office, Title, streetAddress, City, State, PostalCode, Country. In addition, it will optionally set CustomAttribute10 to “Team 1”; this is  handy if you have EAPs that use a custom attribute.

The script has some basic error handling and by default will continue the mailbox creation process if the new user ID already exist.

The following lines should be customized for your environment:

$Group = ‘Mail Users’ # Only used if $AddToGroup = $True

$Password = “P@ssW0rD!”
$UPNSufix = “”

$SMTPServer = “”
$From = “”
$MsgSubject = “Welcome to the Company!”
$MsgBody = Get-Content “D:\Tools\Welcome.htm”(Example here:

# This script create a new mailbox on the smallest mailbox database that matches a filter and set other attributes

# Created by Jason Sherry ( 4/20/2012
# Last Updated: 8/22/2014
#	5/24/12: Added support to add user to a group & set CustomAttribute10
# 7/8/13: Added support to copy a template user account, including a list of attributes and group membership
# 7/23/13: Fixed an issue with $Null detection for parameters
# 8/22/14: Added error handling for when no databases are returned, when no other name values are provided, & when source member of is empty

# Source:
#	 Partial code source:

	[Parameter(Mandatory = $true)][String]$NewUserID,
	[String]$SourceUserID = $Null,
	[String]$DisplayName = $Null,
	[String]$GivenName = $Null,
	[String]$Surname = $Null,
	[String]$CustomAttrib = $Null,
	[String]$SendWelcome = $True)

$DBFilter = "Primary*" # Limit databases to only those that start with "Primary"
$RetentionPolicy = "Default Archive and Retention Policy"

$ContiuneOnExist = $True # Option good for testing

$CopyAttributes = "MemberOf","Description","Company","Department","Office","Title","streetAddress","City","State","PostalCode","Country"
# List of properties:

$AddToGroup = $False
$Group = 'Mail Users'

$Password = "P@ssW0rD!"
$UPNSufix = ""

$SMTPServer = ""
$From = ""
$MsgSubject = "Welcome to the Company!"
$MsgBody = Get-Content "D:\Tools\Welcome.htm" | out-string

Function Get-SmallestDB {
	Try {
		$MBXDbs = Get-MailboxDatabase | ? {$_.Identity -like $DBFilter } 
		$MBXDBCount = $PSSessions.Count
	Catch {
		$MBXDBCount =  0
	If (!$MBXDbs) {ExitScript "find databases with a name that matches a filter of [$DBFilter]." $False}

	# Loop through each of the MBXDbs
	ForEach ($MBXDB in $MBXDbs)	{
		# Get current mailboxes sizes by summing the size of all mailboxes and "Deleted Items" in the database
		$TotalItemSize = Get-MailboxStatistics -Database $MBXDB | %{$_.TotalItemSize.Value.ToMB()} | Measure-Object -sum
		$TotalDeletedItemSize = Get-MailboxStatistics -Database $MBXDB.DistinguishedName | %{$_.TotalDeletedItemSize.Value.ToMB()} | Measure-Object -sum
#XXX		$TotalDeletedItemSize = Get-MailboxStatistics -Database $MBXDB| %{$_.TotalDeletedItemSize.Value.ToMB()} | Measure-Object -sum
		#Get-MailboxDatabase and the DatabaseSize property was not used since it does not return a value in some environments  as of 2010 SP2
		$TotalDBSize = $TotalItemSize.Sum + $TotalDeletedItemSize.Sum
		# Compare the sizes to find the smallest DB
		If (($TotalDBSize -lt $SmallestDBsize) -or ($SmallestDBsize -eq $null)) {
			$SmallestDBsize = $DBsize
			$SmallestDB = $MBXDB }}
	return $SmallestDB }

Function ExitScript ($ErrorText,$ShowFullError) {
	Write-Host "`nAn error occurred when trying to $ErrorText, exiting script`r`n"  -ForegroundColor Red
	If ($ShowFullError) {
		Write-Host "Error: " $error[0] -ForegroundColor Red
	Break }

Function CopyUser ($SourceUser,$NewUser,$GivenName,$Surname,$DisplayName) {

	Import-module ActiveDirectory

	If ($DisplayName -eq "") {
		If ($GivenName -eq "" -And $SurName -eq "") {
			$DisplayName = $NewUserID
		Else {
			$DisplayName = "$GivenName $SurName" # Set DisplayName if it's blank
		$DisplayName = $DisplayName.Trim() # Removes the leading & trailing spaces

	Write-Host "New user account will be based on user: [$SourceUser]" -ForegroundColor Green
	Try {$UserTemp = Get-User $SourceUser -ErrorAction SilentlyContinue}
	Catch {ExitScript "run [Get-User $SourceUser], confirm user ID exist" $False}

	$UserTemp = $Null
	$UserTemp = Get-User $NewUser -ErrorAction SilentlyContinue
	If ($UserTemp -and !$ContiuneOnExist) {
		Write-Host "User ID $NewUser already exist, script exiting." -ForegroundColor Red
	ElseIf ($UserTemp -and $ContiuneOnExist) {

	$SecurePassword = convertto-securestring $Password -asplaintext -force

	Try {$objSourceUser = Get-ADUser -Identity $SourceUser -Properties $CopyAttributes -ErrorAction Stop}
	Catch {ExitScript "run [Get-ADUser $SourceUser], confirm user ID exist" $False}

	$OUPath = $objSourceUser.DistinguishedName.Replace("CN=$($objSourceUser.Name),","")

	Write-Host "`tCreating user -> DisplayName: [$DisplayName] GivenName: [$GivenName] SurName: [$SurName] `n`tCreating in OU: [$OUPath]" -ForegroundColor Cyan

	New-ADUser -Instance $objSourceUser -UserPrincipalName "$NewUser$UPNSufix" -SAMAccountName $NewUser -Name $DisplayName -DisplayName $DisplayName -AccountPassword $SecurePassword -GivenName $GivenName  -Surname $Surname -Path $OUPath
	Set-ADUser -Identity $NewUser -Enabled $True # I've seen the accounts randomly not being enabled without this
	# -Instance required Windows 2008 R2 DCs:
	Sleep 1 # Delay to prevent object not found error with below command
	If ($objSourceUser.memberOf) {
		Get-ADUser -Identity $NewUser | Add-ADPrincipalGroupMembership -MemberOf $objSourceUser.memberOf

If ($SourceUserID){
	CopyUser $SourceUserID $NewUserID $GivenName $Surname $DisplayName }
Else {
	Write-Host "SourceUserID not specified, script will assume user [$NewUserID] already exist and will Mailbox enabled it only." -ForegroundColor Yellow

Try {$UserTemp = Get-User $NewUserID -ErrorAction Stop}
Catch {ExitScript "run [Get-User $NewUserID], confirm user ID exist" $False}

Write-Host "`nGetting smallest Exchange DB that matches filter [$DBFilter]..." -ForegroundColor Green -NoNewLine
$TargetDB = Get-SmallestDB
Write-Host " Found, [$TargetDB] will be used." -ForegroundColor Green 

Write-Host "`nCreating mailbox for [$NewUserID] on [$TargetDB]" -ForegroundColor Cyan
Try {Enable-Mailbox $NewUserID -Database $TargetDB -ErrorAction Stop}
Catch {
	If ($_.Exception.Message -like '*is of type UserMailbox*') {
		Write-Host `t "[$NewUserID] already has a mailbox, continuing to next step " -ForegroundColor Yellow}
	Else {ExitScript "run [Enable-Mailbox $NewUserID -Database $TargetDB]" $True}}

Write-Host "Enabling retention policy and single item recovery" -ForegroundColor Cyan
Try {Set-Mailbox  $NewUserID  -RetentionPolicy $RetentionPolicy -SingleItemRecoveryEnabled $true  -CustomAttribute10 $CustomAttrib -ErrorAction Stop}
Catch {ExitScript "run [Set-Mailbox  $NewUserID  -RetentionPolicy $RetentionPolicy -SingleItemRecoveryEnabled $true]" $True}

If ($AddToGroup) {
	Try {Add-DistributionGroupMember -Identity $Group -Member $NewUserID -BypassSecurityGroupManagerCheck -ErrorAction Stop}
	Catch {
		If ($_.Exception.Message -like '*already a member of the group*') {
			Write-Host `t "Object [$NewUserID] is already in [$Group]" }
		Else {ExitScript "run [Add-DistributionGroupMember -Identity $Group -Member $NewUserID]" $True}}

If ($SendWelcome) {
	$objNewUser = Get-ADUser $NewUserID -Properties "EmailAddress"
	$EmailAddress = $objNewUser.EmailAddress
	If ($EmailAddress -eq $Null) {
		Write-Host "New user [$NewUserID] not found, waiting 5 seconds and retying..."
		Start-Sleep -s 5
		$objNewUser = Get-ADUser $NewUserID -Properties "EmailAddress"
		$EmailAddress = $objNewUser.EmailAddress
		If ($EmailAddress -eq $Null) {
			Write-Host "New user still not found, Welcome e-mail not sent. Exiting."
	Write-Host "`nSending Welcome message to [$EmailAddress], waiting 10 seconds for mailbox creation process to finish" -ForegroundColor Cyan
	start-sleep 10 # This delay is sometimes needed

	# See for Send-Mail HTML examples
	Try {Send-MailMessage -From $From -To $EmailAddress -Subject $MsgSubject -Body $MsgBody -SmtpServer $SMTPServer -BodyAsHtml}
	Catch {ExitScript "run [Send-MailMessage -From $From -To $EmailAddress -SmtpServer $SMTPServer]" $True}

About Jason Sherry

I am a ~30 year Exchange consultant and expert. I currently work for Commvault as a Solutions Specialist for Microsoft Infrastructure For more info see my resume at:
This entry was posted in Exchange, Script, Technical and tagged , , . Bookmark the permalink.

10 Responses to Script: Create-Mailbox.ps1 – Copy an existing user and create a mailbox

  1. Pingback: Script: Set-MailboxPolicies.ps1 | Jason (Izzy) Sherry's Blog

  2. Adam Bond says:

    Looks promising, but I’m getting this for line 125:
    The Try statement is missing its Catch or Finally block.


  3. jasonsherry says:

    Just ran multiple test against it, found one issue with $Null detection when an account is not being copied. Ran fine otherwise and I’ve updated the code above with a fix for the $null issue.

    Note: This script has only been tested against Exchange 2010, might work with 2013 but won’t with 2007 due to PowerShell 2.0 usage.


  4. Adam Bond says:

    I still get the same thing, only now it is on line 128 instead. Exchange 2010 here btw.

    I’ll keep banging on it and see if I can get it going.


  5. jasonsherry says:

    I found a few hidden\odd\HTML characters in the script on the blog. I’ve updated it, but I suggest people download the PS1 file directly from my servers to prevent this issue. I’ve seen WordPress mess up code a few times now.


  6. Cid says:

    In addition to, can one add permissions to others mailbox within that script.


  7. Pingback: Script: Set-UPN-O365.ps1 – Sets UPNs on-premises and in Office 365 | Jason (Izzy) Sherry's Blog

Leave a Reply to jasonsherry Cancel reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s