Public/NC.Statistics.ps1
|
#Requires -Version 5.0 using namespace System.Management.Automation # Nebula.Core: Statistics helpers =================================================================================================================== function Export-MboxStatistics { <# .SYNOPSIS Exports mailbox (and archive) size/quota statistics. .DESCRIPTION Ensures an Exchange Online session, retrieves either all mailboxes or a single identity, calculates usage/quota information (optionally rounding quotas), and writes to CSV or returns objects to the pipeline. .PARAMETER UserPrincipalName Optional single mailbox identity. When omitted, exports all mailboxes to CSV. .PARAMETER CsvFolder Destination folder for the CSV file (defaults to current directory when exporting all mailboxes). .PARAMETER Round Round quota values up to the nearest integer GB. .PARAMETER BatchSize Number of processed mailboxes before flushing partial CSV output (defaults to 25). .PARAMETER Resume Resume from the most recent existing mailbox statistics CSV in the target folder. .PARAMETER CsvPath Optional CSV file to resume. When omitted, the most recent matching CSV in the target folder is used. .PARAMETER MaxConsecutiveErrors Stop the export after this many mailbox-level failures in a row. #> [CmdletBinding()] param( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('User', 'Identity')] [string]$UserPrincipalName, [string]$CsvFolder, [string]$CsvPath, [switch]$Round, [ValidateRange(1, 500)] [int]$BatchSize = 25, [switch]$Resume, [ValidateRange(1, 100)] [int]$MaxConsecutiveErrors = 5 ) Set-ProgressAndInfoPreferences try { if (-not (Test-EOLConnection)) { Add-EmptyLine Write-NCMessage "Can't connect or use Microsoft Exchange Online Management module. Please check logs." -Level ERROR return } $exportAll = [string]::IsNullOrWhiteSpace($UserPrincipalName) $mailboxes = @() try { if ($exportAll) { $mailboxes = Get-Mailbox -ResultSize Unlimited -WarningAction SilentlyContinue } else { $mailboxes = @(Get-Mailbox -Identity $UserPrincipalName -ErrorAction Stop) } } catch { Write-NCMessage "Failed to retrieve mailbox information: $($_.Exception.Message)" -Level ERROR return } if (-not $mailboxes -or $mailboxes.Count -eq 0) { Write-NCMessage "No mailboxes matched the provided criteria." -Level WARNING return } $folder = if ($CsvFolder) { Test-Folder $CsvFolder } else { Test-Folder $null } $statsBuffer = New-Object System.Collections.Generic.List[object] $processedCount = 0 $writeToCsv = $exportAll $csvPath = $null $csvInitialized = $false $failedInARow = 0 $aborted = $false $normalizeIdentity = { param([object]$Value) if ($null -eq $Value) { return $null } $text = [string]$Value if ([string]::IsNullOrWhiteSpace($text)) { return $null } return $text.Trim().ToLowerInvariant() } $processedIdentities = [System.Collections.Generic.HashSet[string]]::new() $pendingMailboxes = [System.Collections.Generic.List[object]]::new() if ($writeToCsv) { $defaultCsvPath = New-File("$($folder)\$((Get-Date -Format $NCVars.DateTimeString_CSV))_M365-MailboxStatistics.csv") if ($Resume) { $resumePath = $null if (-not [string]::IsNullOrWhiteSpace($CsvPath)) { $resumePath = $CsvPath } else { $existingCsv = Get-ChildItem -LiteralPath $folder -File -Filter "*_M365-MailboxStatistics.csv" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if ($existingCsv) { $resumePath = $existingCsv.FullName } } if ($resumePath) { $csvPath = $resumePath if (Test-Path -LiteralPath $csvPath) { $csvInitialized = ((Get-Item -LiteralPath $csvPath).Length -gt 0) try { foreach ($row in (Import-CSV -LiteralPath $csvPath -Delimiter $NCVars.CSV_DefaultLimiter -ErrorAction Stop)) { $identity = & $normalizeIdentity $row.UserPrincipalName if (-not $identity) { $identity = & $normalizeIdentity $row.PrimarySmtpAddress } if (-not $identity) { $identity = & $normalizeIdentity $row.UserName } if ($identity) { $null = $processedIdentities.Add($identity) } } } catch { Write-NCMessage ("Unable to read existing CSV '{0}' for resume. {1}" -f $csvPath, $_.Exception.Message) -Level WARNING $processedIdentities.Clear() $csvPath = $defaultCsvPath $csvInitialized = $false } if ($csvPath -ne $defaultCsvPath) { Write-NCMessage ("Resuming mailbox statistics from {0}; {1} mailbox(es) already recorded." -f $csvPath, $processedIdentities.Count) -Level INFO } } else { Write-NCMessage ("Resume requested for '{0}', but the file does not exist. Starting a new report at that path." -f $csvPath) -Level INFO } } else { $csvPath = $defaultCsvPath Write-NCMessage ("Resume requested, but no existing CSV was found. Starting a new report at {0}." -f $csvPath) -Level INFO } } else { $csvPath = $defaultCsvPath } Write-NCMessage ("Mailbox statistics export will flush every {0} mailbox(es). Resume: {1}. Stop after {2} consecutive error(s)." -f $BatchSize, $Resume.IsPresent, $MaxConsecutiveErrors) -Level INFO Write-NCMessage "Saving report to $csvPath" -Level DEBUG foreach ($mailbox in $mailboxes) { $identity = & $normalizeIdentity $mailbox.UserPrincipalName if (-not $identity) { $identity = & $normalizeIdentity $mailbox.PrimarySmtpAddress } if ($Resume -and $identity -and $processedIdentities.Contains($identity)) { continue } $null = $pendingMailboxes.Add($mailbox) } } else { foreach ($mailbox in $mailboxes) { $null = $pendingMailboxes.Add($mailbox) } } $totalMailboxes = $pendingMailboxes.Count if ($writeToCsv -and $Resume -and $csvPath -and $processedIdentities.Count -gt 0 -and $totalMailboxes -eq 0) { Write-NCMessage "All matching mailboxes are already present in the CSV. Nothing to do." -Level WARNING return } foreach ($mailbox in $pendingMailboxes) { $processedCount++ $Percentage = Get-NCProgressPercent -Current $processedCount -Total $totalMailboxes Write-Progress -Activity "Processing $($mailbox.DisplayName)" -Status "$processedCount of $totalMailboxes - $Percentage%" -PercentComplete $Percentage $mailboxHadError = $false $stats = Get-MailboxStatisticsSafe -Identity $mailbox.UserPrincipalName if ($null -eq $stats) { $mailboxHadError = $true } $mailboxSizeGb = if ($stats) { Convert-MbxSizeToGB -SizeObject $stats.TotalItemSize } else { "Error" } $hasArchive = ($mailbox.ArchiveStatus -eq 'Active') -or ($mailbox.ArchiveGuid -and $mailbox.ArchiveGuid -ne [guid]::Empty) $archiveSize = $null if ($hasArchive) { $archiveStats = Get-MailboxStatisticsSafe -Identity $mailbox.UserPrincipalName -Archive if ($null -eq $archiveStats) { $mailboxHadError = $true } $archiveSize = if ($archiveStats) { Convert-MbxSizeToGB -SizeObject $archiveStats.TotalItemSize } else { "Error" } } $record = [pscustomobject][ordered]@{ UserPrincipalName = $mailbox.UserPrincipalName UserName = $mailbox.DisplayName ServerName = $mailbox.ServerName Database = $mailbox.Database RecipientTypeDetails = $mailbox.RecipientTypeDetails PrimarySmtpAddress = $mailbox.PrimarySmtpAddress "Mailbox Size (GB)" = $mailboxSizeGb "Issue Warning Quota (GB)" = Resolve-MbxQuotaValue -RawValue $mailbox.IssueWarningQuota -Round:$Round "Prohibit Send Quota (GB)" = Resolve-MbxQuotaValue -RawValue $mailbox.ProhibitSendQuota -Round:$Round "Archive Database" = if ($mailbox.ArchiveDatabase) { $mailbox.ArchiveDatabase } else { $null } "Archive Name" = if ($hasArchive) { $mailbox.ArchiveName } else { $null } "Archive State" = if ($hasArchive) { $mailbox.ArchiveState } else { $null } "Archive Mailbox Size (GB)" = $archiveSize "Archive Warning Quota (GB)" = if ($hasArchive) { Resolve-MbxQuotaValue -RawValue $mailbox.ArchiveWarningQuota -Round:$Round } else { $null } "Archive Quota (GB)" = if ($hasArchive) { Resolve-MbxQuotaValue -RawValue $mailbox.ArchiveQuota -Round:$Round } else { $null } AutoExpandingArchiveEnabled = $mailbox.AutoExpandingArchiveEnabled } $statsBuffer.Add($record) | Out-Null if ($mailboxHadError) { $failedInARow++ } else { $failedInARow = 0 } if ($MaxConsecutiveErrors -gt 0 -and $failedInARow -ge $MaxConsecutiveErrors) { if ($writeToCsv -and $statsBuffer.Count -gt 0) { if ($csvInitialized) { $statsBuffer | Export-CSV -LiteralPath $csvPath -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $($NCVars.CSV_DefaultLimiter) -Append } else { $statsBuffer | Export-CSV -LiteralPath $csvPath -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $($NCVars.CSV_DefaultLimiter) $csvInitialized = $true } $statsBuffer.Clear() } Write-NCMessage ("Stopping export after {0} consecutive mailbox error(s). Partial report kept at {1}." -f $failedInARow, $csvPath) -Level ERROR $aborted = $true break } if ($writeToCsv -and (($processedCount % $BatchSize) -eq 0)) { if ($csvInitialized) { $statsBuffer | Export-CSV -LiteralPath $csvPath -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $($NCVars.CSV_DefaultLimiter) -Append } else { $statsBuffer | Export-CSV -LiteralPath $csvPath -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $($NCVars.CSV_DefaultLimiter) $csvInitialized = $true } Write-Verbose "Processed $processedCount / $totalMailboxes mailboxes, flushed batch to CSV." $statsBuffer.Clear() } } if ($writeToCsv -and -not $aborted) { if ($statsBuffer.Count -gt 0) { if ($csvInitialized) { $statsBuffer | Export-CSV -LiteralPath $csvPath -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $($NCVars.CSV_DefaultLimiter) -Append } else { $statsBuffer | Export-CSV -LiteralPath $csvPath -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $($NCVars.CSV_DefaultLimiter) } } Write-NCMessage "Mailbox statistics exported to $csvPath." -Level SUCCESS } elseif ($aborted) { if ($statsBuffer.Count -gt 0) { if ($csvInitialized) { $statsBuffer | Export-CSV -LiteralPath $csvPath -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $($NCVars.CSV_DefaultLimiter) -Append } else { $statsBuffer | Export-CSV -LiteralPath $csvPath -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $($NCVars.CSV_DefaultLimiter) } } } else { $statsBuffer } Write-Progress -Activity "Export complete" -Completed } finally { Restore-ProgressAndInfoPreferences } } function Export-MboxDeletedItemSize { <# .SYNOPSIS Exports mailbox deleted item store usage. .DESCRIPTION Ensures an Exchange Online session, retrieves all user mailboxes or a selected subset, calculates the deleted item size for each mailbox, and exports the report to CSV by default. .PARAMETER UserPrincipalName Optional mailbox identity or identities. Accepts pipeline input. .PARAMETER CsvFolder Destination folder for the CSV file when exporting the report. .PARAMETER Csv When present, export the report to CSV. Defaults to on. .PARAMETER BatchSize Number of processed mailboxes before flushing partial CSV output. .PARAMETER Resume Resume from the latest matching CSV in the target folder or from -CsvPath. .PARAMETER CsvPath Explicit CSV file to resume. When omitted, the most recent matching CSV in the target folder is used. .PARAMETER MaxConsecutiveErrors Stop after this many consecutive mailbox-level failures. #> [CmdletBinding()] param( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('User', 'Identity', 'Mailbox', 'SourceMailbox')] [string[]]$UserPrincipalName, [string]$CsvFolder, [bool]$Csv = $true, [ValidateRange(1, 500)] [int]$BatchSize = 25, [switch]$Resume, [string]$CsvPath, [ValidateRange(1, 100)] [int]$MaxConsecutiveErrors = 5 ) begin { Set-ProgressAndInfoPreferences $requestedMailboxes = [System.Collections.Generic.List[string]]::new() $report = [System.Collections.Generic.List[object]]::new() $processedSinceFlush = 0 $consecutiveErrors = 0 $aborted = $false } process { foreach ($entry in $UserPrincipalName) { if (-not [string]::IsNullOrWhiteSpace($entry)) { $requestedMailboxes.Add($entry.Trim()) | Out-Null } } } end { try { if (-not (Test-EOLConnection)) { Add-EmptyLine Write-NCMessage "Can't connect or use Microsoft Exchange Online Management module. Please check logs." -Level ERROR return } $mailboxes = @() if ($requestedMailboxes.Count -gt 0) { foreach ($mailboxId in ($requestedMailboxes | Select-Object -Unique)) { try { $mailboxes += @(Get-Mailbox -Identity $mailboxId -ErrorAction Stop) } catch { Write-NCMessage "Mailbox '$mailboxId' not found. $($_.Exception.Message)" -Level WARNING } } } else { $mailboxes = @(Get-Mailbox -ResultSize Unlimited -WarningAction SilentlyContinue | Where-Object { $_.RecipientTypeDetails -eq 'UserMailbox' }) } if (-not $mailboxes -or $mailboxes.Count -eq 0) { Write-NCMessage "No user mailboxes matched the provided criteria." -Level WARNING return } $folder = if ($CsvFolder) { Test-Folder $CsvFolder } else { Test-Folder $null } $defaultCsvPath = New-File "$folder\$((Get-Date -Format $NCVars.DateTimeString_CSV))_M365-DeletedItemSize.csv" $csv = $defaultCsvPath $processedMailboxes = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) if ($Resume) { $resumePath = $null if (-not [string]::IsNullOrWhiteSpace($CsvPath)) { $resumePath = $CsvPath } else { $existingCsv = Get-ChildItem -LiteralPath $folder -File -Filter "*_M365-DeletedItemSize.csv" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if ($existingCsv) { $resumePath = $existingCsv.FullName } } if ($resumePath) { $csv = $resumePath if (Test-Path -LiteralPath $csv) { try { foreach ($row in (Import-CSV -LiteralPath $csv -Delimiter $NCVars.CSV_DefaultLimiter -ErrorAction Stop)) { if ($row.UserPrincipalName) { $null = $processedMailboxes.Add(([string]$row.UserPrincipalName).Trim()) } } Write-NCMessage ("Resuming deleted item export from {0}; {1} mailbox(es) already recorded." -f $csv, $processedMailboxes.Count) -Level INFO } catch { Write-NCMessage ("Unable to read existing CSV '{0}' for resume. {1}" -f $csv, $_.Exception.Message) -Level WARNING $processedMailboxes.Clear() $csv = $defaultCsvPath } } else { Write-NCMessage ("Resume requested for '{0}', but the file does not exist. Starting a new report at that path." -f $csv) -Level INFO } } else { Write-NCMessage ("Resume requested, but no existing CSV was found. Starting a new report at {0}." -f $csv) -Level INFO } } Write-NCMessage ("Deleted item export will flush every {0} mailbox(es). Resume: {1}. Stop after {2} consecutive error(s)." -f $BatchSize, $Resume.IsPresent, $MaxConsecutiveErrors) -Level INFO Write-NCMessage "Saving report to $csv" -Level DEBUG $totalMailboxes = $mailboxes.Count $processedCount = 0 foreach ($mailbox in $mailboxes) { $processedCount++ $Percentage = Get-NCProgressPercent -Current $processedCount -Total $totalMailboxes Write-Progress -Activity "Processing $($mailbox.DisplayName)" -Status "$processedCount of $totalMailboxes - $Percentage%" -PercentComplete $Percentage if ($Resume -and $mailbox.UserPrincipalName -and $processedMailboxes.Contains($mailbox.UserPrincipalName)) { Write-Verbose "Skipping $($mailbox.UserPrincipalName), already processed." continue } $stats = Get-MailboxStatisticsSafe -Identity $mailbox.UserPrincipalName if (-not $stats) { $consecutiveErrors++ if ($MaxConsecutiveErrors -gt 0 -and $consecutiveErrors -ge $MaxConsecutiveErrors) { if ($report.Count -gt 0) { if ((Test-Path -LiteralPath $csv) -and ((Get-Item -LiteralPath $csv).Length -gt 0)) { $report | Export-Csv -LiteralPath $csv -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $NCVars.CSV_DefaultLimiter -Append } else { $report | Export-Csv -LiteralPath $csv -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $NCVars.CSV_DefaultLimiter } $report.Clear() } Write-NCMessage ("Stopping export after {0} consecutive mailbox error(s). Partial report kept at {1}." -f $consecutiveErrors, $csv) -Level ERROR $aborted = $true break } continue } $report.Add([pscustomobject][ordered]@{ UserPrincipalName = $mailbox.UserPrincipalName DisplayName = $mailbox.DisplayName PrimarySmtpAddress = $mailbox.PrimarySmtpAddress TotalDeletedItemSizeGB = Convert-MbxSizeToGB -SizeObject $stats.TotalDeletedItemSize }) | Out-Null $null = $processedMailboxes.Add($mailbox.UserPrincipalName) $consecutiveErrors = 0 $processedSinceFlush++ if ($processedSinceFlush -ge $BatchSize -and $report.Count -gt 0) { if ((Test-Path -LiteralPath $csv) -and ((Get-Item -LiteralPath $csv).Length -gt 0)) { $report | Export-Csv -LiteralPath $csv -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $NCVars.CSV_DefaultLimiter -Append } else { $report | Export-Csv -LiteralPath $csv -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $NCVars.CSV_DefaultLimiter } Write-Verbose "Processed $processedCount / $totalMailboxes mailboxes, flushed batch to CSV." $report.Clear() $processedSinceFlush = 0 } } if ($Csv -and $report.Count -gt 0) { if ((Test-Path -LiteralPath $csv) -and ((Get-Item -LiteralPath $csv).Length -gt 0)) { $report | Export-Csv -LiteralPath $csv -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $NCVars.CSV_DefaultLimiter -Append } else { $report | Export-Csv -LiteralPath $csv -NoTypeInformation -Encoding $NCVars.CSV_Encoding -Delimiter $NCVars.CSV_DefaultLimiter } } if ($Csv) { if ($aborted) { Write-NCMessage "Deleted item size report export stopped early. Partial data kept at $csv." -Level ERROR } else { Write-NCMessage "Deleted item size report exported to $csv." -Level SUCCESS } } else { $report } } finally { Write-Progress -Activity "Processing deleted item size" -Completed Restore-ProgressAndInfoPreferences } } } function Get-MboxStatistics { <# .SYNOPSIS Returns simplified mailbox statistics. .DESCRIPTION Ensures an Exchange Online session, retrieves mailbox statistics and returns a concise set of key fields (size, quotas, basic usage info, latest message trace, and oldest mailbox item metadata). .PARAMETER UserPrincipalName Optional single mailbox identity. When omitted, returns all mailboxes. .PARAMETER IncludeArchive When present, includes archive size, archive quota, and archive usage percentage (if available). .PARAMETER IncludeMessageActivity When present, includes latest message trace info and oldest mailbox item metadata (LastReceived, LastSent, OldestItemReceivedDate, OldestItemFolderPath). .PARAMETER Round Round quota values up to the nearest integer GB (default: $true). #> [CmdletBinding()] param( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('User', 'Identity')] [string]$UserPrincipalName, [switch]$IncludeArchive, [switch]$IncludeMessageActivity, [bool]$Round = $true ) begin { Set-ProgressAndInfoPreferences $pipelineUpns = [System.Collections.Generic.List[string]]::new() } process { if (-not [string]::IsNullOrWhiteSpace($UserPrincipalName)) { [void]$pipelineUpns.Add($UserPrincipalName) } } end { try { if (-not (Test-EOLConnection)) { Add-EmptyLine Write-NCMessage "Can't connect or use Microsoft Exchange Online Management module. Please check logs." -Level ERROR return } $mailboxes = @() try { if ($pipelineUpns.Count -eq 0) { $mailboxes = Get-Mailbox -ResultSize Unlimited -WarningAction SilentlyContinue } else { foreach ($upn in ($pipelineUpns | Select-Object -Unique)) { try { $mailboxes += @(Get-Mailbox -Identity $upn -ErrorAction Stop) } catch { Write-NCMessage "Mailbox not found for '$upn'. Skipping. $($_.Exception.Message)" -Level WARNING } } } } catch { Write-NCMessage "Failed to retrieve mailbox information: $($_.Exception.Message)" -Level ERROR return } if (-not $mailboxes -or $mailboxes.Count -eq 0) { Write-NCMessage "No mailboxes matched the provided criteria." -Level WARNING return } $processedCount = 0 $totalMailboxes = $mailboxes.Count foreach ($mailbox in $mailboxes) { $processedCount++ $Percentage = Get-NCProgressPercent -Current $processedCount -Total $totalMailboxes Write-Progress -Activity "Processing $($mailbox.DisplayName)" -Status "$processedCount of $totalMailboxes - $Percentage%" -PercentComplete $Percentage $stats = Get-MailboxStatisticsSafe -Identity $mailbox.UserPrincipalName if (-not $stats) { continue } $mailboxSizeGb = Convert-MbxSizeToGB -SizeObject $stats.TotalItemSize $prohibitSendQuota = Resolve-MbxQuotaValue -RawValue $mailbox.ProhibitSendQuota -Round:$Round $warningQuota = Resolve-MbxQuotaValue -RawValue $mailbox.IssueWarningQuota -Round:$Round $oldestItemReceivedDate = $null $oldestItemFolderPath = $null $lastTrace = $null if ($IncludeMessageActivity) { $lastTrace = Get-MboxLastMessageTrace -SourceMailbox $mailbox.UserPrincipalName try { $oldestItem = Get-MailboxFolderStatistics -Identity $mailbox.UserPrincipalName -IncludeOldestAndNewestItems -ErrorAction Stop | Where-Object { $null -ne $_.OldestItemReceivedDate } | Sort-Object -Property OldestItemReceivedDate | Select-Object -First 1 if ($oldestItem) { $oldestItemReceivedDate = $oldestItem.OldestItemReceivedDate $oldestItemFolderPath = $oldestItem.FolderPath } } catch { Write-NCMessage ("Unable to retrieve oldest mailbox item details for '{0}'. {1}" -f $mailbox.PrimarySmtpAddress, $_.Exception.Message) -Level WARNING } } $percentUsed = $null if ($prohibitSendQuota -is [double] -and $prohibitSendQuota -gt 0) { $percentUsed = [Math]::Round(($mailboxSizeGb / $prohibitSendQuota) * 100, 2) } $archiveSize = $null $archivePercentUsed = $null $archiveQuota = $null $hasArchive = ($mailbox.ArchiveStatus -eq 'Active') -or ($mailbox.ArchiveGuid -and $mailbox.ArchiveGuid -ne [guid]::Empty) if ($IncludeArchive -and $hasArchive) { $archiveQuota = Resolve-MbxQuotaValue -RawValue $mailbox.ArchiveQuota -Round:$Round $archiveStats = Get-MailboxStatisticsSafe -Identity $mailbox.UserPrincipalName -Archive if ($archiveStats) { $archiveSize = Convert-MbxSizeToGB -SizeObject $archiveStats.TotalItemSize if ($archiveQuota -is [double] -and $archiveQuota -gt 0) { $archivePercentUsed = [Math]::Round(($archiveSize / $archiveQuota) * 100, 2) } } } $mailboxTypeDetail = if ($stats.PSObject.Properties.Match('MailboxTypeDetail').Count -gt 0) { $stats.MailboxTypeDetail } elseif ($stats.PSObject.Properties.Match('RecipientTypeDetails').Count -gt 0) { $stats.RecipientTypeDetails } else { $mailbox.RecipientTypeDetails } $mailboxCreated = $null if ($mailbox.PSObject.Properties.Match('WhenCreatedUTC').Count -gt 0 -and $mailbox.WhenCreatedUTC) { $mailboxCreated = $mailbox.WhenCreatedUTC } elseif ($mailbox.PSObject.Properties.Match('WhenCreated').Count -gt 0 -and $mailbox.WhenCreated) { $mailboxCreated = $mailbox.WhenCreated } elseif ($stats.PSObject.Properties.Match('WhenMailboxCreated').Count -gt 0 -and $stats.WhenMailboxCreated) { $mailboxCreated = $stats.WhenMailboxCreated } elseif ($stats.PSObject.Properties.Match('DateCreated').Count -gt 0 -and $stats.DateCreated) { $mailboxCreated = $stats.DateCreated } elseif ($stats.PSObject.Properties.Match('Created').Count -gt 0 -and $stats.Created) { $mailboxCreated = $stats.Created } $record = [ordered]@{ DisplayName = $mailbox.DisplayName UserPrincipalName = $mailbox.UserPrincipalName PrimarySmtpAddress = $mailbox.PrimarySmtpAddress MailboxTypeDetail = $mailboxTypeDetail ArchiveEnabled = [bool]$hasArchive MailboxSizeGB = $mailboxSizeGb ItemCount = $stats.ItemCount MailboxCreated = $mailboxCreated LastLogonTime = $stats.LastLogonTime WarningQuotaGB = $warningQuota ProhibitSendQuotaGB = $prohibitSendQuota PercentUsed = $percentUsed } if ($IncludeMessageActivity) { $record.LastReceived = if ($lastTrace) { $lastTrace.LastReceived } else { $null } $record.LastSent = if ($lastTrace) { $lastTrace.LastSent } else { $null } $record.OldestItemReceivedDate = $oldestItemReceivedDate $record.OldestItemFolderPath = $oldestItemFolderPath } if ($IncludeArchive) { $record.ArchiveSizeGB = $archiveSize $record.ArchiveQuotaGB = $archiveQuota $record.ArchivePercentUsed = $archivePercentUsed } [pscustomobject]$record } Write-Progress -Activity "Export complete" -Completed } finally { Restore-ProgressAndInfoPreferences } } } |