For quite a while I’ve had automated PowerShell scripts that monitor our Exchange database sizes. I set this up so we could see weekly/monthly/annual growth patterns for different departments in the company. We weren’t really concerned about running out of space on the backend servers, but it would be good to have a rouch idea of the trajectory. And more data is always better, right?

To start, here is how you get the database size in Exchange PowerShell:

> Get-MailboxDatabase -Status <databaseName> | select DatabaseSize

DatabaseSize
------------
8.063 MB (8,454,144 bytes)

That size format is a bit odd, but in Exchange 2010, PowerShell would let me convert that to gigabytes, using the ToGB() function on the DatabaseSize property. So, I set up a script to iterate through all of our databases, convert each DatabaseSize to GB, and then enter that data into a database.

ForEach ($db in $arr) {
	Get-MailboxDatabase -Status $db | select DatabaseSize | ForEach {
		$size = $_.DatabaseSize.ToGB()
		$query = "INSERT INTO ``sizes`` (``timestamp``, ``database_id``, ``size``) VALUES (DATE(NOW()),(SELECT id FROM ``databases`` WHERE db_name LIKE `'$db`'),$size)"
		Write-Host $query
		$Rows = WriteMySQLQuery $conn $query
	}
}

The WriteMySQLQuery at the end is just a function defined elsewhere that executes the MySQL query.

After we upgraded to Exchange 2013, these scripts stopped working. I tried running the script manually so that I could see errors, and I saw a bunch of these:

ForEach : Method invocation failed because [System.String] doesn't contain a method named 'ToGB'.
At C:\exchangescripts\db_sizes.ps1:99 char:58
+     Get-MailboxDatabase -Status $db | select DatabaseSize | ForEach {
+                                                             ~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [ForEach-Object], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound,Microsoft.PowerShell.Commands.ForEachObjectCommand

I don’t know the exact reason, or I at least can’t find any documentation indicating that Microsoft changed anything. However, in my search for a solution, I found this forum post, which gave me the fix. I inserted these two lines at the top to load the Exchange Data dll:

$dll = "C:\Program Files\Microsoft\Exchange Server\V15\Bin\Microsoft.Exchange.Data.dll"
[Reflection.Assembly]::LoadFile($dll)

Then I used a ByteQuantifiedSize object to hold the DatabaseSize property, like so:

[Microsoft.Exchange.Data.ByteQuantifiedSize]$obj = $db.DatabaseSize

Working this concept into the original script above, it now looks like this:

ForEach ($db in $arr) {
	Get-MailboxDatabase -Status $db | select DatabaseSize | ForEach {
		[Microsoft.Exchange.Data.ByteQuantifiedSize]$rawSize = $db.DatabaseSize
		$size += $rawSize.ToGB()
		$query = "INSERT INTO ``sizes`` (``timestamp``, ``database_id``, ``size``) VALUES (DATE(NOW()),(SELECT id FROM ``databases`` WHERE db_name LIKE `'$db`'),$size)"
		Write-Host $query
		$Rows = WriteMySQLQuery $conn $query
	}
}

This is how I got my data out of Exchange. I also set up a task in Task Schedular to run this every night. Over time this generates a nice bit of data that can be charted. Once I have a bunch of data, maybe I will come back and edit this post with some of those visulations.