Excerpt
Exchange Online sometimes reports mailbox sizes with “Unlimited” wrappers that break simple math. Today I built a one-liner-friendly PowerShell snippet that returns Used GB, Quota GB, and Available GB—even when EXO wraps values in Unlimited<T>.
Intro
I needed the available mailbox size for [mailbox]@[domain] without exposing tenant internals. The usual TotalItemSize parsing failed because EXO returned Unlimited<ByteQuantifiedSize>. Here’s the redacted approach that works reliably and falls back cleanly.
Notes from {Speaker}
- Context: Exchange Online + PowerShell; target was [mailbox]@[domain].
- Constraint:
TotalItemSizeandProhibitSendQuotashow Unlimited wrappers or localized strings. - Goal: Get UsedGB / QuotaGB / AvailableGB with no tenant secrets.
Perspective (direct quotes)
“If it’s
Unlimited<T>, ask for.Value—and always guard withIsUnlimited.”
“When objects don’t expose bytes, regex the(123,456 bytes)pattern as a fallback.”
Practice (today, not someday)
Use this redacted snippet. It works with Get-EXO* and falls back to classic cmdlets:
# EXO connection (redacted UPN)
Connect-ExchangeOnline -UserPrincipalName [me] -ShowBanner:$false
$upn = '[mailbox]@[domain]' # e.g., [email protected]
try {
$stat = Get-EXOMailboxStatistics -Identity $upn -ErrorAction Stop
$mbx = Get-EXOMailbox -Identity $upn -PropertySets Quota -ErrorAction Stop
$usedBytes = if ($stat.TotalItemSize.PSObject.Properties.Name -contains 'Value') {
[int64]$stat.TotalItemSize.Value.ToBytes()
} else {
[int64](([regex]::Match($stat.TotalItemSize.ToString(), '\(([\d,]+)\sbytes\)')).Groups[1].Value -replace ',','')
}
$quotaBytes = if ($mbx.ProhibitSendQuota -and `
($mbx.ProhibitSendQuota.PSObject.Properties.Name -contains 'IsUnlimited') -and `
-not $mbx.ProhibitSendQuota.IsUnlimited) {
[int64]$mbx.ProhibitSendQuota.Value.ToBytes()
} elseif ($mbx.ProhibitSendQuota.ToString() -notmatch 'Unlimited') {
[int64](([regex]::Match($mbx.ProhibitSendQuota.ToString(), '\(([\d,]+)\sbytes\)')).Groups[1].Value -replace ',','')
} else { $null }
}
catch {
$stat = Get-MailboxStatistics -Identity $upn
$mbx = Get-Mailbox -Identity $upn
$usedBytes = [int64](([regex]::Match($stat.TotalItemSize.ToString(), '\(([\d,]+)\sbytes\)')).Groups[1].Value -replace ',','')
$quotaBytes = if ($mbx.ProhibitSendQuota.ToString() -match 'Unlimited') { $null }
else { [int64](([regex]::Match($mbx.ProhibitSendQuota.ToString(), '\(([\d,]+)\sbytes\)')).Groups[1].Value -replace ',','') }
}
$usedGB = [math]::Round($usedBytes/1GB, 2)
$quotaGB = if ($quotaBytes) { [math]::Round($quotaBytes/1GB, 2) } else { $null }
$availGB = if ($quotaBytes) { [math]::Round(($quotaBytes-$usedBytes)/1GB, 2) } else { $null }
[pscustomobject]@{
Mailbox = $upn
UsedGB = $usedGB
QuotaGB = $quotaGB
AvailableGB = $availGB
StorageLimitStatus = $stat.StorageLimitStatus
}
Final Reflection
EXO’s objects are powerful but quirky. Guarding for IsUnlimited, using .Value.ToBytes(), and keeping a regex fallback turns a flaky one-off into a repeatable tool.
Pocket I’m Keeping
“Parse what’s there, not what you expect.” When APIs return wrapped or localized strings, a small fallback (regex for (#### bytes)) saves the day.
What I Hear Now (direct quotes)
“Measure in bytes, report in GB.”
“Handle Unlimited first, then do math.”
“One clean object out—every time.”
Link to the Script
Microsoft Exchange Online PowerShell (Get-EXOMailbox, Get-Mailbox) — official docs
© 2012–2025 Jet Mariano. All rights reserved.
For usage terms, please see the Legal Disclaimer.
Leave a Reply