Wrangling Byte Quantified Size details from Exchange 2007 with Powershell

Exchange 2007 has some very powerful cmdlets which can be used to generate fairly effective reports, out of the box. However, sometimes they're just a bit TOO powerful, and it leads to some confusion about how to get out the data you want.

Example of this -- an email I responded to today: Customer was using an MS home-grown "Out-Chart" script that Vivek or someone had written many months ago. This script simply takes the pipeline input and grabs one parameter as the XAxis and a second parameter as YAxis for creating a chart using the Office 2003 "Office Web Components" OWC11.ChartSpace object.

The problem? The customer was piping in like this:

Get-MailboxStatistics | Out-Chart DisplayName TotalItemSize

Looks great, right? Well, except that this Out-Chart script was just passing in the two parameters to the charting object verbatim, which was expecting them to both be integers of some sort. And, of course, the powerful richness of Get-MailboxStatistics provides TotalItemSize as a "Byte Quantified Size" (BQS) type rather than an integer. Because it's BQS type, this means that when the scale is not specified, the scale details are embedded in the output (so it looks like "1234B" to represent a size in bytes, rather than "1234" - which could mean 1234 bytes or 1234 mb!). The chart object was throwing an exception and not drawing a useful chart. (see this earlier post for some more detail on BQS).

What to do? We need to do some sort of conversion so that the output object passed into the Out-Chart script will be in the integer format it expects (yeah, sure... we could also change Out-Chart to do the conversion, but what fun would that be?!). And whether you're trying to use this charting script or not, this next bit may be useful to you at some point as it describes a generic way of doing type conversions with BQS type.

The strategy will be as follows: Get the data objects, iterate across each returned object - doing a type conversion for TotalItemSize, and then output a new object in the format we need (to the pipeline). Easy! Here's what it might look like, if we follow the same pattern we used for the combined object in the Bringing Users and Mailboxes together blog post:

Get-MailboxStatistics | ForEach { New-Object psobject | Add-Member -Passthru NoteProperty Displayname $_.Displayname | Add-Member -Passthru NoteProperty TotalItemSize $_.TotalItemSize.Value.ToKB() } | Out-Chart DisplayName TotalItemSize

Let's talk through it:

  1. Get-MailboxStatistics = Outputs all of the mailbox statistics for the default database
  2. ForEach = Cracks open each MailboxStatistics output object and does something with it (our iterator)
  3. New-Object = Creates a new powershell object on the pipeline and passes it along the pipeline
  4. first Add-Member = Pull the DisplayName member from the original object and add it to the new object
  5. second Add-Member = Pull the TotalItemSize member from the original object, convert it to KB Uint64 and add it to the new object (key part here highlighted in red)
  6. Output the new object (now with only two parameters) to Out-Chart

But what if you don't want to create a whole new object? What if you actually want to use all of the original object properties PLUS this great, new, calculated property? Good news, here's some alternate example syntax to do just that:

Get-MailboxStatistics | ForEach { Add-Member -Input $_ -MemberType NoteProperty TotalItemSizeInKB $_.TotalItemSize.Value.ToKB() -Passthru } | Out-Chart DisplayName TotalItemSizeInKB

Happy Powershelling!