Public/Get-O365MailboxUsage.ps1
function Get-O365MailboxUsage { <# .SYNOPSIS Retrieves Office 365 mailbox usage statistics showing storage utilization and percentage full. .DESCRIPTION This function connects to Exchange Online and retrieves mailbox statistics including current usage, total quota, and percentage full. Results are sorted by highest usage percentage by default. Can process specific users from pipeline input or return all mailboxes. .PARAMETER UserPrincipalName Specific user principal name (email address) to retrieve statistics for. Can accept pipeline input. .PARAMETER SortBy Property to sort results by. Valid values: PercentFull, UsedSpaceGB, TotalQuotaGB, DisplayName Default is PercentFull (descending). .PARAMETER IncludeArchive Include archive mailbox statistics if available. .PARAMETER ExportToCsv Export results to a CSV file in C:\Temp folder. .PARAMETER CsvPath Custom path for CSV export. If not specified, defaults to C:\Temp\O365MailboxUsage_YYYYMMDD_HHMMSS.csv .EXAMPLE Get-O365MailboxUsage Returns all mailboxes sorted by percentage full (highest first). .EXAMPLE "user@domain.com" | Get-O365MailboxUsage Returns statistics for specific user from pipeline input. .EXAMPLE # Process multiple users @("user1@domain.com", "user2@domain.com") | Get-O365MailboxUsage .EXAMPLE Get-O365MailboxUsage -SortBy UsedSpaceGB -IncludeArchive Returns all mailboxes sorted by used space including archive statistics. .EXAMPLE Get-O365MailboxUsage -ExportToCsv Returns all mailboxes and exports results to CSV in C:\Temp folder. .EXAMPLE Get-O365MailboxUsage -CsvPath "D:\Reports\mailbox_report.csv" Returns all mailboxes and exports to a custom CSV path. .NOTES Requires Exchange Online PowerShell module and appropriate permissions. Run Connect-ExchangeOnline before using this function. #> [CmdletBinding()] param( [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Alias("Identity", "PrimarySmtpAddress", "Email")] [string[]]$UserPrincipalName, [Parameter()] [ValidateSet("PercentFull", "UsedSpaceGB", "TotalQuotaGB", "DisplayName")] [string]$SortBy = "PercentFull", [Parameter()] [switch]$IncludeArchive, [Parameter( HelpMessage = "Export results to CSV file in C:\Temp folder" )] [switch]$ExportToCsv, [Parameter( HelpMessage = "Custom path for CSV export" )] [ValidateNotNullOrEmpty()] [string]$CsvPath ) begin { # Check if Exchange Online connection exists and is functional if (-not (Test-O365ExchangeConnection -Quiet)) { Write-Host "Exchange Online connection required but not found." -ForegroundColor Yellow Write-Host "Attempting to establish connection..." -ForegroundColor Cyan if (-not (Connect-O365Exchange -ShowProgress)) { Write-Error "Failed to connect to Exchange Online. Please run Connect-O365Exchange manually." return } } Write-Verbose "Exchange Online connection verified and ready" # Initialize collection for results $results = @() # Function to convert bytes to GB with proper formatting function ConvertTo-GB { param([string]$SizeString) if ([string]::IsNullOrEmpty($SizeString) -or $SizeString -eq "Unlimited") { return 0 } # Extract numeric value and convert to GB if ($SizeString -match '([\d,]+\.?\d*)\s*([KMGT]?B)') { $value = [double]($matches[1] -replace ',', '') $unit = $matches[2] switch ($unit) { "TB" { return [math]::Round($value * 1024, 2) } "GB" { return [math]::Round($value, 2) } "MB" { return [math]::Round($value / 1024, 2) } "KB" { return [math]::Round($value / 1048576, 2) } "B" { return [math]::Round($value / 1073741824, 2) } default { return [math]::Round($value, 2) } } } return 0 } # Function to process mailbox statistics function ProcessMailboxStats { param( [object]$Mailbox, [object]$Stats, [object]$ArchiveStats = $null ) # Convert sizes to GB $usedSpaceGB = ConvertTo-GB -SizeString $Stats.TotalItemSize $totalQuotaGB = ConvertTo-GB -SizeString $Stats.ProhibitSendReceiveQuota # Calculate percentage full (avoid division by zero) $percentFull = if ($totalQuotaGB -gt 0) { [math]::Round(($usedSpaceGB / $totalQuotaGB) * 100, 1) } else { 0 } # Create result object $result = [PSCustomObject]@{ DisplayName = $Mailbox.DisplayName UserPrincipalName = $Mailbox.PrimarySmtpAddress UsedSpaceGB = $usedSpaceGB TotalQuotaGB = $totalQuotaGB PercentFull = $percentFull ItemCount = $Stats.ItemCount LastLogonTime = $Stats.LastLogonTime MailboxType = $Mailbox.RecipientTypeDetails } # Add archive information if requested and available if ($IncludeArchive -and $ArchiveStats) { $archiveUsedGB = ConvertTo-GB -SizeString $ArchiveStats.TotalItemSize $archiveTotalGB = ConvertTo-GB -SizeString $ArchiveStats.ProhibitSendReceiveQuota $archivePercentFull = if ($archiveTotalGB -gt 0) { [math]::Round(($archiveUsedGB / $archiveTotalGB) * 100, 1) } else { 0 } $result | Add-Member -NotePropertyName "ArchiveUsedSpaceGB" -NotePropertyValue $archiveUsedGB $result | Add-Member -NotePropertyName "ArchiveTotalQuotaGB" -NotePropertyValue $archiveTotalGB $result | Add-Member -NotePropertyName "ArchivePercentFull" -NotePropertyValue $archivePercentFull $result | Add-Member -NotePropertyName "ArchiveItemCount" -NotePropertyValue $ArchiveStats.ItemCount } return $result } } process { try { # Determine which mailboxes to process if ($UserPrincipalName) { # Process specific users from pipeline or parameter foreach ($upn in $UserPrincipalName) { Write-Verbose "Processing mailbox: $upn" # Get mailbox information $mailbox = Get-Mailbox -Identity $upn -ErrorAction Stop # Get mailbox statistics $stats = Get-MailboxStatistics -Identity $upn -ErrorAction Stop # Get archive statistics if requested $archiveStats = $null if ($IncludeArchive -and $mailbox.ArchiveStatus -eq "Active") { try { $archiveStats = Get-MailboxStatistics -Identity $upn -Archive -ErrorAction Stop } catch { Write-Warning "Could not retrieve archive statistics for ${upn}: $($_.Exception.Message)" } } # Process and add to results $result = ProcessMailboxStats -Mailbox $mailbox -Stats $stats -ArchiveStats $archiveStats $results += $result } } else { # Get all mailboxes Write-Verbose "Retrieving all mailboxes..." $mailboxes = Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox, SharedMailbox, RoomMailbox, EquipmentMailbox $totalCount = $mailboxes.Count $currentCount = 0 foreach ($mailbox in $mailboxes) { $currentCount++ $percentComplete = [math]::Round(($currentCount / $totalCount) * 100, 0) Write-Progress -Activity "Processing mailboxes" -Status "Processing $($mailbox.DisplayName) ($currentCount of $totalCount)" -PercentComplete $percentComplete Write-Verbose "Processing mailbox: $($mailbox.PrimarySmtpAddress)" try { # Get mailbox statistics $stats = Get-MailboxStatistics -Identity $mailbox.PrimarySmtpAddress -ErrorAction Stop # Get archive statistics if requested $archiveStats = $null if ($IncludeArchive -and $mailbox.ArchiveStatus -eq "Active") { try { $archiveStats = Get-MailboxStatistics -Identity $mailbox.PrimarySmtpAddress -Archive -ErrorAction Stop } catch { Write-Warning "Could not retrieve archive statistics for $($mailbox.PrimarySmtpAddress): $($_.Exception.Message)" } } # Process and add to results $result = ProcessMailboxStats -Mailbox $mailbox -Stats $stats -ArchiveStats $archiveStats $results += $result } catch { Write-Warning "Could not retrieve statistics for $($mailbox.PrimarySmtpAddress): $($_.Exception.Message)" } } Write-Progress -Activity "Processing mailboxes" -Completed } } catch { Write-Error "Error processing mailbox statistics: $($_.Exception.Message)" return } } end { if ($results.Count -eq 0) { Write-Warning "No mailbox statistics retrieved." return } Write-Verbose "Retrieved statistics for $($results.Count) mailboxes" # Sort results based on specified property switch ($SortBy) { "PercentFull" { $sortedResults = $results | Sort-Object PercentFull -Descending } "UsedSpaceGB" { $sortedResults = $results | Sort-Object UsedSpaceGB -Descending } "TotalQuotaGB" { $sortedResults = $results | Sort-Object TotalQuotaGB -Descending } "DisplayName" { $sortedResults = $results | Sort-Object DisplayName } default { $sortedResults = $results | Sort-Object PercentFull -Descending } } # Output results to pipeline Write-Output $sortedResults # Export to CSV if requested if ($ExportToCsv -or $CsvPath) { try { # Determine CSV path if ($CsvPath) { $csvFilePath = $CsvPath # Ensure directory exists $csvDirectory = Split-Path $csvFilePath -Parent if (-not (Test-Path $csvDirectory)) { New-Item -Path $csvDirectory -ItemType Directory -Force | Out-Null } } else { # Create C:\Temp if it doesn't exist if (-not (Test-Path "C:\Temp")) { New-Item -Path "C:\Temp" -ItemType Directory -Force | Out-Null } # Generate timestamped filename $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $csvFilePath = "C:\Temp\O365MailboxUsage_$timestamp.csv" } # Export to CSV $sortedResults | Export-Csv -Path $csvFilePath -NoTypeInformation -Encoding UTF8 Write-Host "Results exported to: $csvFilePath" -ForegroundColor Green Write-Host "Total records exported: $($sortedResults.Count)" -ForegroundColor Cyan # Show file size $fileInfo = Get-Item $csvFilePath $fileSizeMB = [math]::Round($fileInfo.Length / 1MB, 2) Write-Host "File size: $fileSizeMB MB" -ForegroundColor Cyan } catch { Write-Warning "Failed to export to CSV: $($_.Exception.Message)" } } } } |