Monday, September 23, 2013

Exchange 2010 Migration Script

Hi!

I have been off the radar recently, knee deep in an Exchange 2007 to 2010 upgrade project.  It is something I have worked with a huge amount in the past, but I have really enjoyed getting to know how Exchange works and designing a cool system for my employers.

Exchange 2010 is such a great step forward.  We have moved all of the storage off the SAN and gone for 3 mail servers with DAS and JBOD.  So far it is working pretty well.

Anywho, we needed to migrate around 2000 mailboxes from 2007 to 2010.  In our old 2007 environment storage groups were created based on a users first intial of their first name e.g.  Storage Group S would hold me Sam Owens.  This was fine, but clearly there are lots of people whose name who begin with J and S.  You guessed it, the storage groups were massively diverse and difficult to keep on top of.

Also naming the storage groups in this way leads you to make assumptions.  I saw a number of users who were created and stored in the wrong group.  If you were troubleshooting an issue, this assumption would lead you down the wrong path and probably waste your time.

So, as part of the migration I wanted to level the databases as much as possible.  Now there are a few ways to do this.  First you could do it on amount of users, mailbox item count, or size.

I decided to go for the latter of these options and try to level the new DBs in 2010 by size.  To achieve this I created a custom powershell script.

What does it do?

It will load a list of usernames from a txt file.  For each user in this list, it checks all of the mailbox databases and sort them from smallest to largest.

It then submits a move-request for the smallest DB for the current user.  The try, catch, finally block will email the admin if there is an exception (users doesn't exist, permissions error etc.)

A do loop which starts which checks the move status, if an exception or error is found, the next mailbox move will be attempted.  If the move is under 50% complete it will sleep for 5 seconds and check again.

At the end it will email the administrator to let them know the last mailboxes are migrating.

I am sure that a powershell guru could clean this up, but it worked well for us.

Just change the $PSEmailServer to your SMTP relay address and the email addresses you want informed of progress.

$PSEmailServer = "email.contoso.com"

$users = get-content "c:\temp\userlist.txt"

foreach ($item in $users) 
{
$MailDBs = @(get-MailboxDatabase -status | sort DatabaseSize | Select Name, DatabaseSize)

$goahead = 0
$exception = 0

Try
 {
New-MoveRequest -identity $item -TargetDatabase $MailDBs[0].Name -ErrorAction Stop
 }
Catch
 {
$exceptionmsg = $Error[0].Exception 
Send-MailMessage -From "[email protected]" -To "[email protected]" -Subject "Mailbox Migration" -Body "The mailbox $item failed due to error $Exceptionmsg"
 $exception = 1
 }
Finally
 {
 }

do{
$goahead = 0

$percent = get-MoveRequestStatistics -identity $item
$Status = get-MoveRequest -identity $item | select status 

if($exception -eq 1) {$goahead = 1}

if($status.status -eq "Failed") {$goahead = 1
Send-MailMessage -From "[email protected]" -To "[email protected]" -Subject "Mailbox Migration" -Body "The mailbox $item has failed to migrate"
}
if($percent.percentcomplete -gt 50){$goahead = 1}

Else{
cls
Write-Host $item mailbox move is at $percent.percentcomplete %
sleep 5}

}
Until($goahead -eq 1)

Write-Host $item mailbox move has failed or is over 50% and next task will complete
}

Send-MailMessage -From "[email protected]" -To "[email protected]" -Subject "Mailbox Migration" -Body "The script on has finished, all mailboxes have migrated or are migrating"