Programmatically counting mailboxes in Mailbox Databases using C#…

by Andy Grogan on May 18, 2013 · 0 comments

in Exchange 2010 (Databases), Exchange 2013 (Databases), Exchange 2013 (Mailboxes), Exchange Programming .NET, Windows Server 2012 [ Active Directory ]

Ok, I know that the above is pretty easy using Powershell. In fact it is possible to also implement this using Automation Runspaces (again executing Powershell cmdlets from within the C# project) to achieve the same thing. But for a program that I am writing at the moment I needed to take a slight different approach.

In essence I need to be able to quickly count all user mailboxes within a Exchange Mailbox Database and return them in a format of my choosing – for example; in the main program that I am writing the format would be an array which is then ported to the Data Grid control. I discovered that whilst this is a very simple task in pure Powershell – it is not that easy to convert into C# to accomplish my goal as described above.

To set the scene the Powershell cmdlets which can be piped together are as follows (referenced from: http://blog.dargel.at/2012/01/30/get-the-number-of-mailboxes-per-database/) :

(get-mailboxdatabase) | foreach-object {write-host $_.name (get-mailbox -database $_.name).count}

Which will give you an output similar to the following:

18-05-201312-54-04

I found that encapsulating something similar to the above within C# presented me with two main issues:

  • Execution time was lengthy – depending on the number of Mailbox Databases and Mailboxes.
  • Formatting the results from the Automation Runspace into an array was not easy.

I had a look around the Internet to see if there was a native way using something like EWS to accomplish the same thing – but alas I could not find exactly what I wanted. So I decided to write something in C# which allowed me to achieve what I needed.

Essentially – whereas it is always preferable to use Powershell when interacting with or querying Exchange – most Exchange Objects – including Databases and Mailboxes are held within Active Directory. This means – that if we know a few Directory attributes which tie Mailboxes to Databases then we should be able to construct an LDAP search string using a DirectorySearcher (using the System.DirectoryServices Reference) and count the results.

So after some brief thinking, a few cups of coffee tea and a heavy session using ADSI Edit I decided on the following two Active Directory attributes:

  • homeMDB – contained on each user object that has a mailbox.
  • DistinguishedName – contained on all Mailbox Databases (and all other objects in AD – but I am interested in the DN on the Mailbox Database).

The homeMDB attribute on each user object in Active Directory is in fact the DistinguishedName of the Mailbox Database that contains the Mailbox object within Exchange – see below:

Mailbox Database Distinguished Name

18-05-20113-18-08

AD User Object homeMDB

18-05-201313-18-58

Given the above using an LDAP filter like:

(&(objectClass=user)(homeMDB=CN=Paris_Office,CN=Databases,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=artTest,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=artTest,DC=local)(!(cn=HealthMailbox*))(!(cn=SystemMailbox*)))

Should return all users with a mailbox within the specified homeMDB. You will note in the example above I have excluded mailboxes with a CN= that starts “SystemMailbox” or “HealthMailbox” – using the following syntax:

(!(cn=HealthMailbox*))(!(cn=SystemMailbox*))

The output of the above LDAP query when testing in Active Directory Users and Computers looks like the following:

18-05-201313-26-26

Now that I had the information that I needed from Active Directory and the correct LDAP query all I needed to do was convert that into C#.

Firstly I wrote a command line based test program which accepts the following parameters:

  • Mailbox Database DN
    • e.g. CN=Paris_Office,CN=Databases,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=artTest,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=artTest,DC=local
  • Execution Mode
    • System – includes system and health mailboxes in the overall count from the database
    • nonSystem – only includes normal mailboxes

Therefore correct command line examples when the program was compiled would be (including the double quotes):

All mailboxes including system and health:

GetDatabaseMailboxCount.exe “CN=Paris_Office,CN=Databases,CN=Exchange Administrative Group(FYDIBOHF23SPDLT),CN=Administrative Groups,CN=artTest,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=artTest,DC=localSystem

Or if you want just normal mailboxes:

GetDatabaseMailboxCount.exe “CN=Paris_Office,CN=Databases,CN=Exchange Administrative Group(FYDIBOHF23SPDLT),CN=Administrative Groups,CN=artTest,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=artTest,DC=localnonSystem

The program (code supplied below) should be compiled using Visual Studio, using .NET 3.5 (or above) as a Windows Console Application with the System.DirectoryServices reference added to the project.

The Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;

namespace GetDatabaseMailboxCount
{
    class Program
    {

        /*
         Define a function that gets the Root Directory Entry Path.
         This is used by other functions to return the default naming context:
         LDAP://DC=artTest,DC=local
         */

        public static DirectoryEntry GetDirectoryEntry()
        {
            try
            {
                DirectoryEntry Root = new DirectoryEntry("LDAP://RootDSE");
                string Domain = (string)Root.Properties["defaultNamingContext"][0];

                DirectoryEntry DirectoryENT = new DirectoryEntry();

                DirectoryENT.Path = "LDAP://" + Domain;
                DirectoryENT.AuthenticationType = AuthenticationTypes.Secure;

                return DirectoryENT;
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Failed to contact Active Directory with error: \n{0}", ex.Message);
                Console.ResetColor();
                return null;
            }
        }

        /*
         
         Define the function which Counts the Mailboxes per Database
         
         
         */

        static void GetDatabaseMailboxCount(string MailboxDN, string sOP)
        {
            
            // Get the Directory Path
            
            string aDE = GetDirectoryEntry().Path.ToString();
            
            // Create a Directory Searcher Object Set to the Path

            DirectorySearcher search = new DirectorySearcher(aDE);

            string filter;


            if (sOP.ToLower() == "system")
            {
                Console.WriteLine("Checking including all Mailboxes");
                
                // If you want to include system and health mailboxes
                // configure LDAP query to look for all users with
                // the homeMDB attribute set the the passed MBDB DN

                filter = "(&(objectClass=user)(homeMDB=" + MailboxDN + "))";
            }
            else
            {
                Console.WriteLine("Checking excluding System and Health Mailboxes");
                
                // Otherwise filter out cn= starting with HealthMailbox or SystemMailbox
                
                filter = "(&(objectClass=user)(homeMDB=" + MailboxDN + ")(!(cn=HealthMailbox*))(!(cn=SystemMailbox*)))";
            }

            search.Filter = filter;
            search.PageSize = 10000;
            search.SearchScope = SearchScope.Subtree;
            
            int Number = 0;

            // Count each result

            foreach (SearchResult result in search.FindAll())
            {
                Number++;
            }

            // Write the results to the console

            Console.WriteLine("Mailbox Database: " + MailboxDN + " contains " + Number + " Mailboxes");

        }

        static void Main(string[] args)
        {

            Console.Clear();
            
            // Get the parameters from the command line

            string DN = args[0];
            string mode = args[1];
            
            // Pass them to the GetDatabaseMailboxCount function

            GetDatabaseMailboxCount(DN,mode);
        
        }
    }
}

Output from the Code:

If the program is executed with the nonSystem parameter the output should look like the following example – notice the Mailbox count for the database is at the end:

18-05-201313-02-19

If the program is executed with the System parameter the output should look like the following example:

18-05-201313-02-43

So how did the above help me?

Well the code contains the following basic logic:

  • Accepts a Mailbox Database distinguishedName
  • Counts all users with Mailboxes in that DB
  • Outputs the DB name and the mailbox count
  • Executes quickly

For me it will now be quite simple to modify the code to return the values to an array or list and then set that as the Data Source for a Grid View control. Which is exactly what I am now doing.

I hope that this helps someone out there who might be looking to the same or similar thing – and please feel free to modify the code how you see fit.

Social

{ 0 comments… add one now }

Leave a Comment

Previous post:

Next post: