Thursday, September 06, 2012

Move Mailboxes and Notify Users

I wrote this script for an Exchange 2003 to Exchange 2010 migration. The administrator wanted a way to communicate with users when their mailbox move was complete, as well as give him a way to monitor the progress of mailbox moves without sitting in front of the computer all night.

This script was tested in an Exchange 2010 SP2 environment where anonymous relay from specific hosts was allowed. 

Purpose

The script accomplishes the following:
-        Create move requests for users specified in the $MailboxestoMove variable at the top
-        Moves each mailbox and monitors status every 30 seconds
-        Will output “Moving Users…” to the screen  every 30 seconds while moves are in process
-        As a move completes with a “complete” status, an email is sent to the user – subject and body defined by variables at the top ($MessageSubject and $MessageBody). Then, the completed move request is cleared
-        As a move request completes with a “CompletedWithWarning” status, an email is sent to the user – the subject and body are defined by variables at the top ($MessageSubject and $MessageBody). Next, an email is sent to the administrator – the subject and body are defined by variables at the top($MessageSubjectWarning and $MessageBodyWarning) – which includes the move history report so you can investigate the warning. Then, the completed move request is cleared
-        If a move request completes with a “Failed” status, then an email is sent to the administrator – the subject and body are defined by variables at the top ($MessageSubjectFailure and $MessageBodyFailure) – which includes the move history report so you can investigate the warning. Then, the failed move request is cleared.   NOTE: an email is NOT sent to the user
-      When all move requests have completed and cleared, an email is sent to the administrator – the subject and body are defined by variables at the top($MessagesSubjectComplete and $MessageBodyComplete). Once this email has been sent, the script ends.

Usability

To use the script, editing the values of the following variables is required:
-        $MailboxestoMove
-        $TargetDB
-        $SMTPServer
-        $SMTPFrom
Optionally, edit the message subject and body variables.

To run the script:
-        Open an elevated Exchange PowerShell window
-        Save the text file as a .PS1 file type rather than TXT
-        Navigate to the place the script is saved
-        Type .\MoveUsersandNotifyv2.7-Final.PS1
-        Script will begin running.

Feature Requests for a Future Version

There are a few things that I think could be improved in order to make this a really great tool.
  • Turn the script into a CMDlet. Doing that would involve wrapping the script in a function, parameterizing the 12 input variables, saving the script as a PSM1, and putting it in the /modules subfolder of your Exchange installation. If anyone does this before I get around to it, please post the updated script.
  • The script does not handle move requests that are suspended or automatically suspended. It completely ignores them. Additional code would need to be written to add script blocks to handle these two move request status types
  • The script assumes that there are no existing move requests when it begins running. If you run the script while there are existing requests, the script will get stuck in an infinite loop because it doesn't know of the existing request.

The Script

#Copyright 2012
#Author: Robin Lehr
#Last Modified Date 09/06/12
#Names of the users you would like to move, enclosed in double quotes and 

#separated by commas. Type these in the format of the user's email address

$MailboxestoMove="test@contoso.com", "test2@contoso.com"

#Type the name of the database you would like to move the users to
$TargetDB = "TestDB2"

#Set variables for sending email
$SMTPServer="smtp.contoso.com"
$SMTPFrom="administrator@contoso.com"
$MessageSubject="Your Mailbox Move is Complete!"
$MessageSubjectFailure="FAILURE: There are one or more failed move requests"
$MessageSubjectWarning="WARNING: There are one or more move requests that completed with warnings"
$MessageSubjectComplete="All Move Requests Have Been Processed" 
$MessageBody="$User - Thank you for your patience, your mailbox move request is complete"
$MessageBodyFailure="The Move request for $User has failed. The Move Request Report says: $StatusReport"
$MessageBodyWarning="The Move request for $User has completed with warnings. The Move Request Report says: $StatusReport"
$MessageBodyComplete="All Move Requests with status other than suspended are complete." 
#End of Variables

$UserstoMove=New-Object System.Collections.ArrayList
ForEach($Mailbox in $MailboxestoMove)
{
    $UserstoMove.Add($Mailbox)
}

#Create Array to remove users from loop processing
$removeUsers=New-Object System.Collections.ArrayList
ForEach ($User in $UserstoMove)
 {
     New-MoveRequest -Identity $User -TargetDatabase $TargetDB -WarningAction "SilentlyContinue"
 }
$Moves=Get-MoveRequest | Where {$_.Status -ne "AutoSuspended" -and $_.Status -ne "Suspended"}
$requests=($Moves|group-object status).count
Do 
 {
 Start-Sleep -seconds 30
 ForEach ($User in $UserstoMove) 
   {
    #Define Variable that will determine the move request status
    $MoveStatus=Get-MoveRequest -Identity $User
    if($MoveStatus.Status -eq "Completed") 
   {
          $SMTPTo=$User
          $SMTP=New-Object Net.Mail.SmtpClient($SMTPServer)
          $smtp.Send($SmtpFrom,$SmtpTo,$messageSubject,$MessageBody)
          Remove-MoveRequest $user -Confirm:$false
          $RemoveUsers.Add($User)
   }
        ElseIf($MoveStatus.Status -eq "Failed") 
   {
       #This Command will send a report for failed move requests
        $StatusReport=Get-MailboxStatistics -Identity $User -IncludeMoveReport

#In this case the email should go to the administrator, which is assumed to 
#be the same as the SMTP From Address. That is why the $SMTPFrom variable is 
#repeated twice in the send command.

        $SMTP=New-Object Net.Mail.SmtpClient($SMTPServer)
        $smtp.Send($SmtpFrom,$SmtpFrom,$messageSubjectFailure,$MessageBodyFailure)

#This script block will send the move request history report and then remove 
#the request. If you do not wish this behavior to happen, comment out the below line. 

#IMPORTANT: if the "Remove-Moverequest" line below is commented out, you 
# MUST also add the following to the Where-Object conditions of the 
# $Moves=Get-MoveRequest line above:
# -and $_.Status -ne "Failed"
            Remove-MoveRequest $User -Confirm:$false
            $RemoveUsers.Add($User)
        }

#In this case the email should go to the administrator, which is assumed to 
#be the same as the SMTP From Address. That is why the $SMTPFrom variable is 
#repeated twice in the send command.

        ElseIf($MoveStatus.Status -eq "CompletedWithWarning") 
        {
#This Command will send a report for failed move requests 
            $StatusReport=Get-MailboxStatistics -Identity $User -IncludeMoveReport
            $SMTP=New-Object Net.Mail.SmtpClient($SMTPServer)
            $smtp.Send($SmtpFrom,$SmtpFrom,$messageSubjectWarning,$MessageBodyWarning)
            $smtp.Send($SmtpFrom,$SmtpTo,$messageSubject,$MessageBody)
            Remove-MoveRequest $User -Confirm:$false
            $RemoveUsers.Add($User)
        } Else {
        Write-Host "Moving Users..."
        }
    } 


#Remove Users who are complete
    ForEach ($value in $UserstoRemove)
    {
        $UserstoMove.Remove($value)
    }

#Find the number of move requests
    $Moves=Get-MoveRequest | Where {$_.Status -ne "AutoSuspended" -and $_.Status -ne "Suspended"}
    $requests=($Moves|group-object status).count
    } 
    Until($requests -eq 0 -or $requests -eq $null) 
#Send Email to Administrator that moves are complete
$SMTP=New-Object Net.Mail.SmtpClient($SMTPServer)
$smtp.Send($SmtpFrom,$SmtpFrom,$messageSubjectComplete,$MessageBodyComplete)