Moving mailboxes is a common task amongst Microsoft Exchange administrators. You can move mailboxes a couple of different ways but with PowerShell the New-MoveRequest
cmdlet can be your friend.
Known as a Local Move Request, you can move user, archive, arbitration, discovery, and other types of mailboxes. In this tutorial, you will learn how to start and manage local move requests using Windows Powershell!
Prerequisites
If you’d like to follow along with all examples demoed in this tutorial, be sure you have the following ahead of time:
- An on-premises Exchange 2013/2016 or 2019 running in an Active Directory environment
- Local Move Request should be performed in the same Active Directory forest
- Logged into a domain-joined computer with a domain user having the Mailbox Move and Migration Permission permission
- The Exchange Mailbox Replication Server (MRS) and Exchange Mailbox Replication Proxy services running on a mailbox server.
- An available source and destination mailbox – This tutorial will use a source mailbox database of Ex01-DB and will move a mailbox to a destination mailbox database of Ex02-DB.
- A mailbox is available to move – This tutorial will be moving the Administrator user mailbox.
- If you logged into the server, then use Exchange Management Shell, or you can connect Exchange server from any workstation using Powershell Remoting.
Related: Connect to Exchange Server using Remote PowerShell
Preparing for a User Mailbox Move
Before you actually move the mailbox to another database, you must ensure it’s possible first. There are numerous scenarios where you’ll know the move would fail ahead of time such as:
- The destination database is Mounted and Healthy
- The User Mailbox is not larger than the Database Limits Policy
- Moving a mailbox generates a lot of transaction logs, make sure that there is enough free space on the disk hosting the EDB and the Transaction log.
- The destination database doesn’t have enough room for the mailbox
With a PowerShell console open and authenticated to Exchange:
- Run the
Get-MailboxStatistics
cmdlet pointing it to the source mailbox as shown below. You need to first discover what database the source mailbox is on.
Get-MailboxStatistics Administrator | Select-Object -Property Database,Displayname
2. If the mailbox database is DAG Enabled then you can now check the status of the database using the Get-MailboxDatabaseCopyStatus
cmdlet. This cmdlet, among other things, will provide the status for each database in your organization.
To see if DAG is configured or not, run the following command
Get-DatabaseAvailabilityGroup
As you can see below, the source database of EX01-DB is Healthy as well as the destination database of EX02-DB.
Get-MailboxDatabaseCopyStatus | select name,Status
A Healthy status means that the database is healthy but not accepting client protocol connection requests. A Mounted status means that the database is healthy, mounted, and accepting client requests.
If DAG is not enabled, an alternative command can be used which
Get-MailboxDatabase -Status | Format-Table Name, Server, Mounted
The command will return all the available databases with their status
3. You’ve now confirmed the source and destination database servers are online and ready to move. Finally, check to see if the destination database has enough space for the mailbox.
Creating the Local Move Request for a User Mailbox
Once you’ve confirmed a move is actually possible, it’s time to create the local move request using the New-MoveRequest
cmdlet. The New-MoveRequest
cmdlet requires only one parameter, which is the Identity
, other parameters are optional and will provide you better control on where to move the mailbox to.
The New-MoveRequest
cmdlet supports moving:
- The Primary mailbox only
- The Archive Mailbox only
- Both the Primary and Archive Mailbox
Moving the User Primary and Archive Mailbox
Creating a local move request only requires a single line. To move the tutorial’s source mailbox from source to the destination database, run the following command:
The below example moves the administrator primary mailbox to the EX02-DB database and the archive mailbox to the EX02-DB database. It will also give a name for the batch as “AdminMailbox.” BatchName: Name of a batch. This can be used to filter the result when used with Get-MoveRequest
New-MoveRequest -Identity administrator -TargetDatabase ex02-db -ArchiveTargetDatabase ex02-db -BatchName "AdminMailbox"
If there are multiple users that should be moved, then it’s possible to type the username and then pipeline it to New-MoveRequest, like “User1″,”User2” | New-MoveRequest -TargetDatabase Ex01-DB. No need to set the -Identity parameter as the Identity will be fed through the pipeline
Moving the User Primary Mailbox Only
If you’d rather leave the user’s archive mailbox as-is, you don’t have to move it. Instead, use the-PrimaryOnly
parameter without specifying the -ArchiveTargetDatabase
parameter as shown below.
New-MoveRequest -Identity administrator -TargetDatabase ex02-db -PrimaryOnly -BatchName "AdminMailbox"
You can also use the
-ArchiveOnly
parameter to only move the archive database.
Moving Arbitration and Discovery Search Mailboxes
Most of the mailboxes you’ll find yourself moving will be user mailboxes. But, Exchange Server has other types of mailboxes that you’ll need to move on occasion called service mailboxes. Service mailboxes perform internal tasks and do not get directly assigned to users.
Arbitration Mailboxes
One of the most common service mailboxes is the Arbitration mailbox. Arbitration mailboxes are are used to store information about Exchange organization federation, information about the Exchange migration service, e-discovery, and more.
You can find a list of all arbitration mailboxes using the Get-Mailbox
cmdlet using the -Arbitration
parameter.
Get-Mailbox -Arbitration
Moving arbitration mailboxes are similar to moving user mailboxes with just a minor difference.
Start by finding all of the arbitration mailboxes in a mailbox database. The example below is finding all arbitration mailboxes in the EX01-DB database limiting the output down to just the Name
.
Get-MailboxDatabase -Identity ex01-db | get-mailbox -arbitration | ft -AutoSize Name
Now that you can see all of the arbitration mailboxes in a database, move them with New-MoveRequest
using the same parameters as with a user mailbox.
The command below is moving all arbitration mailboxes inside of the EX01-DB database to the EX02-DB database.
Get-MailboxDatabase -Identity ex01-db | get-mailbox -arbitration | New-MoveRequest -TargetDatabase Ex02-DB -BatchName "All Arbitration Mailboxes"
If all goes well, you should see a successful execution as shown below.
These mailboxes are usually not large, so moving these mailboxes won’t take much time.
Like user mailboxes, you can also see the progress using the Get-MoveRequest
cmdlet.
Discovery Search Mailboxes
Much like arbitration mailboxes, you can move discovery search mailboxes in the same manner but with one minor change to discover discovery search mailboxes.
To find discovery search mailboxes, you must filter the output of Get-Mailbox
and return only mailboxes with a RecipientTypeDetails
property value of DiscoveryMailbox
as shown below.
$DiscoveryMailboxes = Get-MailboxDatabase Ex01-db | Get-Mailbox | where {$_.RecipientTypeDetails -like "DiscoveryMailbox"}
Once you’ve collected all of the discovery mailboxes in a database, you can then initiate a new move request just like with an arbitration or user mailbox.
$DiscoveryMailbox | New-MoveRequest -TargetDatabase Ex02-db
Monitoring Mailbox Moves with Get-MoveRequest
A move request doesn’t happen immediately. If you need to monitor the progress of a move request, you can use the Get-MoveRequest
cmdlet. The Get-MoveRequest
cmdlet displays all of the currently-running move requests.
Running the Get-MoveRequest
command will return all the current move requests regardless of where these requests started from either from the web interface or with PowerShell.
A move request initiated from Powershell won’t appear in the Exchange Management Console (Web Interface). These jobs can be viewed only using the Get-MoveRequest command.
By default, as you can see from the above screenshot, Get-MoveRequest
doesn’t tell you a whole lot. It only displays the mailbox name, status, and what database it’s currently being moved to. Inspect all of the properties it returns by piping the output to either Select-Object
or the Format-List
cmdlet (fl
alias).
Get-MoveRequest | Format-List
You can now see other properties like the source and destination but it’d be nice to see a percentage of where the move is at.
Getting Local Move Statistics
As you’ve seen, the Get-MoveRequest
cmdlet doesn’t offer a whole lot of information. To remedy this, pipe the output of Get-MoveRequest
to Get-MoveRequestStatistics
. The Get-MoveRequestStatistics
cmdlet provides a lot more detail such as progress percentage.
Get-MoveRequest | Get-MoveRequestStatistics
Similar to the output of Get-MoveRequest
, you can also pipe the output of Get-MoveRequestStatistics
to Select-Object
or Format-List
again as shown below.
Get-MoveRequest Administrator | Get-MoveRequestStatistics | Format-List
The Get-MoveRequestStatistics
cmdlet returns a lot of useful information so it’s important you understand what many of the most useful properties mean.
- Displayname: The display name for the targeted account.
- PercentComplete: The progress percentage of the move progress.
- StatusDetail: Details about the current status, can be Completed, InProgress, CreatingInitialSyncCheckpoint.
- Suspend: The move request will be created but will not yet start. It will be in Suspend state, to start the move request by using
Resume-MoveRequest -Identity administrator
. After executing this command, theSuspend
property will switch to$false
and the move request will start. - SuspendWhenReadyToComplete: Will sync up to 95% of the mailbox, the admin has to complete the request by using
Resume-MoveRequest
- BadItemsEncountered: Number of corrupted items encountered during the migration.
- OverallDuration: The total time the migration took to complete
- TotalMailboxSize: Mailbox size in MB
- Message: Detailed information about the move request. For example, when initiating
New-MoveRequest
and setting the-SuspendWhenReadyToComplete
parameter to$true
the move operation will stop at 95% and will require the administrator to resume it. TheMessage
property will show: Informational: The move request for mailbox 60ba0d44-e046-4608-b162-6382bd9d070c is ready to complete and has been automatically suspended because the SuspendWhenReadyToComplete parameter is set to $true.
Resuming and Completing a Move Request
To resume the move request, the Resume-MoveRequest
command is used. It doesn’t accept many parameters, unlike the New-MoveRequest
. The easiest way to use Resume-MoveRequest
is to pipeline it with Get-MoveRequest
. The Get-MoveRequest
will get the move request, and the Resume-MoveRequest
will read the move request and resume it to the end.
Get-MoveRequest administrator | Resume-MoveRequest
There will be no output from this command, But if Get-MoveRequest | Get-MoveRequestStatistics | fl
is executed, you will notice that the Status is InProgress and the SyncStage is FinalIncrementalSync. Wait for a few minutes and run Get-MoveRequest | Get-MoveRequestStatistics
; you will notice that the progress is completed and the PercentComplete is 100.
Delaying Move Requests
Whenever you initiate a move request with New-MoveRequest
for a user’s primary mailbox and complete the move, the owner of that mailbox may encounter this message: “The Microsoft Exchange Administrator has made a change that requires you to quit and restart Outlook“.
To prevent any messages presented to the mailbox owner, you may want to migrate the user’s mailbox, but not completing it until the weekend. This way, when the user comes back into the office, they can open Outlook with no message. This scenario is an example of why you might want to use the -SuspendWhenReadyToComplete
parameter with New-MoveRequest
.
Personally speaking, I never use this command, instead, I inform the user about what I am doing and what they might see.
Using the example from earlier, perhaps you know use the -SuspendWhenReadyToComplete
parameter as shown below.
New-MoveRequest -Identity administrator -TargetDatabase ex01-db -ArchiveTargetDatabase ex01-db -BatchName "AdminMailbox" -SuspendWhenReadyToComplete
The Administrator account will be moved to the EX01-DB database along with the archive mailbox. But this time, the process will suspend at 95%.
Once started, wait a little bit and run the following command:
Get-MoveRequest administrator | Get-MoveRequestStatistics | Select-Object -Property *
You should now see the SuspendWhenReadyToComplete
property is set to True
.
Now when the operation reaches 95%, the StatusDetail
property value will be AutoSuspended
and the SuspendWhenReadyToComplete
value will be False
.
You’ll also see the Message
property mentions the process was placed on hold as SuspendWhenReadyToComplete
was set to True
when you started the move request.
Removing Move Requests
When a move request is running, it will appear active. When the move is complete, the move request should be removed. But what if you changed your mind and you want to cancel the move? In that case, you can use the Remove-MoveRequest
cmdlet.
If, for example, you decide to cancel the move request for the Administrator mailbox initiated earlier, simply pipe the request to Remove-MoveRequest
as shown below.
Get-MoveRequest administrator | Remove-MoveRequest