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.

 Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction "SilentlyContinue"

Set a variable for the new-moverequest batch name.

$BatchName = RebalanceDAG

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

# Get all the DAG databases
$TarDatabases = Get-MailboxDatabase | where {$_.mastertype `
-eq "DatabaseAvailabilityGroup" -and $_.Recovery -eq $False}

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.

# Create empty arrays to store sorting data.  One for Database counts and one `
#  for mailbox types and move request assignments.
$DBCountArray = @()
$MBXArray = @()

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.

# Build the list of databases with associated counts for the various criteria used during sorting.
Foreach ($TarDatabase in $TarDatabases) {
# Show a status bar for progress while data is collected.
$PercentComplete = [Math]::Round(($LoopCount++ / $TarDatabases.Count * 100),1)
$CurrentDB = $TarDatabase.Name
Write-Progress -Activity "Mailbox Database Query in Progress" -PercentComplete $PercentComplete `
-Status "$PercentComplete% Complete" -CurrentOperation "Current Database: $CurrentDB"

# Query the current database in the foreach loop and get every mailbox.
$TarDBInfo = Get-Mailbox -Database $TarDatabase

# Set the initial values to 0 for each database being processed.
$TotalActiveSTDMBs = 0
$TotalActiveLRGMBs = 0
# Loop through each mailbox in the database checking to see if it is a large mailbox or not.
Foreach ($MBXInfo in $TarDBINfo) {
$MBXObject = $null
$MBXObject = New-Object system.Object
$MBXObject | Add-Member -type NoteProperty -Name "Database" -Value $MBXInfo.database.Name
$MBXObject | Add-Member -type NoteProperty -Name "Name" -Value $MBXInfo.Name
$MBXObject | Add-Member -type NoteProperty -Name "PrimarySMTP" -Value $MBXInfo.PrimarySmtpAddress
If ($MBXInfo.UseDatabaseQuotaDefaults -eq $true) {
# It's a standard mailbox size so increment that number.
$TotalActiveSTDMBs++
$MBXObject | Add-Member -type NoteProperty -Name "MBXSize" -Value "Standard"
} Else {
# It's a large mailbox size so increment that number.
$TotalActiveLRGMBs++
$MBXObject | Add-Member -type NoteProperty -Name "MBXSize" -Value "Large"
}
$MBXObject | Add-Member -type NoteProperty -Name "MoveRequest" -Value $Null
$MBXArray += $MBXObject
# Store the collected data in the DBCountArray variable.
$DBObject = $Null
$DBObject = New-Object system.Object
$DBObject | Add-Member -type NoteProperty -Name "Database" -Value $TarDatabase
$DBObject | Add-Member -type NoteProperty -Name "StandardMailbox" -Value $TotalActiveSTDMBs
$DBObject | Add-Member -type NoteProperty -Name "LargeMailbox" -Value $TotalActiveLRGMBs
$DBObject | Add-Member -type NoteProperty -Name "TotalMailboxes" -Value ($TotalActiveSTDMBs + $TotalActiveLRGMBs)
$DBCountArray += $DBObject
}

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.

# Measure the criteria counts to obtain various data. Average will be used to judge
# what each database count should be.
$TotalMBs = $DBCountArray | Measure-Object "TotalMailboxes" -Average
$TotalLMBs = $DBCountArray | Measure-Object "LargeMailbox" -Average
$TotalStdMBs = $DBCountArray | Measure-Object "StandardMailbox" -Average

# Round the averages.
$TotalMBAvg = ([Math]::Round($TotalMBs.average))
$TotalLMBAvg = ([Math]::Round($TotalLMBs.average))
$TotalStdMBAvg = ([Math]::Round($TotalStdMBs.average))

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.

# Using the averages obtained get the variance on the current database counts.
Foreach ($DB in $DBCountArray){
$DBCountArray | where {$_.database.name -eq $DB.database.Name} `
| Add-Member -type NoteProperty -Name "StdMbxDiff" -Value ($DB.Standardmailbox - $TotalStdMBAvg)
$DBCountArray | where {$_.database.name -eq $DB.database.Name} `
| Add-Member -type NoteProperty -Name "LrgMbxDiff" -Value ($DB.Largemailbox - $TotalLMBAvg)
$DBCountArray | where {$_.database.name -eq $DB.database.Name} `
| Add-Member -type NoteProperty -Name "TotMbxDiff" -Value ($DB.TotalMailboxes - $TotalMBAvg)
}

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.

# Loop through and assess the count on the Database count, increment the various criteria and make new move assignments.
Write-Host "Assigning Large Mailboxes" -ForegroundColor Yellow
do {
# initialiaze/null out the variables to prevent contaminating the loop.
$DBsource = $null
$DBtarget = $null
# Get the large mailbox count source and target databases
$DBsource = $DBCountArray | where {$_.lrgmbxdiff -gt 0} | sort totmbxdiff -Descending | select -First 1
$DBtarget = $DBCountArray | where {$_.lrgmbxdiff -lt 0} `
| sort totmbxdiff | select -First 1
# If they aren't null then do the work.
if ($DBsource -ne $null -and $DBtarget -ne $null){
# decrement the source databases
($DBCountArray | where {$_.database.name -eq $DBsource.database}).largemailbox = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).largemailbox -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).totalmailboxes = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).totalmailboxes -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).lrgmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).lrgmbxdiff -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).totmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).totmbxdiff -1
# increment the target databases
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).largemailbox = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).largemailbox +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totalmailboxes = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totalmailboxes +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).lrgmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).lrgmbxdiff +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totmbxdiff +1
# Access the list of mailboxes and make an assigment based on the target identified in $DBtarget
($MBXArray | where {$_.database -eq $DBsource.Database -and $_.mbxsize -eq "large" -and $_.moverequest -eq $null} | select -First 1).moverequest = $DBtarget.Database.Name
}
}
# Keep doing the loop until either variable runs out of databases.
until ($null -eq $DBtarget -or $null -eq $DBsource)

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

# Loop through and assess the count on the Database count, increment the various criteria and make new move assignments.
Write-Host "Assigning Standard Mailboxes" -ForegroundColor Yellow
do {
# initialiaze/null out the variables to prevent contaminating the loop.
$DBsource = $null
$DBtarget = $null
# Get the standard mailbox count source and target databases
$DBsource = $DBCountArray | where {$_.stdmbxdiff -gt 0} | sort totmbxdiff -Descending | select -First 1
$DBtarget = $DBCountArray | where {$_.stdmbxdiff -lt 0} | sort totmbxdiff | select -First 1
# If they aren't null then do the work.
if ($DBsource -ne $null -and $DBtarget -ne $null){
# decrement the source databases
($DBCountArray | where {$_.database.name -eq $DBsource.database}).standardmailbox = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).standardmailbox -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).totalmailboxes = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).totalmailboxes -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).stdmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).stdmbxdiff -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).totmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).totmbxdiff -1
# increment the target databases
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).standardmailbox = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).standardmailbox +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totalmailboxes = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totalmailboxes +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).stdmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).stdmbxdiff +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totmbxdiff +1
# access the list of mailboxes and make an assignment based on the target identified in $DBtarget
($MBXArray | where {$_.database -eq $DBsource.Database -and $_.mbxsize -eq "standard" -and $_.moverequest -eq $null} | select -First 1).moverequest = $DBtarget.Database.Name
}
}
# Keep doing the loop until either variable runs out of databases.
until ($null -eq $DBtarget -or $null -eq $DBsource)

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

$DBCountArray | ft -AutoSize

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.

$MBXArray

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.

# New-Move request to initiate seeding of mailboxes to databases as assigned in $MailboxData table.
$MoveRequest = $MBXArray | where {$_.moverequest -ne $null}
$MoveRequest | ForEach {
$TargetDB = $_.DatabaseName
$ID = $_.PrimarySMTPAddress
New-MoveRequest -identity $ID -TargetDatabase $TargetDB -BadItemLimit 50 -Batchname $BatchName -SuspendWhenReadyToComplete -Confirm:$false
}

Here is the script in it’s entirety:

# Get all the DAG databases
$TarDatabases = Get-MailboxDatabase | where {$_.mastertype -eq "DatabaseAvailabilityGroup" -and $_.Recovery -eq $False}

# Create empty arrays to store sorting data.  One for Database counts and one `
#  for mailbox types and move request assignments.
$DBCountArray = @()
$MBXArray = @()

# Build the list of databases with associated counts for the various criterea `
#  used durning sorting.
Foreach ($TarDatabase in $TarDatabases) {
# Show a status bar for progress while data is collected.
$PercentComplete = [Math]::Round(($LoopCount++ / $TarDatabases.Count * 100),1)
$CurrentDB = $TarDatabase.Name
Write-Progress -Activity "Mailbox Database Query in Progress" -PercentComplete $PercentComplete `
-Status "$PercentComplete% Complete" -CurrentOperation "Current Database: $CurrentDB"

# Define the query which which finds every mailbox in the database.
$TarDBInfo = Get-Mailbox -Database $TarDatabase
# Set the initial values to 0 for each database being processed.
$TotalActiveSTDMBs = 0
$TotalActiveLRGMBs = 0
# Loop through each mailbox in the database checking to see if it is a large mailbox or not
Foreach ($MBXInfo in $TarDBINfo) {
$MBXObject = $null
$MBXObject = New-Object system.Object
$MBXObject | Add-Member -type NoteProperty -Name "Database" -Value $MBXInfo.database.Name
$MBXObject | Add-Member -type NoteProperty -Name "Name" -Value $MBXInfo.Name
$MBXObject | Add-Member -type NoteProperty -Name "PrimarySMTP" -Value $MBXInfo.PrimarySmtpAddress

If ($MBXInfo.UseDatabaseQuotaDefaults -eq $true) {
# It's a standard mailbox size so increment that number.
$TotalActiveSTDMBs++
$MBXObject | Add-Member -type NoteProperty -Name "MBXSize" -Value "Standard"
} Else {
# It's a large mailbox size so increment that number.
$TotalActiveLRGMBs++
$MBXObject | Add-Member -type NoteProperty -Name "MBXSize" -Value "Large"
}
$MBXObject | Add-Member -type NoteProperty -Name "MoveRequest" -Value $Null
$MBXArray += $MBXObject
}
# Store the collected date in the DBCountArray variable.
$DBObject = $Null
$DBObject = New-Object system.Object
$DBObject | Add-Member -type NoteProperty -Name "Database" -Value $TarDatabase
$DBObject | Add-Member -type NoteProperty -Name "StandardMailbox" -Value $TotalActiveSTDMBs
$DBObject | Add-Member -type NoteProperty -Name "LargeMailbox" -Value $TotalActiveLRGMBs
$DBObject | Add-Member -type NoteProperty -Name "TotalMailboxes" -Value ($TotalActiveSTDMBs + $TotalActiveLRGMBs)
$DBCountArray += $DBObject
}

# Measure the critera counts to obtain various data. Average will be used to judge `
#  what each database count should be.
$TotalMBs = $DBCountArray | Measure-Object "TotalMailboxes" -Average -Maximum -Minimum -Sum
$TotalLMBs = $DBCountArray | Measure-Object "LargeMailbox" -Average -Maximum -Minimum -Sum
$TotalStdMBs = $DBCountArray | Measure-Object "StandardMailbox" -Average -Maximum -Minimum -Sum

# Round the averages.
$TotalMBAvg = ([Math]::Round($TotalMBs.average))
$TotalLMBAvg = ([Math]::Round($TotalLMBs.average))
$TotalStdMBAvg = ([Math]::Round($TotalStdMBs.average))

# Using the averages obtained get the variance on the current database counts.
Foreach ($DB in $DBCountArray){
# Blackberry and Large Mailbox
$DBCountArray | where {$_.database.name -eq $DB.database.Name} | Add-Member -type NoteProperty -Name "StdMbxDiff" -Value ($DB.Standardmailbox - $TotalStdMBAvg)
$DBCountArray | where {$_.database.name -eq $DB.database.Name} | Add-Member -type NoteProperty -Name "LrgMbxDiff" -Value ($DB.Largemailbox - $TotalLMBAvg)
$DBCountArray | where {$_.database.name -eq $DB.database.Name} | Add-Member -type NoteProperty -Name "TotMbxDiff" -Value ($DB.TotalMailboxes - $TotalMBAvg)
}

$DBCountArray | ft -AutoSize

# Loop through and assess the count on the Database count, increment the various `
#  criteria and make new move assignments.
Write-Host "Assigning Large Mailboxes" -ForegroundColor Yellow
do {
# initialiaze/null out the variables to prevent contaminating the loop.
$DBsource = $null
$DBtarget = $null
# Get the large mailbox count source and target databases
$DBsource = $DBCountArray | where {$_.lrgmbxdiff -gt 0} | sort totmbxdiff -Descending | select -First 1
$DBtarget = $DBCountArray | where {$_.lrgmbxdiff -lt 0} | sort totmbxdiff | select -First 1
# If they aren't null then do the work.
if ($DBsource -ne $null -and $DBtarget -ne $null){
# decrement the source databases
($DBCountArray | where {$_.database.name -eq $DBsource.database}).largemailbox = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).largemailbox -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).totalmailboxes = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).totalmailboxes -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).lrgmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).lrgmbxdiff -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).totmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).totmbxdiff -1
# increment the target databases
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).largemailbox = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).largemailbox +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totalmailboxes = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totalmailboxes +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).lrgmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).lrgmbxdiff +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totmbxdiff +1
# access the list of mailboxes and make an assigment based on the target identified in $DBtarget
($MBXArray | where {$_.database -eq $DBsource.Database -and $_.mbxsize -eq "large" -and $_.moverequest -eq $null} | select -First 1).moverequest = $DBtarget.Database.Name
}
}
# Keep doing the loop until either variable runs out of databases.
until ($null -eq $DBtarget -or $null -eq $DBsource)

$DBCountArray | ft -AutoSize

# Loop through and assess the count on the Database count, increment the various `
#  criteria and make new move assignments.
Write-Host "Assigning Standard Mailboxes" -ForegroundColor Yellow
do {
# initialiaze/null out the variables to prevent contaminating the loop.
$DBsource = $null
$DBtarget = $null
# Get the standard mailbox count source and target databases
$DBsource = $DBCountArray | where {$_.stdmbxdiff -gt 0} | sort totmbxdiff -Descending | select -First 1
$DBtarget = $DBCountArray | where {$_.stdmbxdiff -lt 0} | sort totmbxdiff | select -First 1
# If they aren't null then do the work.
if ($DBsource -ne $null -and $DBtarget -ne $null){
# decrement the source databases
($DBCountArray | where {$_.database.name -eq $DBsource.database}).standardmailbox = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).standardmailbox -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).totalmailboxes = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).totalmailboxes -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).stdmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).stdmbxdiff -1
($DBCountArray | where {$_.database.name -eq $DBsource.database}).totmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBsource.database}).totmbxdiff -1
# increment the target databases
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).standardmailbox = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).standardmailbox +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totalmailboxes = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totalmailboxes +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).stdmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).stdmbxdiff +1
($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totmbxdiff = ($DBCountArray | where {$_.database.name -eq $DBtarget.database}).totmbxdiff +1
# access the list of mailboxes and make an assigment based on the target identified in $DBtarget
($MBXArray | where {$_.database -eq $DBsource.Database -and $_.mbxsize -eq "standard" -and $_.moverequest -eq $null} | select -First 1).moverequest = $DBtarget.Database.Name
}
}
# Keep doing the loop until either variable runs out of databases.
until ($null -eq $DBtarget -or $null -eq $DBsource)

# Uncomment to double check your arrays
# $DBCountArray | ft -AutoSize
# $MBXArray | where {$_.moverequest -ne $null}

# New-Move request to initiate seeding of mailboxes to databases as assigned in $MailboxData table.
### The new-moverequest loop is commented out so you don't accidently initiate a move request
<# $MoveRequest = $MBXArray | where {$_.moverequest -ne $null} $MoveRequest | ForEach { $TargetDB = $_.DatabaseName $ID = $_.PrimarySMTPAddress New-MoveRequest -identity $ID -TargetDatabase $TargetDB -BadItemLimit 50 -Batchname $BatchName -SuspendWhenReadyToComplete -Confirm:$false } #>

Monitoring the DAG Replication Network

The Exchange 2010 Database Availability Group (DAG) is an important feature that provides high availability.  Therefore, monitoring the DAG replication network is an important part of keeping the high availability in your environment as healthy as possible.  Replication of Exchange 2010 databases in your messaging environment has specific network requirements outlined in this TechNet document.  The best scenario is to use two network adapters per DAG node member.  One adapter supports the MAPI Network which is used by other servers, for example other Exchange 2010 servers or directory servers.  The other adapter is for the DAG replication network which is dedicated for database replication.  With all this said, Exchange 2010 is smart enough to switch replication from the replication network to the MAPI network in the event of a communications problem on the DAG replication network.

Read moreMonitoring the DAG Replication Network

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

Who moved my PAM?

If your Exchange 2010 deployment utilizes a DAG in two or more Active Directory sites you may have run into situation where the Primary Active Manager (PAM) is unexpectedly located on a DAG node that resides in another site.  The PAM, aka the “cluster group” will shift around as necessary among the members of your DAG for various reasons.  So you ask, “Who moved my PAM?”:

  1. You may have moved it as part of DAG maintenance.
  2. There was problem with the DAG node hosting the PAM, such as errors in the ESE or a storage problem, and the role moved to a server in an alternate site.
  3. A network communications error occurred, affecting the cluster service heartbeat, causing the PAM to move to what it believes is a surviving member of the DAG.

Unfortunately there is no way to add a server preference to the PAM role.  It would be nice to be able to tell the PAM to try and stay on a server at a preferred active directory site.  The only thing that you can do is to block a DAG member from having database copies activated on it with the following command:

Read moreWho moved my PAM?

DAG Replication Network

Setting up DAG replication is extremely important as outlined in Tim McMichael’s TechNet post, Exchange 2010: Collapsing DAG Networks.  Even when you correctly disable the MAPI network from log shipping, Exchange 2010 will still resort to log shipping on the MAPI network if there is a communication problem on the replication network.  Once you become aware that there has been a communications problem between DAG nodes on the DAG replication network, the next thing you should do is to check a few things out.


Read moreDAG Replication Network