Redistribute Mailboxes Across an Exchange 2010 DAG

I have put together a script to redistribute mailboxes across an Exchange 2010 DAG and wanted to share my work with the Exchange community.  Over time some of the databases in an Exchange 2010 DAG  will have an uneven distribution of mailboxes.  The script in this post automates the assignment of mailboxes for redistribution based on a couple of criteria, standard mailbox count, large mailbox count and total mailbox count.  These definition of standard and large mailbox sizes are different in just about every environment.  Therefore, I’m making the assumption that if a mailbox in your environment is using the default database quotas, it is a standard mailbox.  Otherwise, the mailbox is not using the default database quotas and has a different, larger quota that is uniform across the board.  The high level steps are:

  • Gather all DAG databases.
  • Gather mailboxes on each database and categorize as standard or large.
  • Make calculations about each mailbox type per DAG database so that we can get accurate tallies and then evenly redistribute mailboxes.
  • Make new-moverequest assignments and track the changes that will occur on the mailbox count per database
  • Initiate the new-moverequest for mailboxes selected.

This script will require the Exchange 2010 snapin.

Set a variable for the new-moverequest batch name.

Get the databases within the DAG but skip recovery databases. Hopefully this makes the database gather process as generic as possible.

We’ll use $DBCountArray for tracking the mailbox count on each database. $MBXArray will be used to store every mailbox in your organization and leveraged to make each new-moverequest.

In this next code block we are taking each DAG database and then enumerating the mailboxes it contains.  This will take some time to execute so a progress bar is helpful here.  There are two variables used as counters (line 15 and 16) which are set to zero.  In the second foreach loop the script examines the current mailbox and determines if it is standard or large based on it using default database quotas or not.  The MBXObject variable records basic data about the mailbox and puts this data into the MBXArray.  At the end of the first foreach loop the script records each mailbox processed and tallies the count for each database.

We now take the $DBCountArray and pipe the collected counts to measure-object to get an average number for each of the three attributes; totalmailboxes, largemailbox, and standardmailbox.  Secondly use [Math] in order to round the averages to a whole number.

We’ll use all the calculated variables to continue building out what the database counts are within $DBCountArray.

In this block the script matches the current database in the foreach loop with where-object and makes a calculation on each criteria by subtracting the average value from the total value and adding the property to the $DBCountArray.  This lets us know where each database is in relation to the average.  The calculated number will be positive integer, negative integer, or will be zero.  This will be the basis on which database to move mailboxes from and where to move them to.

This section handles new-moverequest assignments for large mailboxes and tracking the database counts for each large mailbox.  The source databases are selected by sorting the totmbxdiff property using the descending switch.  Selecting the first one will always grab the database in the list that is farthest out of skew from the computed average.  I’ve devised a non elegant way to add and subtract each property in the array.  By using the do until code block the script nulls out the source and target during each iteration and will continue until either to source or target databases have reached zero, or in other words, until it runs out of mailboxes to move or a place to move them to.

This next section of code is rinse and repeat for the standard mailboxes.

If you have made it this far then take a peek at what has been generated in the $DBCountArray

Taking a look at the contents of the $MBXArray you will see something similar.  This example list shows standard mailboxes but the mailboxes are not assigned a database for a move request.  The $MBXArray will be used with the new-moverequest cmdlet to direct mailboxes to their new databases.

The final step is to initiate the new-move request.  This is accomplished by taking the $MBXArray and trimming out any mailboxes without the moverequest property populated using where-object.  The $MoveRequest variable contains all the mailboxes that will be moved so simply pipe that variable into a ForEach loop that creates a new move request.

Here is the script in it’s entirety:

PowerShell Try Catch Finally Error Handling

I’ve recently been experimenting with the script blocks in PowerShell try catch finally.  These script blocks can help you deal with terminating errors that may occur in your scripts.  A terminating error, otherwise known as an exception, will stop a command from processing the current object or from processing additional objects.  On the other hand, there are many situations that will result in a non-terminating error.  In a non-terminating error situation an error will generally be displayed on the console and continue processing.  An example of a non-terminating error is passing a variable containing a list of mailboxes to the Get-Mailbox cmdlet.  If an error occurs matching a name in the variable to an Exchange mailbox, the process continues but an error is displayed to the console.  A good resource for determining what exactly constitutes a terminating error or non-terminating error can be found in this MSDN article. This is where the script blocks try catch finally can be helpful. The try block is where you will put the code that you want to execute and monitor for errors.  If an error occurs then the code in the catch block is executed.  It is possible to include multiple catch blocks in order to catch certain types of errors.  The finally block runs no matter what happens during the try and catch script blocks.  The finally block can be used to provide informational output gather from a script or free up resources, per Microsoft’s documentation.  You may even choose to omit the finally block as part of your script if you don’t feel that it is necessary.

So if you are using the try catch finally blocks and have non-terminating errors within the try block, the catch block ignores the errors and does not fire.  It is possible to force any error in the try block by using the -ErrorAction switch.  Setting the error action to stop forces the catch block to see all errors, either terminating or non-terminating.

You’ll notice in the catch block that I’m using the $Error variable. This is where PowerShell stores all the errors for a given session.  Using [0] after the $Error variable denotes accessing the first time in a PowerShell array so we’ll be returning the most recent error in $Error.  Just for the sake of the example, the finally block unloads the Exchange 2010 PowerShell snapin.

Ok so this script is reinventing the wheel a bit but the try catch finally script blocks are extremely useful for handling terminating errors or when you want to trap non-terminating errors in your code.

Count Mailboxes Per Database Faster

Waiting for a script to gather data can be a frustratingly long experience at times.  A common task when using PowerShell cmdlets with Exchange 2010 is to enumerate mailboxes on a database.  If you are counting the number of mailboxes per database using Get-Mailbox, it’s possible to speed things up.  A faster method is to use Get-MailboxStatistics, although the cmdlets return different data, the mailbox count per database is what we want in a speedy manner.  I’ve created two scripts with a timer built into each so that the speed of mailbox enumeration can be effectively illustrated.  We are doing the following:

  • Comparing Get-Mailbox versus Get-MailboxStatistics.
  • Using a timer to illustrate the speed difference of counting mailboxes per database.

The test environment consists of 48 DAG databases with about 400 mailboxes on each, spread across four servers.

Read moreCount Mailboxes Per Database Faster

Exchange 2010 Migration Script Part 2

Do you need an automated process to migrate from a legacy Exchange environment to Exchange 2010?  This article is step two of a two step migration process that involves separate PowerShell scripts to accomplish an Exchange 2010 migration.  This is the second script  that resumes the migration of legacy Exchange mailboxes and distributes them evenly across existing Exchange 2010 databases.  The bulk of this script deals with reporting the results of the migration for later review and analysis.  I’ll break down the script in detail so that you can understand what is happening.   (Part 1 can be found here)

The script  executes the following:

  1. Selects the pending move requests based on a batch name.
  2. Verifies that status of move requests are acceptable and then resumes the migration.
  3. Waits until all resumed migrations are completed or completed with warnings before starting to build a report.
  4. Creates a report about all the mailboxes moved.
  5. Writes report to CSV and sends the report via email.

Read moreExchange 2010 Migration Script Part 2

Exchange 2010 Migration Script Part 1

Do you need an automated process to migrate from a legacy Exchange environment to Exchange 2010?  This article is step one of a two step migration process that involves separate PowerShell scripts to accomplish an Exchange 2010 migration.  The two part migration process will allow you to begin migrations during the work day and then cut over the mailboxes to Exchange 2010 at a convenient time.  This is the first script  that migrates legacy Exchange mailboxes evenly across existing Exchange 2010 databases.  I’ll break down the script in detail so that you can understand what is happening.  The script  executes the following:

  1. Selects a set of mailboxes to migrate based upon membership of a distribution group.
  2. Selects the Exchange 2010 databases that you want to target.
  3. Creates a data table to hold the mailboxes to be migrated.
  4. Creates a data table of target mailbox databases that will be used to select the optimal database target based on various criteria.
  5. Queries the target Exchange 2010 databases to see how the databases are populated and considers various criteria.
  6. Queries the source mailboxes and assigns them to target Exchange 2010 databases for a move request.
  7. Submits new move requests.
  8. Loops until all mailboxes are in an auto suspended state or failed state.

Read moreExchange 2010 Migration Script Part 1