Exchange Online: Mailbox Used/Quota/Available (Redacted)

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: TotalItemSize and ProhibitSendQuota show 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 with IsUnlimited.”
“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.

Comments

Leave a Reply

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

error: Content is protected !!