blog.powershell.no

On Windows PowerShell and other admin-related topics

Retrieve number of mailboxes per database in Exchange Server 2010

Mailbox databases in Exchange Server 2010 doesn`t contain any information regarding the numbers of mailboxes stored in them. A common approach to retrieve this information is using the Exchange Management Shell.

Two examples

 1: #Example 1

 

 2: (Get-MailboxDatabase) | ForEach-Object {Write-Host $_.Name (Get-Mailbox -Database $_.Name).Count}

 

 1: #Example 2

 

 2: (Get-MailboxDatabase) | Select-Object Name,@{Name="Number of users";Expression={(Get-Mailbox -Database $_.name).Count}}

 

Either of these works fine if you want to get the number of mailboxes quick and dirty. However, in larger environments, these one-liners may run for a while.

I did a quick measurement using the Measure-Command cmdlet; Example 1 ran for 36 seconds and example 2 ran for 30 seconds. The environment I ran this in got 5 mailbox databases and approximately 1300 mailboxes.

When running these as one-liners from the Exchange Management Shell, or as part of a scheduled task, the performance might not be an issue.

A colleague of mine was using the Cmdlet Extension Scripting Agent to provision new mailboxes to the mailbox database with the least number of mailboxes in it. In this scenario the performance is key. To achieve better performance, we used an LDAP-query using System.DirectoryServices.DirectorySearcher.

Get-MDBMailboxCount function

I created a PowerShell function to retrieve the number of mailboxes per mailbox database using the DN of a mailbox database as an argument.

 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

#dispose of the search and results properly to avoid a memory leak
 $Searcher.Dispose()
 $results.Dispose()

return $returnValue

 

}

 

A problem we stumbled upon was that wildcards didn`t seem to work on the homeMDB-attribute, so we couldn`t use *Mailbox Database Name* for this value.

When looking up on MSDN, it turned out that wildcards are not supported on LDAP DN`s:

For queries that include attributes of DN syntax in a filter, specify full distinguished names. Wildcards (for example, cn=John*) are not supported.

That`s the reason for the function taking DN as an argument, and not the name of a mailbox database.

The function runs the query against the current domain, and hence you don`t need to specify a domain for the LDAP-query.

Back to the performance part, I now ran Measure-Command against the following one-liner:

 1: (Get-MailboxDatabase) | ForEach-Object {Write-Host $_.Name (Get-MDBMailboxCount -DN $_.DistinguishedName)}

 

This time the performance was 3,7 seconds, almost 10x faster. Actually, using a foreach-loop instead of Foreach-Object also makes it slightly faster; 3,5 seconds. You can read up on the difference between these two approaches here.

Get-MDBMailboxCount script

Having the Get-MDBMailboxCount function, I also decided to create a script using this function, which generates a CSV-file.

The script uses a foreach-loop to go through each database in the Exchange-organization, and then creates a custom object for each database containing the name and number of mailboxes. The custom objects are added to an array which can be used for other things like determining the smallest/largest database. You could also use this information to generate graphs like I showed in this blog-post.

Note that I`ve only tested the above against Exchange Server 2010. I suppose it also should work against Exchange Server 2007, if someone can verify this it would be nice if you could leave a comment below.

Update 04.08.2011: The function has been updated  to avoid a memory leak when the function is called multiple times. Thanks to Weston McNamee for the tip (see his comment below).

Advertisements

November 21, 2010 - Posted by | Exchange Server 2010, Exchange Server management, Scripting, Windows PowerShell |

15 Comments »

  1. You can also use

    get-mailbox -resultsize unlimited | group-object -property Database -noelement

    In my environment ~5000 mailboxes, your example #1 and #2 take 89 seconds, the above takes 52 seconds.
    Your get-mdbmailboxcount method takes 25 seconds, so it still wins, though it’s also good to know there is a slightly faster way to group and count using exchange powershell commands only.

    Comment by KB | January 10, 2011 | Reply

    • Nice to know, thanks for sharing the information!

      Comment by Jan Egil Ring | January 10, 2011 | Reply

  2. Sweet. This is just what I was looking for.

    In my environment with 175 databases and 75000 mailboxes:

    your script = 3 minutes
    KB’s comment = killed the process after 35 minutes.

    Comment by Eric | February 3, 2011 | Reply

  3. The above code has a memory leak issue. Powershell process memory consumption continues to grow after every call to the function (I’ve commented out everything else to isolate). $Searcher.Dispose() isn’t enough either. Still researching how to fix.

    Comment by Weston McNamee | July 20, 2011 | Reply

  4. Solution Found:

    the last line of the function should be rewritten to this:

    # The results from the searcher need to be assigned to a variable so we can dispose of them per the MS article:
    # http://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.findall.aspx
    #
    # Due to implementation restrictions, the SearchResultCollection class cannot release all of its unmanaged resources when it is garbage collected.
    # To prevent a memory leak, you must call the Dispose method when the SearchResultCollection object is no longer needed.
    #
    $results = $Searcher.FindAll()

    $returnValue = $results.Count

    #dispose of the search and results properly to avoid a memory leak
    $Searcher.Dispose()
    $results.Dispose()

    return $returnValue

    Comment by Weston McNamee | July 20, 2011 | Reply

    • Thanks Weston, I`ve updated the post.

      Comment by Jan Egil Ring | August 4, 2011 | Reply

      • By the way, I wanted to thank you for the post. I’m in the BPOS team at Microsoft, and I always hated the idea of using @(Get-Mailbox -ResultSize Unlimited).count to get a mailbox count, because get-mailbox loads a bunch of extra stuff in memory that I don’t really care about.

        Comment by Weston McNamee | August 10, 2011

  5. […] http://blog.powershell.no/2010/11/21/retrieve-number-of-mailboxes-per-database-in-exchange-server-20… Share this:Shareпо электронной почтеTwitterLike this:НравитсяБудьте первым, кому понравился этот . […]

    Pingback by Exchange 2010 – размер базы и количество почтовых ящиков в ней | ILYA Sazonov: ITPro | September 23, 2011 | Reply

  6. I understand what’s going on in the script, but how can I use it to determine the smallest database? In other words I’ve combined the Get-MDBMailboxCount function and the last one-liner listed above and all I want to see is the database with the least number of mailboxes on it.
    Thanks in advance.

    Comment by Lance | September 26, 2011 | Reply

    • Hi,
      Try this: Get-MailboxDatabase | Select-Object Name,@{Name=”Count”;Expression={Get-MDBMailboxCount -DN $_.DistinguishedName}} | Sort-Object count | Select-Object -First 1

      Comment by Jan Egil Ring | September 26, 2011 | Reply

      • Worked like a champ. That beats the way I was doing this by 15 minutes.

        One more question if I may. Is it possible to return just the name of the database and not the count? I would like to be able to assign the line you sent me to a variable, and then use that variable as the database to where I want a mailbox created or whatever.

        Thanks.

        Comment by Lance | September 26, 2011

  7. Sure:
    Get-MailboxDatabase | Select-Object Name,@{Name=”Count”;Expression={Get-MDBMailboxCount -DN $_.DistinguishedName}} | Sort-Object count | Select-Object -First 1 | Select-Object name

    Comment by Jan Egil Ring | September 26, 2011 | Reply

    • Worked like a champ.

      Thanks for all your help.

      Comment by Lance | September 26, 2011 | Reply

  8. hi, how can i edit the above script to count mailboxes with ex.: (custom attribute 1 = ‘test’) ???

    Comment by Mohamed Zakaria | April 17, 2012 | Reply

  9. another notice when running any of the two commands at the beginning, it gets the DB names at the first coloum (ex: 8 DB names) and only the count value for the first (4 DBs ) …. any advise??

    Comment by Mohamed Zakaria | April 17, 2012 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: