In the last post, I walked through how to get mailboxes who met certain quota criteria (including inheriting these values from the mailbox database values).
Today, let's cover this from a different perspective: "who is over their quota".
Sure, it's possible to get this with a simple one-liner like:
Get-MailboxStatistics | fl DisplayName,StorageLimitStatus
But, in addition to that being a bit boring as an Exchange+PowerShell blog example, it's also not real-time. It only updates to represent "over quota" only perodically. If I want to check at 3pm based on current mailbox size at 3pm, I might need something more versatile. Plus, this is supposed to be an interesting Exchange+PowerShell scripting example, so we can't just do it the simple way. :)
Back to the problem... getting back the real-time size vs. quota information is a bit more involved than the last quota example, as it requires you to not only find the accurate mailbox quotas for the users, but be able to use the value to compare against the actual mailbox sizes for each mailbox. This is a bit more time consuming, since it requires a callout to each mailbox store to get the MailboxStatistics info.
Makes sense? Well, let's get to it then and hold onto your seatbelts for this one!
Let's first catalog what we need to do at a logical level:
Sounds easy. But at a glance it sounds like we're going to need to head back into hash-table land since we have two totally disparate objects we're trying to map against one another (see my "Bringing users and mailboxes together" post for the last time we hit this).
Here's a crazy-long one-liner that seems to do the trick (I've broken it out into many lines to make it easier to read):
1 Get-Mailbox -ResultSize Unlimited |2 % { if ($_UseDatabaseQuotaDefaults -eq $false)3 { New-Object psobject | 4 Add-Member -passthru noteproperty Name $_.Name |5 Add-Member -passthru noteproperty DistinguishedName $_.DistinguishedName |6 Add-Member -passthru noteproperty ExchangeGuid $_.ExchangeGuid |7 Add-Member -passthru noteproperty IssueWarningQuota $_.IssueWarningQuota 8 } else {9 New-Object psobject |10 Add-Member -passthru noteproperty Name $_.Name |11 Add-Member -passthru noteproperty DistinguishedName $_.DistinguishedName |12 Add-Member -passthru noteproperty ExchangeGuid $_.ExchangeGuid |13 Add-Member -passthru noteproperty IssueWarningQuota $($(Get-MailboxDatabase $_.Database).IssueWarningQuota)15 }16 } | % { $Quotas = @{} } 17 { $Quotas[$_.ExchangeGuid] = $_.IssueWarningQuota ; $_ } |18 % { Get-MailboxStatistics $_.DistinguishedName } |19 Where { $_.TotalItemSize -gt $Quotas[$_.MailboxGuid].Value }
Whew!
That "one-liner" dumps out mailbox statistics objects, filtered for only those mailboxes that are over their configured (effective) quota at the time they're evaluated. With not too much extra work (and following patterns demonstrated just above), this one-liner could easily be modified to output an object that had both the current size of the mailbox and the current, effective IssueWarningQuota of the mailbox... suitable for framing, er feeding into a report of some sort.
And how does it do it? Line by line analysis:
Now, this is certainly the longest contiguous one-liner I've created to date, and even at a glance it looks pretty inefficient (in terms of repetitive looping, if nothing else). I'm a bit curious if anyone has a more effective way of doing this same work. Feel free to weigh in at the comments section or send me some email if you have suggestions, and perhaps we can post about this again in the future.