Exchange 2010 Database Statistics with PowerShell

by Mike Pfeiffer on March 10, 2010

This morning I was asked to write a script that would email a daily report of Exchange 2010 database statistics. The customer wanted the following information about each database:

  • Total database size
  • Total number of mailboxes per database
  • Average mailbox size
  • Date and time of the last full backup
  • Amount of white space in the database

Thanks to the improvements made to the Get-MailboxDatabase cmdlet in Exchange 2010, it's a lot easier to gather all of this information than it used to be. Here is the function I created to grab the info:

function Get-DatabaseStatistics {
    $Databases = Get-MailboxDatabase -Status
    foreach($Database in $Databases) {
        $DBSize = $Database.DatabaseSize
        $MBCount = @(Get-MailboxStatistics -Database $Database.Name).Count

        $MBAvg = Get-MailboxStatistics -Database $Database.Name |
          %{$_.TotalItemSize.value.ToMb()} |
            Measure-Object -Average            

        New-Object PSObject -Property @{
            Server = $Database.Server.Name
            DatabaseName = $Database.Name
            LastFullBackup = $Database.LastFullBackup
            MailboxCount = $MBCount
            "DatabaseSize (GB)" = $DBSize.ToGB()
            "AverageMailboxSize (MB)" = $MBAvg.Average
            "WhiteSpace (MB)" = $Database.AvailableNewMailboxSpace.ToMb()
        }
    }
}

Here is a screen shot running the above function in the Exchange Management Shell:

Using the Get-DatabaseStatistics function, it's easy to export the data to a csv:

Get-DatabaseStatistics | Export-Csv c:\windows\temp\report.csv -Force -NoType

And then send the report to the administrator in an email:

Send-MailMessage -To administrator@litware.internal -From powershell@litware.internal -Subject "Database Statistics for $((get-date).ToShortDateString())" -SmtpServer hc1 -Attachments c:\windows\temp\report.csv

I added all of this to a single script and configured a scheduled task so that it would email the customer daily.

Update 5/31/2011 - I updated the code for this function so that archive and disconnected mailboxes would be taken into consideration.

Related Posts

{ 40 comments… read them below or add one }

Ramu April 30, 2010 at 3:08 am
Mike Pfeiffer May 1, 2010 at 3:45 pm

There are differences, for example check out the AvailableNewMailboxSpace and DatabaseSize properties when using Get-MailboxDatabase -Status in Exchange 2010.

Reply

Chris Lehr June 29, 2010 at 10:07 am

How do you integrate the command into the EMS? I made it a PS1 and ran it, but then the command doesn’t work. Am I being dense?

Reply

Mike Pfeiffer June 29, 2010 at 10:16 am

Hey Chris,

Since you saved the function to a .ps1 you need to dot source that file. You do that by typing a period, then a space and then the path to the file. For example:

[PS:1] . C:\functions.ps1

After that, the function will be available to you and you can call it like a cmdlet using the name: Get-DatabaseStatistics.

You could also add the function to your PowerShell profile and restart the shell (that’s what I usually do). Also, you can just paste the function code into the shell, and it will be available for the remainder of your current shell session.

Reply

Chris Lehr June 30, 2010 at 8:00 am

I must really be missing something. I saved this as databasestats.ps1. Ran EMS (As Admin), went to the directory I saved it in, ran the PS1 (nothing happens, as expected) – then get-DatabaseStatistics acts like an unknown cmdlet.

Reply

Mike Pfeiffer June 30, 2010 at 8:06 am

You need to dot source the script, so type a period, then a space and then the path to that file. For example:

PS C:\> . C:\scripts\databasestats.ps1

Then you can call the function.

Reply

Chris Lehr June 30, 2010 at 8:28 am

Was doing just that.

2010 RU3

[PS] D:\installers>.\databasestats.ps1
[PS] D:\installers>get-databasestatistics
The term ‘get-databasestatistics’ is not recognized as the name of a cmdlet, function, script file, or operable program
. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:23
+ get-databasestatistics <<<.D:\installers\databasestats.ps1
The term ‘.D:\installers\databasestats.ps1′ is not recognized as the name of a cmdlet, function, script file, or operab
le program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:33
+ .D:\installers\databasestats.ps1 <<<

Reply

Mike Pfeiffer June 30, 2010 at 8:34 am

Oh ok…try this:

[PS] D:\installers>. .\databasestats.ps1

Reply

Chris Lehr June 30, 2010 at 10:26 am

That doesnt work. Ran d:\installers\databasestats.ps1 (full path) and still doesn’t work. Sorry to keep bugging you. I am running EMC as Admin, and I set exec policy to unrestricted as well, no difference.

Reply

Chris Lehr June 30, 2010 at 10:30 am

I didn’t see the space between your dots. That did it. “..\databasestats.ps1″ Sorry to take your time, we’re ALL busy ;)

Would love to understand WHY sometime :)

Reply

Mike Pfeiffer June 30, 2010 at 12:09 pm

No worries…glad you got it going :)

Reply

Rob RG August 16, 2010 at 12:30 am

Like the script but how do i get it to output in the order I want. ie Server, DBName, DBsize, etc

not alphbetic nor order of specification

Reply

Mike Pfeiffer August 16, 2010 at 5:01 am

Hi Rob,

Just pipe the function to select-object and specify the property names in order:

Get-DatabaseStatistics | select-object Server,DatabaseName,”DatabaseSize (GB)”

You continue piping it to out export-csv if needed.

Reply

Rob RG September 8, 2010 at 9:15 pm

excellent. made some adjustments to output others may be interested in…

$OutFile = “c:\bw_apps\exch_spacereport.csv”
$TmpFile = “c:\bw_apps\exch_update.csv”

# save the output to a new object
$d = Get-DatabaseStatistics | select-object Date,Server,DatabaseName,”DatabaseSize (MB)”,”WhiteSpace (MB)”

# export the object
$d | Export-Csv $TmpFile -Force -NoTypeInformation

# add the new data to the end of existing CSV file, dropping the header
get-content $tmpfile | select -Skip 1 >> $outfile

# convert object to string for inclusion is email body ###
$e = $d | out-string

# send email to the it group for review
Send-MailMessage -To it_admin@qwerty.com -From exch_powershell@qwerty.com -Subject “Exchange Statistics for $((get-date -format “dd/MMM/yyyy”))” -SmtpServer MailMan -body $e

Remove-Item $tmpfile

Reply

Jaymz October 13, 2010 at 11:31 am

So what would the single script look like that you mentioned?

Reply

Mike Pfeiffer October 13, 2010 at 11:35 am

The .ps1 script would contain this code:

function Get-DatabaseStatistics {
    $Databases = Get-MailboxDatabase -Status
    foreach($Database in $Databases) {
        $DBSize = $Database.DatabaseSize
        $MBCount = @(Get-MailboxStatistics -Database $Database.Name).Count

        $MBAvg = Get-MailboxStatistics -Database $Database.Name |
          %{$_.TotalItemSize.value.ToMb()} |
            Measure-Object -Average            

        New-Object PSObject -Property @{
            Server = $Database.Server.Name
            DatabaseName = $Database.Name
            LastFullBackup = $Database.LastFullBackup
            MailboxCount = $MBCount
            "DatabaseSize (GB)" = $DBSize.ToGB()
            "AverageMailboxSize (MB)" = $MBAvg.Average
            "WhiteSpace (MB)" = $Database.AvailableNewMailboxSpace.ToMb()
        }
    }
}

Get-DatabaseStatistics | Export-Csv c:\windows\temp\report.csv -Force -NoType

Send-MailMessage -To administrator@litware.internal -From powershell@litware.internal -Subject "Database Statistics for $((get-date).ToShortDateString())" -SmtpServer hc1 -Attachments c:\windows\temp\report.csv

Then you could schedule the .ps1 script to run as needed. So let’s say you saved the above code to c:\dbreport.ps1, you could schedule the following command to run on the server:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command ". 'C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1'; Connect-ExchangeServer -auto; c:\dbreport.ps1"

If you aren’t already familiar with scheduling Exchange 2010 PowerShell scripts check out this post.

Reply

Jaymz October 14, 2010 at 5:22 am

Much appreciated. I was under the assumption you didnt have to have a trigger script to launch the dbreport.ps1. But still, I can live with this, good job!

Reply

sentinelace October 25, 2010 at 5:29 am

Mine runs, but I get no result. No error either. Ideas?

Reply

Fabrizio Zavalloni December 17, 2010 at 11:05 am

Nice script, it´s exactly I was looking for.

Reply

Ambers Ferrara December 20, 2010 at 9:21 am

Would it be more accurate to change line 5 to:

$MBCount = (Get-MailboxStatistics -Database $Database.Name).Count

Get-mailbox doesn’t include disconnected or archive mailboxes in its count whereas Get-MailboxStatistics does.

Reply

Mike Pfeiffer May 31, 2011 at 2:59 pm

Finally updated it, thanks :)

Reply

Derrick January 25, 2011 at 7:20 am

The script will run but does not display statistics. When I use the command to export to CSV, it is a blank file.

:(

What am I missing?

Reply

Derrick January 25, 2011 at 7:22 am

Ahh, got it. Thanks, very helpful!

Reply

Mike Pfeiffer January 25, 2011 at 7:32 am

Great, glad you got it working :)

Reply

Toni March 8, 2011 at 5:51 am

Strange, I get this. Any ideas?

You cannot call a method on a null-valued expression.
At C:\get-dbstats.ps1:6 char:33
+ $MBAvg = $DBSize.ToBytes <<<< () / $MBCount
+ CategoryInfo : InvalidOperation: (ToBytes:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

You cannot call a method on a null-valued expression.
At C:\get-dbstats.ps1:13 char:63
+ "DatabaseSize (GB)" = "{0:n2}" -f ($DBSize.ToBytes <<<< () / 1GB)
+ CategoryInfo : InvalidOperation: (ToBytes:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

Reply

Shawn March 29, 2011 at 12:04 am

Derrick im getting the same issue as you. I pipe the script into a .csv but no output in the file. What did you do to fix this? Many thanks.

Reply

Mike Pfeiffer March 29, 2011 at 8:17 am

Hi Shawn,

Make sure the function is actually getting loaded into the shell, see this post:
http://www.mikepfeiffer.net/2010/06/how-to-add-functions-to-your-powershell-session/

Then you should be able to do something like:

Get-DatabaseStatistics | Export-Csv c:\report.csv -Force -NoType

Reply

Jonny May 12, 2011 at 4:39 am

The script you supplied is just what I was looking for.
I’ve modified the script for information on Public folder size
Function Get-DatabaseStatisticspublic {
$Databases = Get-publicfolderdatabase -Status
foreach($Database in $Databases) {
$DBSize = $Database.DatabaseSize
$MBAvg = $DBSize.ToBytes()

New-Object PSObject -Property @{
Server = $Database.Server.Name
DatabaseName = $Database.Name
LastFullBackup = $Database.LastFullBackup
“DatabaseSize (GB)” = “{0:n2}” -f ($DBSize.ToBytes() / 1GB)
“AverageMailboxSize (MB)” = “{0:n2}” -f ($MBAvg / 1MB)
“WhiteSpace (MB)” = “{0:n2}” -f ($Database.AvailableNewMailboxSpace.ToBytes() / 1MB)
}
}
}

is there a way to combined the the Maildatabase script and this script attached together to export it to 1 one CSV file
if anyone can help that would be much appreciated
many thanks
Jon

Reply

Jon May 31, 2011 at 2:05 pm

Awesome info.. One thing… Upon running this script, we returned “infinity”
Why would that be? Empty database?

Thanks in advance

Reply

Mike Pfeiffer May 31, 2011 at 2:57 pm

Actually I just cleaned up a few things in this code…hadn’t looked at this in a while…Try running this new version and see if that helps.

Reply

Johnny Dallas September 22, 2011 at 12:40 pm

I too was stumped at why the script wouldn’t show any output when I ran it from command line. Then I figured out that I was just running the script without calling the function.

For other scripting newbs that might have made this mistake, just put the following at the end of the file after the last brace to call the function:

get-DatabaseStatistics

Reply

Marty van Doorninck October 4, 2011 at 9:58 pm

Praise be to those who share this great information. Thanks!

Reply

Adam October 12, 2011 at 12:16 pm

WOW !! thanks — putting the period in from of the PS1 script adds the function
. C:\Users\admin\Desktop\exchange_db.ps1

I after the period .. .then the fucntion became avaiable !! thanks< !!!

Reply

sarelis November 7, 2011 at 4:26 am

works great, many thanks

Reply

guytou November 25, 2011 at 7:13 am

You roxx thanks :)

Reply

James Willett December 14, 2011 at 7:52 am

Is it possible to target function at a specific DAG, rather than every server in the environment?

Reply

James Willett December 14, 2011 at 11:36 am

Nevermind. I figured it out. Thanks

Reply

Abdul January 26, 2012 at 3:08 am

If not mis-taken, to run this script we need to have event 1221 in application log of exchange 2007 to get the result.

B’Coz, i tried importing the module and then running the script, it doesn’t return any value neither error.

Appreciate your help.

Reply

Sam January 29, 2012 at 4:55 pm

Hi James Willett:

what did you do to target it some specific DAG or some specific databases only in an environment?? I dont want the script to report every DB/server in the environment, rather some servers/databases of my own choice…

Reply

Mike Pfeiffer January 30, 2012 at 6:49 pm

Here’s a quick and dirty mod to the function adding identity and server parameters

function Get-DatabaseStatistics {
    param($identity='*', $server)

		if($Identity) {
			$Databases = Get-MailboxDatabase -Identity $identity -Status
		}		

		if($server) {
			$Databases = Get-MailboxDatabase -Server $server -Status
		}		

    foreach($Database in $Databases) {
        $DBSize = $Database.DatabaseSize
        $MBCount = @(Get-MailboxStatistics -Database $Database.Name).Count

        $MBAvg = Get-MailboxStatistics -Database $Database.Name |
          %{$_.TotalItemSize.value.ToMb()} |
            Measure-Object -Average            

        New-Object PSObject -Property @{
            Server = $Database.Server.Name
            DatabaseName = $Database.Name
            LastFullBackup = $Database.LastFullBackup
            MailboxCount = $MBCount
            "DatabaseSize (GB)" = $DBSize.ToGB()
            "AverageMailboxSize (MB)" = $MBAvg.Average
            "WhiteSpace (MB)" = $Database.AvailableNewMailboxSpace.ToMb()
        }
    }
}

A few examples of how you might run it:

Get-DatabaseStatistics
Get-DatabaseStatistics -server mbx1
Get-DatabaseStatistics -identity *12*
Get-DatabaseStatistics -identity ‘Mailbox Database 1271702967′

Reply

Leave a Comment

Previous post:

Next post: