Skip to main content

How To Migrate Mailboxes from Exchange 2010 to Exchange 2016 using PowerShell

The Scenario

Your organisation have decided to migrate from Exchange 2010 to Exchange 2016. The Exchange 2016 server have been installed into your current Exchange Organization. The Mailbox role have been installed on the Exchange 2016 server and you are ready to start moving mailboxes from the Exchange 2010 server to the Exchange 2016 server.

Migrating a Mailbox from Exchange 2010 to Exchange 2016

Using New-MoveRequest

Migrating a single mailbox involves invoking the cmdlet New-MoveRequest from the Exchange Management Shell on the Exchange 2016 server. Make sure that your user account that you have logged into the server with have the Organization Management role.

The common parameters that I use for the New-MoveRequest cmdlet is :

New-MoveRequest -Identity '' -TargetDatabase "DB02" -BadItemLimit 10

The -Identity parameter identifies the mailbox to be migrated. I usually use the e-mail address of the mailbox for the identity since it's unique.

The -TargetDatabase parameter value is the name of the database that the mailbox will move to.

The -BadItemLimit parameter specifies the number of corrupt or bad items that are allowed to be left out of the move.

The BadItemLimit parameter is optional. If you don't use the option, then the migration for that mailbox will not succeed if there is any corruption with the mailbox. If you can tolerate leaving a few bad items behind, you can use the option and set the limit to 10 or lower. If the migration still fails because there are more than 10 bad items, it may be worth running a repair on the mailbox rather than increasing the BadItemLimit value. If you end up increasing the BadItemLimit, you will also need to use the -AcceptLargeDataLoss option if the value of the BadItemLimit is 51 or more.

(The cmdlet to repair a mailbox is : New-MailboxRepairRequest.)

After issuing the New-MoveRequest cmdlet, you will see something similar to the below screen shot. The area surrounded by the red marker shows the command that was issued and the output.

To see the status of the move request, we can issue the Get-MoveRequest cmdlet :

Get-MoveRequest -Identity ''

We can see in our example that the migration of the mailbox has completed.

Using New-MigrationBatch

The New-MigrationBatch cmdlet is available for Exchange 2013, Exchange 2016 and Exchange Online. Therefore, we need to execute this cmdlet from the Exchange Management Shell on the Exchange 2016 server in our scenario.

The cmdlet allows for batch migrations of multiple mailboxes.

I usually use the New-MigrationBatch with the following parameters :

New-MigrationBatch -Local -Name BatchMove1
-CSVData ([System.IO.File]::ReadAllBytes("C:\migration\BatchMove1.csv")) -TargetDatabases DB02 -BadItemLimit 10 -AutoStart -AutoComplete

(Note: The above is supposed to be typed on one line.)

The -Local parameter lets Exchange know that it's a move to another database within the same Forest.

The -Name parameter value is used to name this particular migration batch. All migration batch names must be unique, unless the migration batch gets deleted. They are not deleted automatically.

The -CSVData parameter specifies the CSV file containing the list of mailboxes to migrate.

The -TargetDatabases parameter specifies the default database to move the mailboxes to.

The -BadItemLimit parameter specifies the number of corrupt or bad items that are allowed to be left out of the move.

The -AutoStart tells Exchange to start the migration automatically and not to have to wait for the Start-MigrationBatch cmdlet to be issued. It usually starts right away.

The -AutoComplete tells Exchange to complete the migration batch once the mailboxes have synced between the source and the target. If this is not specified, the migration batch is not completed until the Complete-MigrationBatch cmdlet is issued. If the migration batch is not completed, and all mailboxes have synced between the source and the target databases in an on-premise environment, the active mailboxes will still be the ones on the source, and any new items will continue to sync to the target databases.

A situation where you would normally not complete a migration batch automatically is if you are migrating to Exchange online because the MX record would still be pointing to the on-premise Exchange server. Once all your mailboxes have synced to Exchange Online and all your users have their Outlook client pointing to Exchange Online for their emails, you can complete the migration batches and then change the MX record for your domain to point to Exchange Online.

Using a CSV File with New-MigrationBatch

The CSV file needs to have a header row. For an on-premise move or migration, the header row must contain the following to represent the comma separated data fields. Each line below the header row represents a different mailbox.


The EmailAddress field is required, and the rest are optional.

In most cases, you would only need the EmailAddress field in the CSV file since the New-MigrationBatch cmdlet has parameters for the TargetDatabases, TargetArchiveDatabases, and BadItemLimit. Specifying these values with the New-MigrationBatch cmdlet means all mailboxes to be moved will use these settings. This is usually going to be the case anyway.

If you have two or more more target databases on the Exchange 2016 server, you can specify more than one value for the TargetDatabases and TargetArchiveDatabases parameters. If more than one value is provided, Exchange will randomly pick which database in the list to move to.

The default setting for the MailboxType to move is Primary and Archive. In most cases, this is what we want the settings to be.

In the example below, I have created a CSV file called BatchMove1.csv (located in the C:\Migration folder) with the following contents :


In the example, the New-MigrationBatch cmdlet is executed with two target databases specified. If there is no error associated with the cmdlet i.e. invalid parameters, then it will display the batch name and it's status.

The batch name will also show in the Exchange Administration Center. In the Exchange Administration Center, click on recipients in the left pane, then click on migration on the top menu :

In this section, any migration batches will be listed here. We can see the status of the migration batch.

Troubleshooting New-MigrationBatch issues

The migration batch name that we have used coincidently is the name of our CSV file. This does not need to be the case. However, it makes troubleshooting easier if we follow some sort of convention.

If a migration batch name is highlighted, we can drill down and click on View details to have a look at the status of each mailbox in the CSV file that was being migrated.

NOTE: If a mailbox in the CSV file was already in another CSV file that was being used in an earlier migration batch, then it will be left out of this batch by Exchange and so it won't show up in the View details section.

Following our example, we can see that the batch had a Completed with errors status. The Failed value is 1, meaning one mailbox failed to move.

If we click on View details, we will see a window showing the mailboxes. In our case, we only have one mailbox.

We can see in the Status column which mailboxes have failed. Highlighting the mailbox presents us with information about the migration of that mailbox on the right pane.

The Error message tells us that there was a previous move request that was made for that mailbox. We didn't see any other migration batches, but we know that the previous move request was done using the New-MoveRequest cmdlet in our earlier example.

Removing Previous Move Requests

Had there been another migration batch with the same mailbox in that batch, we could remove the mailbox from that batch by highlighting that migration batch, clicking on View details, highlight the mailbox, and clicking the Trash or Bin icon.

In our example, we will issue the Remove-MoveRequest cmdlet.

Resuming the Migration Batch

Let's resume our migration batch by going into Exchange Administration Center, highlighting the migration batch that failed, and clicking on the resume button.

Wait some time before checking the status again.

Our migration batch has succeeded.

Verifying The Mailbox Have Moved To Another Database

You can look at the properties of the mailbox to see which database it is now stored on.

In Exchange Administration Center, click on recipients in the left pane, and click on mailboxes from the top menu.

This list all the mailboxes in the Exchange Organization. Double-click on the mailbox you want to verify.

Click on More options

This will display the database the mailbox is stored on.

We have now verified that the mailbox have moved to another database.


In our scenario, the source database lives on an Exchange 2010 server, and the target database lives on the Exchange 2016 server.

We have used two different cmdlets to move a mailbox from one database to another.

The preferred method if you have a lot of mailboxes to migrate is to use the New-MigrationBatch cmdlet. This cmdlet can be incorporated into a PowerShell script and scheduled to run at a particular time.

(See my other post on how to schedule Exchange PowerShell scripts in Task Scheduler  )

We have seen that each mailbox can only have one move request associated with it. The migration batch name must also be unique.

Related Tutorials

How To Install Exchange Server 2016

How To Configure Exchange Server 2016 After Installation for Mail Flow


Popular posts from this blog

How to Schedule an Exchange PowerShell Script in Task Scheduler

Exchange Management Shell Since Exchange 2007, Microsoft has provided the Exchange Management Shell so administrators can manage all aspects of the Exchange server from the command line. The Exchange Management Shell has Exchange specific PowerShell cmdlets. These Exchange cmdlets are not normally available in an ordinary PowerShell command environment. An example of what can be done in the Exchange Management Shell is to run a PowerShell script to list all the mailboxes on the Exchange server to a file. You can output columns based on display name, size of the mailbox, last logon, and other available mailbox attributes. You can also schedule a batch migration of mailboxes from one database to another such as the migration of mailboxes from Exchange 2010 to Exchange 2013. Scheduling the PowerShell Script Once you have written a PowerShell script and utilised the Exchange cmdlets, you can run it with no problems inside the Exchange Management Shell. If you were to try

Elastic Load Balancing in AWS

Elastic Load Balancing is a service which allows for the automatic distribution of incoming traffic across multiple Amazon EC2 instances.These EC2 instances should be in separate availability zones in a particular region.This enables applications to achieve fault tolerance and high availability if they are designed so that they can be accessed from multiple server instances. Sometimes an application may not need to be designed as such if they both point to the same data source. More often than not, these applications can be run from anywhere. They are good candidates to be put behind the Elastic Load Balancing service.It is up to the system or cloud administrator to ensure that identical versions of the application exist across all servers that are going to be load balanced. The Elastic Load Balancing service can be integrated with Auto Scaling in AWS. As more load is put on your application servers, additional EC2 instances can be launched by Auto Scaling.Once the load dissipates. E