[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Bulk Mailbox Creation

Posted on 2012-08-24
10
Medium Priority
?
1,237 Views
Last Modified: 2012-09-02
My company has a multi-tenant Exchange server that hosts mail services for many customers. We separate the customers into separate OUs and each has their own mailbox database. Any time we need to create a new user mailbox we run a script that we have designed to gather all the necessary information and then run the New-Mailbox cmdlet to create a linked mailbox. The code is below.

function f {
   param([string]$a)
   [string]$value=(Read-Host "$a")
   return $value
}

[string]$custnum=f("Enter the customer number")
[string]$fname=f("Enter user First Name")
[string]$lname=f("Enter user Last Name")
[string]$fi=$fname.Substring(0,1)
[string]$login=f("Enter domain login (enter uses a default of $fi$lname)")
if ($login -eq $null -or $login -eq "") {
   [string]$uname = "$fi$lname"
   [string]$samacct = "$fi$lname.$custnum.local"
} else {
   [string]$uname = $login
   [string]$samacct = "$login.$custnum.local"
}

Write-Host "You entered the following information, please confirm it"
Write-Host "Customer Number: $custnum"
Write-Host "First Name: $fname"
Write-Host "Last Name: $lname"
Write-Host "Login Name: $uname"
[string]$confirm=(Read-Host "Continue (Y or N)")
if ($confirm -ne "Y" -and $confirm -ne "y") {
   write-host "Aborting process"
   exit
}
write-host "Continuing"
Write-host "Creating mailbox"

trap {
   $script:vdc = "ERROR"
   Continue
}
$servervdc = "ipremisevdc1.$custnum.local"
$serversvr = "ipremisesvr.$custnum.local"
$ldc = $servervdc
$vdc = [System.Net.Dns]::GetHostAddresses($servervdc)
if ($vdc -eq "ERROR") {
   write-host "Cannot find IP for $servervdc, trying $serversvr"
   trap {
      Continue
   }
   $ldc = $serversvr
   $svr = [System.Net.Dns]::GetHostAddresses($serversvr)
   if ($svr -eq "ERROR") {
      write-host "Cannot find IP for a domain controller - stopping"
      exit
   }
}

$mbdatabase = "MB$custnum"

if ($samacct.length -gt 20) {$samacct=$samacct.substring(0,20)}
if ($samacct.length -eq 20) {if ($samacct.substring(19,1) -eq ".") {$samacct=$samacct.substring(0,19)}}
New-Mailbox -Name "$fname $lname" -Alias "$uname.$custnum.local" -OrganizationalUnit "$custnum" -UserPrincipalName "$uname.$custnum.local@ipc.local" -SamAccountName "$samacct" -FirstName "$fname" -Initials "" -LastName "$lname" -LinkedMasterAccount "$custnum\$uname" -LinkedDomainController "$ldc" -Database "$mbdatabase"
Set-Mailbox -Identity "$uname.$custnum.local" -SingleItemRecoveryEnabled:$true
write-host "Setting Offline Address Book"
Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} -resultsize unlimited | Set-Mailbox -OfflineAddressBook "$custnum OAB"
write-host "Setting showInAddressBook value"
Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} -resultsize unlimited | Set-Mailbox -ApplyMandatoryProperties
# write-host "Adding to security group, expect errors for users already in address book"
# Get-Mailbox -Identity "$uname.$custnum.local" | Add-DistributionGroupMember -Identity "$custnum"
Write-host "Sleeping for 15 seconds while mailboxes get created"
Start-Sleep -s 15
write-host "Setting address book for OWA"
Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} -resultsize unlimited | Foreach { $dn = "LDAP://" + $_.distinguishedname; $obj = [ADSI]$dn; $obj.msExchQueryBaseDN = "OU=$custnum,DC=ipc,DC=local";$obj.setinfo()}
cd \users\public\desktop\scripts
exit

Open in new window


Sometimes, though, for customer upgrades or new customer installs, we need to add many users at a time. Running the above script 20+ times can get very tedious. Then I found the below script to create mailboxes in bulk.

Function ReadCSV{
 Param([string]$fileName)
 $users = Import-Csv $fileName
 foreach ($user in $users){
 $ht  = @{
 'givenName'=$user.fn
 'sn'=$user.ln
 'displayName'=$user.dispname
 'alias'=$user.alias
 'samAccountName'=$user.alias
 'userPrincipalName' = $user.upn
 'database' = $user.Database
 'organizationalUnit' = $user.ou
 'name' = ($user.fn + $user.sn)
 }
 Write-Output $ht
 }

}
Function CreateUser{
 Param($userInfo)
 $secureString = ConvertTo-SecureString "Passw0rd" -AsPlainText –Force
 New-Mailbox  -Name $userInfo['name' ]`

 -Alias $userInfo['alias'] -UserPrincipalName $userInfo['userPrincipalName'] `
 -SamAccountName $userInfo['alias'] -Database $userInfo['Database']`
 -FirstName $userInfo['givenName'] -LastName $userInfo['sn'] `
 -OrganizationalUnit $userinfo['organizationalUnit']`
 -DisplayName $userInfo['DISPLAYNAME'] -Password $secureString -ResetPasswordOnNextLogon $true
 
}
 
Function CreateMailbox{
 PROCESS
 {
 CreateUser $_
 }
}
 
 ReadCSV c:\Scripts\UserList_10.csv | CreateMailbox

Open in new window


However, it is clearly not designed to interact with our multi-tenant exchange environment properly. So I've been working on trying to merge the two EMS scripts. Here's what I've come up with:

function f {
   param([string]$a)
   [string]$value=(Read-Host "$a")
   return $value
}

[string]$custnum=f("Enter the customer number")
[string]$path=f("Enter the full path to the CSV file (ex: \\support\mailbox\UserList.csv)")
$mbdatabase = "MB$custnum"

trap {
   $script:vdc = "ERROR"
   Continue
}
$servervdc = "ipremisevdc1.$custnum.local"
$serversvr = "ipremisesvr.$custnum.local"
$ldc = $servervdc
$vdc = [System.Net.Dns]::GetHostAddresses($servervdc)
if ($vdc -eq "ERROR") {
   write-host "Cannot find IP for $servervdc, trying $serversvr"
   trap {
      Continue
   }
   $ldc = $serversvr
   $svr = [System.Net.Dns]::GetHostAddresses($serversvr)
   if ($svr -eq "ERROR") {
      write-host "Cannot find IP for a domain controller - stopping"
      exit
   }
}

Function ReadCSV{
 Param([string]$path)
 $userInfo = Import-Csv $path
 foreach ($user in $userInfo){
 $ht  = @{
 'fname'=$user.fn
 'lname'=$user.ln
 'uname'= ($user.fn.Substring(0,1)+$user.ln)
 'name' = ($user.fn + " " + $user.ln)
 }
 Write-Output $ht
 }

}

Function CreateUser{
 Param($userInfo)
 [string]$samacct = "$userInfo['uname'].$custnum.local"
 if ($samacct.length -gt 20) {$samacct=$samacct.substring(0,20)}
 if ($samacct.length -eq 20) {if ($samacct.substring(19,1) -eq ".") {$samacct=$samacct.substring(0,19)}}
 New-Mailbox  -Name $userInfo['name'] -Alias "$samacct" -OrganizationalUnit "$custnum" `
 -UserPrincipalName "$samacct@ipc.local" -SamAccountName "$samacct" `
 -FirstName "$userInfo['fname']" -Initials "" -LastName "$userInfo['lname']" `
 -LinkedMasterAccount "$custnum\$userInfo['uname']" -LinkedDomainController "$ldc" `
 -Database "$mbdatabase"
 Set-Mailbox -Identity "$samacct" -SingleItemRecoveryEnabled:$true
 Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} `
 -resultsize unlimited | Set-Mailbox -OfflineAddressBook "$custnum OAB"
 Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} `
 -resultsize unlimited | Set-Mailbox -ApplyMandatoryProperties
 Start-Sleep -s 15
 Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} `
 -resultsize unlimited | Foreach { $dn = "LDAP://" + $_.distinguishedname; $obj = [ADSI]$dn; $obj.msExchQueryBaseDN = "OU=$custnum,DC=ipc,DC=local";$obj.setinfo()}

}


ReadCSV $path | CreateUser

cd \users\public\desktop\scripts
exit

Open in new window


The CSV file only has two columns -- first name and last name -- and I've successfully tested the ReadCSV function alone. But when I run the entire script, it asks for the customer number, the the path of the CSV file, and then just quits. I've been testing it in a powershell debug window on the multi-tenant exchange server (with the Exchange Management module added), but I don't see any errors. I can't figure out what is going wrong. I'm not great with powershell, so I wouldn't be surpised if I'm missing something simple. Let me know if you need more information.
0
Comment
Question by:ipremise
  • 5
  • 4
10 Comments
 
LVL 52

Expert Comment

by:Manpreet SIngh Khatra
ID: 38329954
Bulk mailbox-enabling users using Exchange Shell in Exchange 2010
http://exchangepedia.com/2006/12/exchange-server-2007-bulk-mailbox-enabling-users-using-exchange-shell.html

Hope the above article helps

- Rancy
0
 
LVL 1

Author Comment

by:ipremise
ID: 38330028
Thanks, Rancy, but that won't work as our multi-tenant mail server is not able to pull information directly from the customer's AD. Our original script creates a linked mailbox by finding the AD user on the linked domain controller. The scrips on the site you recommended are assuming that the domain of the Exchange server is the same as the user's domain. Our's is not. Our multi-tenant Exchange is on a separate domain with cross-forest trusts with every customer domain. That's why separation by OU is so important.
0
 
LVL 52

Expert Comment

by:Manpreet SIngh Khatra
ID: 38330068
Our original script creates a linked mailbox by finding the AD user on the linked domain controller - Oh ok 'Gotcha'

I have doen soem cross forest migrations and we had to run a $GetSourceCredentials and Traget credentials command ..... not sure if you have that in-corporated or how does you script pulls information from Source Forest ?

- Rancy
0
Problems using Powershell and Active Directory?

Managing Active Directory does not always have to be complicated.  If you are spending more time trying instead of doing, then it's time to look at something else. For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why

 
LVL 1

Author Comment

by:ipremise
ID: 38330153
If you notice in the original script, starting at line 33, the linked domain controller is searched for. The FQDN of the LDC is dependent on the $custnum variable which is previously supplied by the user running the script. The trust is already established with the customer's domain using their domain administrator credentials. Make sense?
0
 
LVL 52

Expert Comment

by:Manpreet SIngh Khatra
ID: 38331678
Make sense? - But is it provided when running the script .... how would my script know which credentials will be used to access data from that DC ?

Are you running this script from Exchange shell ?

- Rancy
0
 
LVL 9

Accepted Solution

by:
chrismerritt earned 1500 total points
ID: 38333137
I think this is more a question of logic than anything else, to "bulk" create users you need to run a foreach loop on a list of users.

So it should be as simple as putting together a function that works fine for one user, and then just expanding that to accommodate a foreach loop.

I would advise against asking for user input too much, that will go against any automation you put in place. I would probably import a CSV file that contains the properties you need to do the user creations and run a foreach loop against that.

Your external domain trusts might complicate things a little though with respects to creating the Linked mailboxes and relying on the connectivity to the remote domains working OK. You will also need to consider error handling, and what will happen if someone fills in something incorrectly, or a user already exists, or fails to create etc.
0
 
LVL 1

Author Comment

by:ipremise
ID: 38337281
Thanks chrismerritt. You are correct in assuming I am looking for help with the logical structure of the script. I've taken your advice and removed the user input parts and simply supplied the necessary info in the script, so that I now have this:
$custnum = "10100127"
$path = "\\support\mailbox\UserList.csv"
$mbdatabase = "MB$custnum"
$ldc = "ipremisevdc1.$custnum.local"

Function ReadCSV{
 Param([string]$path)
 $userInfo = Import-Csv $path
 foreach ($user in $userInfo){
 $ht  = @{
 'fname'=$user.fn
 'lname'=$user.ln
 'uname'= ($user.fn.Substring(0,1)+$user.ln)
 'name' = ($user.fn + " " + $user.ln)
 }
 Write-Output $ht
 }

}

Function CreateUser{
 Param($userInfo)
 [string]$samacct = "$userInfo['uname'].$custnum.local"
 if ($samacct.length -gt 20) {$samacct=$samacct.substring(0,20)}
 if ($samacct.length -eq 20) {if ($samacct.substring(19,1) -eq ".") {$samacct=$samacct.substring(0,19)}}
 New-Mailbox  -Name $userInfo['name'] -Alias "$samacct" -OrganizationalUnit "$custnum" `
 -UserPrincipalName "$samacct@ipc.local" -SamAccountName "$samacct" `
 -FirstName "$userInfo['fname']" -Initials "" -LastName "$userInfo['lname']" `
 -LinkedMasterAccount "$custnum\$userInfo['uname']" -LinkedDomainController "$ldc" `
 -Database "$mbdatabase"
 Set-Mailbox -Identity "$samacct" -SingleItemRecoveryEnabled:$true
 Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} `
 -resultsize unlimited | Set-Mailbox -OfflineAddressBook "$custnum OAB"
 Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} `
 -resultsize unlimited | Set-Mailbox -ApplyMandatoryProperties
 Start-Sleep -s 15
 Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} `
 -resultsize unlimited | Foreach { $dn = "LDAP://" + $_.distinguishedname; $obj = [ADSI]$dn; $obj.msExchQueryBaseDN = "OU=$custnum,DC=ipc,DC=local";$obj.setinfo()}

}

Function CreateMailbox{
 PROCESS
 {
 CreateUser $_
 }
}

ReadCSV $path | CreateMailbox

cd \users\public\desktop\scripts

Open in new window

However, I now get the following error output:
Couldn't find user "10100127\System.Collections.Hashtable['uname']" in the global catalog "ipremisevdc1.10100127.local". Please make sure you typed the name correctly.
At C:\Users\ip-svrsupport\Documents\BulkUserImport.ps1:26 char:2
+   <<<< New-Mailbox  -Name $userInfo['name'] -Alias "$samacct" -OrganizationalUnit "$custnum" `
    + CategoryInfo          : NotSpecified: (:) [], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : 9D654546
 
Set-Mailbox : The operation couldn't be performed because object 'System.Collections.H' couldn't be found on 'cdc1.ipc.local'.
At C:\Users\ip-svrsupport\Documents\BulkUserImport.ps1:31 char:13
+  Set-Mailbox <<<<  -Identity "$samacct" -SingleItemRecoveryEnabled:$true
    + CategoryInfo          : NotSpecified: (0:Int32) [Set-Mailbox], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : 6D008B27,Microsoft.Exchange.Management.RecipientTasks.SetMailbox

Open in new window

It seems as though the script is having trouble piping information from the ReadCSV function into the CreateUser function.
0
 
LVL 52

Expert Comment

by:Manpreet SIngh Khatra
ID: 38338006
ipremisevdc1.10100127.local - is that the GC name mentioned in the script ?

System.Collections.H - whats this ??

- Rancy
0
 
LVL 1

Assisted Solution

by:ipremise
ipremise earned 0 total points
ID: 38342404
I've had to modify the script significantly to get it to work. Basically I removed all functions, made each item in the CSV file an object instead of a value in a hash table, and included the new-mailbox command as part of the foreach loop. Here's the final code:
Write-host "BE SURE TO CHANGE THE CUSTOMER NUMBER BEFORE PROCEDING!!!"
Write-Host "Press any key to continue ..."
cmd /c pause | out-null
$custnum = "10100127"
$mbdatabase = "MB$custnum"
$ldc = "ipremisevdc1.$custnum.local"
$userInfo = Import-Csv \\support\mailbox\UserList.csv

foreach ($user in $userInfo){
    $fname = $user.fn
    $lname = $user.ln
    $uname = ($user.fn.Substring(0,1)+$user.ln)
    $name = ($user.fn + " " + $user.ln)
    $samacct = ($uname+"."+$custnum+".local")
    if ($samacct.length -gt 20) {$samacct=$samacct.substring(0,20)}
    if ($samacct.length -eq 20) {if ($samacct.substring(19,1) -eq ".") {$samacct=$samacct.substring(0,19)}}
    $alias = ($uname+"."+$custnum+".local")
    
    New-Mailbox  -Name $name -Alias "$alias" -OrganizationalUnit "$custnum" `
    -UserPrincipalName "$alias@ipc.local" -SamAccountName "$samacct" `
    -FirstName "$fname" -Initials "" -LastName "$lname" `
    -LinkedMasterAccount "$custnum\$uname" -LinkedDomainController "$ldc" `
    -Database "$mbdatabase"
    Set-Mailbox -Identity "$alias" -SingleItemRecoveryEnabled:$true
    Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} `
    -resultsize unlimited | Set-Mailbox -OfflineAddressBook "$custnum OAB"
    Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} `
    -resultsize unlimited | Set-Mailbox -ApplyMandatoryProperties
    Start-Sleep -s 15
    Get-Mailbox -OrganizationalUnit "$custnum" -Filter {RecipientType -eq "UserMailbox"} `
    -resultsize unlimited | Foreach { $dn = "LDAP://" + $_.distinguishedname; $obj = [ADSI]$dn; $obj.msExchQueryBaseDN = "OU=$custnum,DC=ipc,DC=local";$obj.setinfo()}

    write-host `n
}

cd \users\public\desktop\scripts

Open in new window


For some reason, I can't get it to work if I make the script prompt for the customer number instead of providing it right-out. But at least the warning at the beginning should help prevent mistakes.
0
 
LVL 1

Author Closing Comment

by:ipremise
ID: 38358443
While chrismerritt's solution led me in the right direction, I still had to do a lot of research about powershell commands and had to slog through a lot of debugging of the script.
0

Featured Post

Making Bulk Changes to Active Directory

Watch this video to see how easy it is to make mass changes to Active Directory from an external text file without using complicated scripts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Eseutil Hard Recovery is part of exchange tool and ensures Exchange mailbox data recovery when mailbox gets corrupt due to some problem on Exchange server.
This article will help to fix the below errors for MS Exchange Server 2016 I. Certificate error "name on the security certificate is invalid or does not match the name of the site" II. Out of Office not working III. Make Internal URLs and Externa…
In this video we show how to create a mailbox database in Exchange 2013. We show this process by using the Exchange Admin Center. Log into Exchange Admin Center.: First we need to log into the Exchange Admin Center. Navigate to the Servers >> Data…
This video discusses moving either the default database or any database to a new volume.
Suggested Courses

834 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question