Weekend Scripter: Use PowerShell to Simplify a Large-scale Exchange Migration

Weekend Scripter: Use PowerShell to Simplify a Large-scale Exchange Migration

  • Comments 2
  • Likes


Summary: See how to use Windows PowerShell to simplify a large-scale Microsoft Exchange server migration project.


Microsoft Scripting Guy Ed Wilson here. Today I am turning the keyboard over to Phil Braniff, one of my former Windows PowerShell workshop attendees, as he talks about a large Microsoft Exchange migration.

Take it away, Phil!


I was tasked with the mailbox migrations for the company I work for when we were converting the domestic US mailbox servers from Exchange 2003 to Exchange 2007. I asked our Microsoft support people several times how other customers were managing their mailbox migrations but never got much help. I knew what needed to be accomplished and I knew 'how' to do it . However, it took lots of persistence to finally hit upon a process that met my expectations for the migrations. The previously posted solutions usually involved one line commands taking a block of users and moving them to a single database, some threaded some not. What I was looking for was multithreaded and multitasked to take advantage of the larger number of databases on Exchange 2007 in addition to the multiple processors on the newer hardware configurations.

I needed a way to inventory all the mailboxes that were still on Exchange 2003 (and those on Exchange 2007) so I wrote a script to create flat files with the primary SMTP address for each mailbox on a server. The E2K7-User-Lists.ps1 (Script 1 of 4) script is uploaded to the Script Center Script Repository.

I had already determined that the prior solutions for mailbox migration would result in 2 to 3 months of manual, tedious migration activity. This was based on the initial migration of about 10,000 pilot mailboxes to Exchange 2007. The mailbox migration process, even when multithreaded, is painfully slow and is thread bound (vs. processor, storage, I/O, or network bandwidth bound). Because the migration process moves each 'item' in the mailbox there is no quick way to move a mailbox with several thousands of items. During the pilot migrations I tried running several tasks with migrations and that did speed up the process of moving a block of mailboxes.

I then began looking for how to take a smaller block of users (without exceeding our design limits for the number of mailboxes on each database) and moving them in multiple Exchange Management Shell tasks. By using multiple tasks (say 1 task per database) I could multiply the migration activity and hopefully not crash the server in the process. By reducing the 'thread' count on each task I was able to migrate in the range of 120 to 150 mailboxes concurrently. Too many threads could result in problems if too many mailboxes are moved at the same time because mailboxes are basically locked during the moves (in an E2K3 to E2K7 migration), it is best to move them as quickly as possible.

I hit upon a solution to start a separate Windows PowerShell task using the System.Diagnostics.Process. After several days of testing I was able not only to start a separate Windows PowerShell task, but was able to configure it in a way that Exchange Management Shell commands could be passed with the input file name and optional parameters needed to start a PowerShell task for each mailbox database. Prepare-Move-File.ps1 (Post 3 of 4)

Before each migration I would verify the current locations of the mailboxes I was about to migrate, with migrations to over 40 clustered servers this was just a cross check to make sure I was moving the block of mailboxes I was expecting to move, anyway this tool proved to be useful. List-Move-FileLocations.ps1 (Post 2 of 4)

The actual move process is performed by the script in the last post. It can be used to migrate a list of users to a server but the ideal way is to use the Prepare-Move-File.ps1 script and have it call the SprayMoves.ps1 (Post 4 of 4) script and use multitasked calls of Windows PowerShell and Exchange Management Shell.

That sums it up. I now turn the keyboard back to Ed.


Phil, I want to thank you for taking the time to share your real-world experience with a large-scale Microsoft Exchange migration project with us. In addition, I especially thank you for posting your scripts to the Script Repository. Tomorrow we continue Weekend Scripter with a visit to the past and the highly popular iCame, iPod, iScripted being dug up from the vaults.

I invite you to follow me on Twitter or Facebook. If you have any questions, send email to me at scripter@microsoft.com or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.


Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • I'm curious about using System.Diagnostics.Process.  Was there a problem with either Start-Job {<code>} or Start-process?


    Jeffrey Snover [MSFT]

    Distinguished Engineer

    Visit the Windows PowerShell Team blog at:    blogs.msdn.com/PowerShell

    Visit the Windows PowerShell ScriptCenter at:  www.microsoft.com/.../msh.mspx

  • The code was written before we installed the new release of PowerShell. Admitadly that would have been a more elegant solution.