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.
     
    .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
        Get-O365MailboxUsage -SortBy UsedSpaceGB -IncludeArchive
        Returns all mailboxes sorted by used space including archive statistics.
     
    .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
    )
    
    begin {
        # Check if Exchange Online module is available and connected
        try {
            $connectionStatus = Get-ConnectionInformation -ErrorAction Stop
            if (-not $connectionStatus) {
                throw "Not connected to Exchange Online"
            }
        }
        catch {
            Write-Error "Exchange Online connection required. Please run Connect-ExchangeOnline first."
            return
        }
        
        Write-Verbose "Connected to Exchange Online: $($connectionStatus.Name)"
        
        # 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
        $stamp = Get-Date -Format 'yyyyMMdd_HHmmss'
        export-csv $sortedResults -Path "C:\Temp\$stamp-O365MailboxUsage.csv" -NoTypeInformation -Encoding UTF8
    }
}

Export-ModuleMember -Function Get-O365MailboxUsage