How to Move Exchange Mailboxes with PowerShell

Faris Malaeb

Read more posts by this author.

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:

  1. 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 
The Administrator mailbox is on the EX01-DB database
The Administrator mailbox is on the EX01-DB database

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 
Healthy Database, it also can be Mounted
Healthy Database, it also can be Mounted

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

Getting Exchange Database status in a Non-DAG environment
Getting Exchange Database status in a Non-DAG environment

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"
Creating a new move request via PowerShell
Creating a new move request via PowerShell

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 
Getting a list of Arbitration Mailboxes
Getting a list of Arbitration Mailboxes

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.

The Progress of the Move Request
The Progress of the Move Request

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.

Move Request Status
Move Request Status

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
Moving DiscoverySearchMailbox
Moving DiscoverySearchMailbox

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.

Get-MoveRequest
Get-MoveRequest

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.

Get a all the properties for a move request
Get a all the properties for a move request

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
Getting mailbox Statistics
Getting mailbox Statistics

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, the Suspend 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. The Message 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-MoveRequestcommand 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.

After Resuming a move request.
After Resuming a move request.

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.

SuspendWhenReadyToComplete is True
SuspendWhenReadyToComplete is 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
Removing a Move Request
Removing a Move Request

Subscribe to Stay in Touch

Never miss out on your favorite ATA posts and our latest announcements!

Looks like you're offline!