Database white space in Exchange 2007

Database white space in Exchange has been an evolving topic. Great improvements were made in Exchange 2010 and 2013 and you can retrieve the white space for each database by running the simple and short command

Get-MailboxDatabase -Status | Select Server, Name, AvailableNewMailboxSpace

However there is no such option in Exchange 2007. As reported here the previous method of retrieving whitespace is to look for event log entry 1221.

Changes were made to online maintenance in Exchange 2010 which meant it’s now very rare that you would need to dismount and run ESEUTIL to regain the database white space (see http://blogs.technet.com/b/exchange/archive/2011/12/14/database-maintenance-in-exchange-2010.aspx), the event ID 1221 was also removed in favour of the PowerShell command above.

There are a few scripts online that do some funky stuff around interrogating the event log to retrieve 1221 events within a certain time range. Examples would be

$ComputerName = Get-ExchangeServer |where {$_.IsMailboxServer -eq 'True'}
$dmtf = [System.Management.ManagementDateTimeconverter] 
$Start = $dmtf::ToDmtfDateTime( (Get-Date).AddDays(-1).Date ) 
$db = @{Name="DB";Expression={$_.InsertionStrings[1]}} 
$free = @{Name="WhiteSpace(MB)";Expression={[int]$_.InsertionStrings[0]}} 
$filter = "LogFile='Application' AND EventCode=1221 AND TimeWritten>='$Start'" 
Get-WMIObject Win32_NTLogEvent -ComputerName $ComputerName -Filter $filter | select ComputerName,$db,$free

and

$ComputerName = Get-ExchangeServer |where {$_.IsMailboxServer -eq 'True'}
$tc = [System.Management.ManagementDateTimeconverter] 
$Start =$tc::ToDmtfDateTime( (Get-Date).AddDays(-10).Date ) 
$End =$tc::ToDmtfDateTime( (Get-Date).Date) 
$DB = @{Name="DB";Expression={$_.InsertionStrings[1]}} 
$FreeMB = @{Name="FreeMB";Expression={[int]$_.InsertionStrings[0]}} 
Get-WMIObject Win32_NTLogEvent -ComputerName $ComputerName -Filter "LogFile='Application' AND EventCode=1221 AND TimeWritten>='$Start' AND TimeWritten<='$End'" | Select-Object ComputerName,$DB,$FreeMB | Sort-Object FreeMB –Unique –Descending

These are great but have some limitations. For example if you scan a 3 day period and have 1 database that had online maintenance run on it for 3 days in a row you will see 3 entries for the database. Conversely if you only retrieve 1 day’s worth of logs you might find (depending how long your online maintenance window is) that there won’t be 1221 entries for all of your databases.

I’ve managed to put together a script that searches through the event log entries and returns the most recent entry for each database.

The 1221 log pulls back info including:

RecordNumber : 2037976
SourceName : MSExchangeIS Mailbox Store
TimeGenerated : 20140310053541.000000+780
TimeWritten : 20140310053541.000000+780
Type : Information
Category : 6
CategoryString : General
EventCode : 1221
EventIdentifier : 1074136261
InsertionStrings : {nnnn, storage_group>\

I started by borrowing some code from the second script above, to ensure that today’s date and the date to start from are in the same format that the event log uses. I have set the script to search the last 7 days of logs, but you can customise it as you wish.

$tc = [System.Management.ManagementDateTimeconverter] 
$Start = $tc::ToDmtfDateTime( (Get-Date).AddDays(-7).Date ) 
$End = $tc::ToDmtfDateTime( (Get-Date).Date) 

I then create some arrays and variables to work with. First is a variable for the cluster server that has the events in the log, then an array which contains all the events we want to iterate through, another array with all databases we want information for and finally an empty array which will be used as a record to ensure once we have the most recent data for a database that the information is not overwritten.

The RecordNumber sort on the events is important, as when we work through the array later we want to ensure we’re working from newest entry to oldest.

$ComputerName = Get-ExchangeServer | where {$_.IsMailboxServer -eq 'True' -and $_.IsMemberOfCluster -eq 'Yes'}
$AppEvents = Get-WMIObject Win32_NTLogEvent -ComputerName $ComputerName -Filter "LogFile='Application' AND EventCode=1221 AND TimeWritten>='$Start' AND TimeWritten<='$End'" | Sort RecordNumber -desc
$Databases = Get-MailboxDatabase
$LatestEvent = @()

As we’re looking for each database in each event entry we need to do a nested foreach loop. The $SG variable is set specifically to match the format in the event entry, it needs to be a string else the IF comparison fails.

The InsertionStrings[0] and InsertionStrings[1] refer to the areas in the event that correspond to the storage group name and amount of database white space.

Finally the IF -not statement combined with the $LatestEvent array ensure if the database has already been processed then the entry will be ignored (so we always have the most recent entry). The $LatestEvent array is then appended to in order to ensure we don’t retrieve older data for that database.

FOREACH ($Database in $Databases) {
    $SGName = $Database.StorageGroupName
    $DBName = $Database.Name
    [string]$SG = "$SGName\$DBname"
    FOREACH ($Event in $AppEvents) {
        $EventSG = $Event.InsertionStrings[1]
        $DBSize = $Event.InsertionStrings[0]
        IF ($SG -eq $EventSG) {
                IF (-not ($LatestEvent -contains $SG)) {
                Write-Host "For database" $DBName "DBSize is:" $DBSize "Mb" -foregroundcolor Green  
                $LatestEvent += $SG
                }
        }
    }
} 

So to put it all together:

Add-PSSnapin Microsoft.Exchange.Management.Powershell.Admin
$tc = [System.Management.ManagementDateTimeconverter] 
$Start = $tc::ToDmtfDateTime( (Get-Date).AddDays(-7).Date ) 
$End = $tc::ToDmtfDateTime( (Get-Date).Date) 
$ComputerName = Get-ExchangeServer | where {$_.IsMailboxServer -eq 'True' -and $_.IsMemberOfCluster -eq 'Yes'}
$AppEvents = Get-WMIObject Win32_NTLogEvent -ComputerName $ComputerName -Filter "LogFile='Application' AND EventCode=1221 AND TimeWritten>='$Start' AND TimeWritten<='$End'" | Sort RecordNumber -desc
$Databases = Get-MailboxDatabase
$LatestEvent = @()
FOREACH ($Database in $Databases) {
    $SGName = $Database.StorageGroupName
    $DBName = $Database.Name
    [string]$SG = "$SGName\$DBname"
    FOREACH ($Event in $AppEvents) {
        $EventSG = $Event.InsertionStrings[1]
        $DBSize = $Event.InsertionStrings[0]
        IF ($SG -eq $EventSG) {
                IF (-not ($LatestEvent -contains $SG)) {
                Write-Host "For database" $DBName "DBSize is:" $DBSize "Mb" -foregroundcolor Green  
                $LatestEvent += $SG
                }
        }
    }
} 
Posted in Guides Tagged with: , ,
One comment on “Database white space in Exchange 2007
  1. Butler says:

    This worked great. Thanks for the post!

Leave a Reply

Your email address will not be published. Required fields are marked *

*