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.


The first script utilizing Get-Mailbox with comments inline:

[powershell]
# Create the timer object and start it.
$StopWatch = New-Object System.Diagnostics.Stopwatch
$StopWatch.Start()

# Create an empty HashTable to store Database and count.
$MailboxCount = @{}

# Get all the databases in the DAG.
$Databases = Get-MailboxDatabase | where {$_.name -like "*dag*"} | sort name

# Loop through each
ForEach ($database in $databases) {
# This is the slow part, Get-mailbox on each database.
$MBs = Get-Mailbox -Database $database
# Add them into the empty HashTable
$MailboxCount.Add($Database,$MBs.Count)
# Keep a running output so you see the work being done.
Write-Host $database $MBs.Count
}

# Finally, stop the stopwatch
$ElapsedTime = $StopWatch.Elapsed

# Write a line to the console with the results.
Write-Host "The script took" $ElapsedTime.Hours "hours,"
$ElapsedTime.Minutes "minutes, and" $ElapsedTime.Seconds "seconds to run."
[/powershell]

The results are painfully slow:

[powershell gutter="false"]
The script took 0 hours, 9 minutes, and 44 seconds to run.
[/powershell]

Now it's time to speed up the enumeration of mailboxes. We'll only change line 14 but the script is here in its entirety:

[powershell]
# Create the timer object and start it.
$StopWatch = New-Object System.Diagnostics.Stopwatch
$StopWatch.Start()

# Create an empty HashTable to store Database and count.
$MailboxCount = @{}

# Get all the databases in the DAG.
$Databases = Get-MailboxDatabase | where {$_.name -like "*dag*"} | sort name

# Loop through each
ForEach ($database in $databases) {
# This is the faster part, Get-mailboxStatistics on each database.
$MBs = Get-MailboxStatistics -Database $database
# Add them into the empty HashTable
$MailboxCount.Add($Database,$MBs.Count)
# Keep a running output so you see the work being done.
Write-Host $database $MBs.Count
}

# Finally, stop the stopwatch
$ElapsedTime = $StopWatch.Elapsed

# Write a line to the console with the results.
Write-Host "The script took" $ElapsedTime.Hours "hours,"

$ElapsedTime.Minutes "minutes, and" $ElapsedTime.Seconds "seconds to run."
[/powershell]

These results are much more pleasing, the script was over six minutes faster!  I’ll take that any day:

[powershell gutter=”false”]
The script took 0 hours, 3 minutes, and 1 seconds to run.
[/powershell]

Ok, so that was a cool test but where can we apply this in the real world?  Think about any of your scripts that you using for counting mailboxes with the Get-Mailbox cmdlet.  You’ll probably need the attributes returned by Get-Mailbox as Get-MailboxStatistics only provides 27 attributes at the time of this writing.  That is unless you use an additional switch such as -includemovehistory, which is useful when doing a migration or gathering information about a mailbox move.

5 thoughts on “Count Mailboxes Per Database Faster

  1. Great script. I only have one comment. Get-MailboxStatistics will also include disconnected mailboxes. To prevent this, you should update line 14 to not include disconnected mailboxes.

    $MBs = Get-MailboxStatistics -Database $database | ?{!$_.DisconnectDate}

    Below are the times it took in my environment. ~25,000 mailboxes on 120 databases.
    Including Disconnected Mailboxes
    The script took 0 hours, 3 minutes, and 39 seconds to run.

    Excluding Disconnected Mailboxes
    The script took 0 hours, 4 minutes, and 9 seconds to run.

  2. The Get-MDBMailboxCount function is from http://blog.powershell.no/2010/11/21/retrieve-number-of-mailboxes-per-database-in-exchange-server-2010/

    Using the script below took 1 minute and 3 seconds to complete as compared to 4 minutes, and 9 seconds using get-mailboxstatistics.

    #Function to count using Active Directory
    function Get-MDBMailboxCount ([string]$DN)
    {
    $Searcher = New-Object System.DirectoryServices.DirectorySearcher
    $Searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry (“LDAP://$(([system.directoryservices.activedirectory.domain]::GetCurrentDomain()).Name)”)
    $Searcher.Filter = “(&(objectClass=user)(homeMDB=$DN))”
    $Searcher.PageSize = 10000
    $Searcher.SearchScope = “Subtree”
    $results = $Searcher.FindAll()
    $returnValue = $results.Count
    $Searcher.Dispose()
    $results.Dispose()
    return $returnValue
    }

    # Create the timer object and start it.
    $StopWatch = New-Object System.Diagnostics.Stopwatch
    $StopWatch.Start()

    # Create an empty HashTable to store Database and count.
    $MailboxCount = @{}

    # Get all the databases in the DAG.
    $Databases = Get-MailboxDatabase | where {$_.name -like “*dag*”} | sort name

    # Loop through each
    ForEach ($database in $databases) {
    $dbdn = $database.distinguishedname
    $count = Get-MDBMailboxCount $dbdn
    write-host $database $count

    }

    # Finally, stop the stopwatch
    $ElapsedTime = $StopWatch.Elapsed

    # Write a line to the console with the results.
    Write-Host “The script took” $ElapsedTime.Hours “hours,” `
    $ElapsedTime.Minutes “minutes, and” $ElapsedTime.Seconds “seconds to run.”

  3. Wow that is significantly faster than what we both saw in results above. I’ll see what kind of results I get in my environment. Additionally, when looking at that function on blog.powershell.no I found a great discussion on the use of foreach vs foreach-object: http://bsonposh.com/archives/327

Leave a Reply