A Script for Getting Mailbox Sizes (using WMI in PowerShell)

Originally published June 28, 2007


This post and the script is Exchange 2003 friendly.

As long as you point the “ExchServer” in the attached script to an Exchange 2003 server, this script can work in a mixed environment containing Exchange 2000, Exchange 2007, and Exchange 2003 servers. It will not work in an Exchange 2000 only, or an Exchange 2007 environment (in Exchange 2007, you would use the Get-MailboxStatistics cmdlet; in Exchange 2000 you would use my MAPI script).

In this post I discussed creating a report using WMI for reporting on mailbox sizes in VBscript. In an earlier post, I discussed creating a report using MAPI for reporting on mailbox sizes in VBscript.

In the current post, I discuss creating a report on mailbox sizes using WMI in PowerShell. PowerShell makes this process much simpler than VBscript - the string processing capabilities of the language are much stronger than in VBscript and access to WMI is easier and more comprehensible. There are downsides, too: 1) you may not have installed PowerShell everywhere (get off your duff and get it done! you need this!) and 2) it is a little slower (in VBscript a single connection object would be used for multiple queries - in PowerShell, a new connection object is created for every query).

That being said, all my new development is in PowerShell and I'm converting my VBscripts as fast as I reasonably can. It's a much more powerful and much more administrator-friendly environment.


# process-exchange-domain.ps1
# Michael B. Smith, June, 2007
# This script scans an Active directory, and for every user object with
# a mailbox, it reports the size of that mailbox. After all objects are
# reported, it shows a summary of the total size of all the objects.
Param (
    [string]$dc       = "domain-controller.example.local",
    [string]$exchange = "exchange-server.example.local"
# you can get a DC out of RootDSE, but I run this cross-domain#
# $rootDSE = [adsi] "LDAP://RootDSE"# [string]$dc = $rootDSE.dnsHostName
# $rootDSE = $null
[string]$dom  = "LDAP://" + $dc + "/DC=example,DC=local"

[System.Int64]$script:totalSize      = 0
[System.Int64]$script:totalItems     = 0
[System.Int64]$script:totalDeleted   = 0
[System.Int64]$script:totalMailboxes = 0

# "(&(ObjectCategory=Person)(ObjectClass=User))"

function process-kids ([string]$dc, [string]$exch, $kid)
    $class = $kid.psbase.schemaClassName

    #"class = " + $class
    #"object = " + $kid.name 
    #"path = " + $kid.psbase.path

    if ($class -eq "organizationalUnit" -or $class -eq "container")
        foreach ($item in $kid.psbase.children)
            process-kids $dc $exch $item
    elseif ($class -eq "user")
        #"user = " + $kid.distinguishedName
        #$qry = "LDAP://" + $dc + "/" + $kid.distinguishedName
        #$u = [adsi] $qry
        # [string]$ledn = $u.legacyExchangeDN

        [string]$ledn = $kid.legacyExchangeDN

        if ($ledn -and ($ledn.Length -gt 0))
            $qry = "select * from Exchange_Mailbox where LegacyDN='" + $ledn + "'"
            $obj = gwmi -computer $exch -namespace "root/MicrosoftExchangeV2" -query "$qry"
            if ($obj)   
                #[string]$str = ([string]$u.Name).PadRight(40)

                [string]$str = ([string]$kid.Name).PadRight(40)
                $str += ([System.Int64]$obj.Size).ToString("N0").PadLeft(12)
                $str += " KB"
                $str += ([System.Int32]$obj.TotalItems).ToString("N0").PadLeft(9)
                $str += "   "
                $str += ([System.Int64]$obj.DeletedMessageSizeExtended).ToString("N0").PadLeft(12)
                $str += " KB"

                $script:totalSize += ([System.Int64]$obj.Size)
                $script:totalItems += ([System.Int32]$obj.TotalItems)
                $script:totalDeleted += ([System.Int64]$obj.DeletedMessageSizeExtended)
                $script:totalMailboxes += 1

                $obj = $null   
                #[string]$str = [string]$u.Name + " has no mailbox."

        $u = $null 
        # lots of funky crap in a domain that we don't care about...
        # $kid.distinguishedName  # $class 

"Mailbox name                               Mailbox size  Item count   Deleted size"
$domain = [adsi] $dom
foreach ($kid in $domain.psbase.children)
    process-kids $dc $exchange $kid
$domain = $null

$str =  ($script:totalMailboxes.ToString("N0") + " mailboxes").PadRight(25)
$str += $script:totalSize.ToString("N0").PadLeft(12) + " KB"
$str += $script:totalItems.ToString("N0").PadLeft(9) + "   "
$str += $script:totalDeleted.ToString("N0").PadLeft(12) + " KB"

Is anything hard about this? Nope. Absolutely not. You will however see some things that you may not be used to seeing in a PowerShell script. Pay close attention to the format strings that I pass to the ToString() method in order to format the numbers for mailbox size, item count, and deleted item size. Also, note the casts to System.Int64 when accessing WMI properties (if you don't do this, you may access truncated or incorrect values). Also, note the use of the PadLeft() and PadRight() methods for string objects. VERY useful methods for formatting, which don't see very much use.

Finally, note the way that ADSI is used. In PowerShell, the ADSI objects are really just a wrapper for System.DirectoryServices (which is a wrapper for the base ADSI that you can access in VBscript). You should note that this script iteratively scans through a domain. This does not scale. In a large domain, you would want to do a directory search for the specific objects you are interested in, instead of doing an iterative scan. (See the LDAP query immediately above the function definition for process-kids.)

For advanced information and research, you could uncomment many of the lines of this routine and learn much about your Active Directory environment.

Published Tuesday, November 13, 2007 8:52 PM by michael
Filed under: , ,


No Comments