Office 365 Hybrid Mailbox Migration Management

2014/10/02 Update: New Version 1.1 has been released - fixed a bug that always causes moves to be suspended.

If you are going to use the Large Item Limit switch, please use it cautiously and ensure your users have the large items backed up.

I wanted to share a small personal project I've been working on with you.

I was finding it cumbersome and painful to keep track of my migrations into the cloud at my customers so I decided to build something quick and easy that can be executed from anywhere.

The aim of this was to allow me to:

  1. Migrate batches of On-premise mailboxes to Exchange online in a Hybrid deployment by using a centralized location.
  2. Get some idea of how long the moves will take,
  3. Keep track of the mailbox moves,
  4. License the mailboxes,
  5. Suspend moves if required and resume them when you need to.
  6. Get an overview of all the license assignments in the tenant.

The outcome was a simple spreadsheet that uses VBA to call PowerShell code depending on the functionality you want.

It’s relatively straightforward, the Excel file contains the following main sheets:

Mailboxes – this sheet contains the main mailbox data that the other areas use to build csv files for the PowerShell scripts.

Planned_Batch – this sheet is where most of the functionality is. In here you can Activate Licenses, Export Scripts for manual execution, initiate the mailbox migrations, show current moves, resume any suspended moves or just open a remote PowerShell session to Office 365.

The following buttons exist on this sheet:

  • Refresh: Updates list based on Move_Variable in Mailboxes sheet
  • Activate Licenses: Assigns a license to the users in this list.
  • Export Scripts: Exports scripts for manual running.
  • Migrate Mailboxes: Kicks off migrations for listed mailboxes.
  • Show InProgress Moves: List current In Progress Moves
  • Resume Suspended Moves: Resume move requests that have been previously suspended.

You will need to populate all the areas marked with yellow with your relevant tenant details:

  • Megabyte per minute during testing – What is your average MB/min that you achieved during the testing\pilot phase? You can get this value from the Move_Request_Statistics sheet – you obviously will need to have moved some mailboxes with data to get a more accurate value here.
  • Hybrid Namespace – your on-premise hybrid namespace ex.
  • Target Delivery Domain – the target delivery domain ex.
  • License – The license you want to assign to the mailboxes.
  • Tenant name – Name of your tenant.
  • Bad Item Limit – the BadItemLimit you want to use during migrations.
  • Large Item Limit - the Large Item Limit for the move request. WARNING: This will result in items being skipped that are larger than the threshold allowed.
  • UsageLocation – the short name for your usage location for licenses. Ex. South Africa is ZA.

There are two option buttons:

  • Suspend moves on completion – this will suspend the move request once the move is ready to be finalized.
  • Finalize moves on completion – Mailboxes will finalize immediately once they are in final stages of the migration.

Moved – Once your planned batch has been successfully migrated you mark the batch completed by changing the Move_Variable to “Moved”. This will allow the moved sheet to be updated with the relevant mailboxes that have been migrated to help you keep track of your migrations.

Move_Request_Statistics – This sheet allows you to track the data on the all your move requests. When you click on the Get-Statistics button it opens a remote PowerShell session to get some data from your current move request and export that to a csv file to the location you specified. You can then import this file and use the Average MB/min to help with the timing calculation in the Planned_batch sheet.

License Report - The two Licensing sheets allows you to export the raw license data from your tenant and import the data into the file. You can then use this report to reconcile your licenses and get an overview of the license situation.


Ok, so that covers the sheets and their functionality. So how would I start with this:

First download and install the Windows Azure Active Directory Module for Windows PowerShell:

When you open the Excel spreadsheet you will first need to populate the mailboxes sheet by running the following within the Exchange 2010/2013 management shell:

$mbx=Get-Mailbox -resultsize unlimited; $mbx | foreach-object {$UPN = $_.UserPrincipalName; $EmailAddress = $_.PrimarySmtpAddress;$OU = $_.OrganizationalUnit; $Type = $_.RecipientTypeDetails; $_ | Get-MailboxStatistics | select @{Name="UPN";expression={$UPN}},@{Name="EmailAddress";expression={$EmailAddress}},@{Name="Type";expression={$Type}},@{Name="OU";expression={$OU}},DisplayName,@{Name="TotalItemSize(MB)";expression={$_.TotalItemSize.Value.ToMB()}},LastLogonTime}|Export-csv .\Mailboxes_Output.csv –notype

The above will export the required data to a CSV file which you can then import into the Mailboxes sheet by using the button provided.

When you want to target a couple of mailboxes for migration you simply mark the Move_Variable column in the mailboxes sheet with an “X”.  Refreshing the Planned_Batch sheet will populate the targeted mailboxes for migration.

I didn’t want to build an elaborate system that uses a database and a front end, because I wanted something quick and easy to use and for me this works. I’m sure there are some brilliant minds out there that will definitely have something to add so feel free to add wherever you want and share. The code is open and available to modify as you see fit.

One more thing, this spreadsheet is in no way an Official Microsoft Office 365 Tool and not maintained by Microsoft, it’s essentially a Michael maintained tool developed to make my life easier during mailbox migrations πŸ™‚

Download the tool here.

If you have any questions or want to give some feedback feel free to e-mail me here - or ask in the comment section below.

Happy mailbox migations! 

Michael Hall

Comments (33)
  1. Ed (DareDevil57) says:

    thanks for sharing.

  2. Hey Gavin,

    I added the large item limit functionality, new version is 1.0. Grab it while its still hot.


  3. Anonymous says:

    Nice tool

  4. jeremy says:

    Do you have to wait till the user has completed migration until you can assign them a license? Or with this tool can you assign them the license as a they finish migrating?

  5. Jeremy,

    The tool will assign a license automatically depending on the licenses you select.


  6. Chris Lehr says:

    BRAVO.  Do you take donations in beer, paypal, or interesting souvenirs?

  7. Phil Howell says:

    This is absolutely brilliant.

  8. Chris Lehr says:


    Recommendation for the mailbox move script after using it a while..   If there is not a matching proxy address, it would be awesome if it prompted you to go ahead and add one.  Now granted, that would require the local change to occur as well as a dirsync, so I understand why it might not be plausible…  but it would be awesome if it did.

    Thanks again for this tool.

  9. Gavin - NZ says:

    Michael, I think I love you πŸ™‚

  10. Gavin - NZ says:

    Don’t get me wrong Michael, still love you buddy, but can this tool in some way add a ‘largeitemlimit’ to each mailbox move command as well as the baditemlimit? I don’t mind adding it in myself, but I’m a little unsure of the VB structure behind the scenes πŸ™‚

    Aside from that, I’d like to report that I’ve managed my first batch using your tool successfully (apart from the users with large items of course). It’s a very nice piece of kit. This might seem like a security threat, but is there any way we could embed the tenancy and on premises credentials somewhere. Just for temporary use?

    Yours very gratefully – Gavin

  11. Jose says:

    Sure could use large item limit options, unless I missed it.

    Looks awesome though, going to test a batch in a few minutes

  12. Gavin NZ says:

    I’m digging around in the VB Macro that does the mailbox migrations, trying to remember how to form a command call with references to variables so I can add -LargeItemLimit into it. Can anyone help?

    Michael has the variable “BadItemLimit” set at the top of his ‘Migrate_Mailboxes’ macro:

    BadItemLimit = wsPlanned.Range(“L18”).Value

    It gets it’s value from the cell on the Planned Batch sheet L18. I’ve added another variable into the declarations, anjd I’ve prepared a call for this config on the spreadsheet. I’m using L24:

    LargeItemLimit = wsPlanned.Range(“L24”).Value

    I now need to alter the Call Shell Line in the macro to include the -LargeItemLimit switch and input the number from the variable. Sound easy right? πŸ˜‰

    There is a bit of ‘If Then’ logic to choose the right command, dependent on what O/S you are using, and whether or not you’ve selected ‘suspendmoves’ but in short, this is the command. I’m focusing on the one tailored for ‘suspendmoves’:

    Call Shell(“C:WindowsSystem32WindowsPowerShellv1.0powershell.exe -ExecutionPolicy Bypass -noexit -file ” & FileName & ” -exportpath ” & ExportPath & ” -SuspendMovesWhenReady 1 -baditemlimit ” & BadItemLimit, vbMaximizedFocus)


  13. Chris Lehr says:

    Another tab I would love.. domain add/verify. Usually not a problem, but have recently had 150+ domains project and I made an XLS to build all the cmdlets. Im trying to reverse engineer the XLS marco’s/script building you did here, but might be easier
    for you to add it πŸ™‚

  14. Anonymous says:

    UPDATE: New version 0.5 has been released with a fix for the module import issue that some people have

  15. Anonymous says:

    If you haven’t seen my mailbox migration tool yet, go and have a look at it here . It’s been

  16. Gavin says:

    Michael, you’re a gent and a scholar. Right in time for my next department’s migration too πŸ™‚
    Thanks heaps πŸ™‚

  17. Jaz says:

    This is fantastic!…bless you πŸ™‚

  18. Itzik says:

    Does it works with Exchange 2007?

  19. turbomcp says:

    Just amazing

  20. Doctorx says:

    Many Thanks Bro! great job!

  21. Asharaf says:

    Looks good but the Moved TAB is not automatically updating , Could you please help on this.

  22. MikeW says:

    Unfortunately you have an incomplete listing of license type SKU’s….

  23. Jason Bollhofer says:

    Thanks for sharing! I’m trying to select "Suspend Moves on Completion" radio button but it won’t let me, any ideas?

  24. Huss says:

    Thanks Michel for the great tool! this saves us a lot of time, I just only miss a license SKU EXCHANGEARCHIVE_ADDON

    Can you please update the tool?
    Kind Regards

  25. WingY says:

    I ran the powershell command from the EMS but where does it save the csv output. Could not find it.

  26. WingY says:

    Scratch that. I found it

  27. Michael,
    still doing development on this? What about adding UTF-8 support for non ASCII regions?
    When add -Encoding UFT8 to the csv export command, the import results in weird characters. This is due to the Excel import TextFilePlatform being set to 437, but UTF8 requires 65001.

  28. anonymouscommenter says:

    Office 365 Hybrid Mailbox Migration Management

  29. Hey Thomas, Thanks for that feedback. I’ll fix that in a new version when I have some free time.

  30. David Dixon says:

    My love for you is boundless Michael.

  31. Andy says:

    A really neat script here – thanks.
    I ran version 1.2 it against 9 mailboxes with the simulataneous migration set to 5 and only 7 mail mailbox move requests were generated in output. Any ideas why none of these numbers match up?
    Also if you have 150 mailboxes and you do 5 at a time, how does it know to kick the next 5 off once one has completed?

  32. Henry J says:

    Thanks for this great tool. Very nice. Except I would like to make changes to the buttons and functions / code that executes. For example the url to connect to MS online. Looking under developer code, I see the code but no reference for any urls. What am I missing? Thanks for shedding light on that.

    1. There’s a tab called scripts that’s hidden, just unhide it and you should be good to go.

Comments are closed.

Skip to main content