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.





{ 40 comments… read them below or add one }
But we can vail this in 2k7 using SP’s
http://technet.microsoft.com/en-us/library/bb124924(EXCHG.80).aspx
There are differences, for example check out the AvailableNewMailboxSpace and DatabaseSize properties when using Get-MailboxDatabase -Status in Exchange 2010.
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?
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.
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.
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.
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 <<<
Oh ok…try this:
[PS] D:\installers>. .\databasestats.ps1
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.
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
No worries…glad you got it going
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
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.
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
So what would the single script look like that you mentioned?
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.csvThen 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:
If you aren’t already familiar with scheduling Exchange 2010 PowerShell scripts check out this post.
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!
Mine runs, but I get no result. No error either. Ideas?
Nice script, it´s exactly I was looking for.
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.
Finally updated it, thanks
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?
Ahh, got it. Thanks, very helpful!
Great, glad you got it working
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
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.
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
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
Awesome info.. One thing… Upon running this script, we returned “infinity”
Why would that be? Empty database?
Thanks in advance
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.
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
Praise be to those who share this great information. Thanks!
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< !!!
works great, many thanks
You roxx thanks
Is it possible to target function at a specific DAG, rather than every server in the environment?
Nevermind. I figured it out. Thanks
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.
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…
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′