# Define path for HTML report $outputFile = "$env:USERPROFILE\Downloads\SharedMailboxesReport_Details.html" # Define path for server address and credentials $serverAddressFile = ".\DefaultServer.txt" $credentialFile = ".\AdminCredential.xml" # --- Helper function to format mailbox size --- Function Format-MailboxSize { param( [Parameter(Mandatory=$true)] $SizeObject ) if ($null -eq $SizeObject) { return "N/A" } # Example SizeObject format: "1.23 GB (1,320,155,136 bytes)" # Extract the part before the parenthesis $sizeString = $SizeObject.ToString().Split('(')[0].Trim() # If it's just bytes, format it if ($sizeString -like "*bytes") { $bytes = $SizeObject.ToBytes() if ($bytes -gt 1GB) { return ("{0:N2} GB" -f ($bytes / 1GB)) } if ($bytes -gt 1MB) { return ("{0:N2} MB" -f ($bytes / 1MB)) } if ($bytes -gt 1KB) { return ("{0:N2} KB" -f ($bytes / 1KB)) } return ("{0} Bytes" -f $bytes) } else { # Return the already formatted part (e.g., "1.23 GB") return $sizeString } } # --- HTML Styling (CSS) --- $cssStyle = @" "@ # --- Script Logic --- $session = $null # Initialize session variable # --- Initialize Report Body with Title and Descriptions --- $reportTitle = "

Shared Mailboxes Report

" $reportDescription = @"

This report lists all shared mailboxes, grouped by their mailbox database.

It includes the mailbox size and the last time the mailbox was accessed (LastLogonTime).

Mailboxes listed with a 'Last Logon' date of Never have potentially never been accessed or logon auditing was not enabled when they might have been accessed.

"@ $reportBody = $reportTitle + $reportDescription try { # --- Input File Validation --- if (-not (Test-Path $serverAddressFile)) { throw "Server address file not found: $serverAddressFile" } if (-not (Test-Path $credentialFile)) { throw "Credential file not found: $credentialFile" } # Import admin credentials $serverAddress = Get-Content -Path $serverAddressFile $credential = Import-Clixml -Path $credentialFile # Establish session with Exchange Server Write-Host "Connecting to Exchange Server: $serverAddress..." $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$serverAddress/PowerShell/" -Authentication Kerberos -Credential $credential -ErrorAction Stop Import-PSSession $session -DisableNameChecking -ErrorAction Stop Write-Host "Successfully connected to Exchange." -ForegroundColor Green # --- Data Retrieval and Processing --- Write-Host "Retrieving mailbox databases..." # Retrieve all non-recovery databases $databases = Get-MailboxDatabase -Status | Where-Object {$_.Recovery -ne $true} | Select-Object Name, Identity # Check database count *before* loop if (-not $databases -or $databases.Count -eq 0) { throw "No non-recovery mailbox databases found. Cannot generate report." } $totalDbCount = $databases.Count Write-Host "Found $totalDbCount databases. Processing mailboxes for each database..." $dbProcessed = 0 foreach ($db in $databases) { $dbProcessed++ # *** ADDED SAFEGUARD FOR Write-Progress calculation *** $percentComplete = 0 # Default value if ($totalDbCount -gt 0) { $percentComplete = [math]::Floor(($dbProcessed / $totalDbCount) * 100) } # Now safe to call Write-Progress Write-Progress -Activity "Processing Databases" -Status "Processing DB: $($db.Name) ($dbProcessed of $totalDbCount)" -PercentComplete $percentComplete $dbName = $db.Name # Add Database heading to report $reportBody += "

Database: $dbName

" $sharedMailboxesInDb = @() $reportDataForDb = @() try { # Get only SharedMailboxes directly from this DB Write-Host " - Retrieving shared mailboxes for database '$dbName'..." $sharedMailboxesInDb = Get-Mailbox -Database $db.Identity -RecipientTypeDetails SharedMailbox -ResultSize Unlimited -ErrorAction Stop if ($sharedMailboxesInDb.Count -gt 0) { Write-Host " Found $($sharedMailboxesInDb.Count) shared mailboxes. Getting statistics..." # Get stats for all found mailboxes in this DB in one go (more efficient) $mailboxStats = $sharedMailboxesInDb | Get-MailboxStatistics -ErrorAction SilentlyContinue # Continue if stats fail for one mailbox # Create a hashtable for quick stats lookup $statsHashTable = @{} $mailboxStats | ForEach-Object { $statsHashTable[$_.MailboxGuid] = $_ } # Process each mailbox and combine with its stats foreach ($mb in $sharedMailboxesInDb) { $currentStats = $statsHashTable[$mb.ExchangeGuid] $formattedSize = "N/A" $lastLogon = $null if ($currentStats) { $formattedSize = Format-MailboxSize -SizeObject $currentStats.TotalItemSize $lastLogon = $currentStats.LastLogonTime } else { Write-Warning "Could not retrieve statistics for $($mb.PrimarySmtpAddress) in database $dbName." } $formattedLastLogon = if ($lastLogon) { $lastLogon.ToString('yyyy-MM-dd HH:mm:ss') } else { "Never" } $reportDataForDb += [PSCustomObject]@{ DisplayName = $mb.DisplayName PrimarySmtpAddress = $mb.PrimarySmtpAddress Size = $formattedSize LastLogon = $formattedLastLogon # Store the formatted string } } # End foreach mailbox in DB # Generate HTML table for this database if data exists if ($reportDataForDb.Count -gt 0) { Write-Host " Generating HTML table for '$dbName'..." $htmlTable = $reportDataForDb | Sort-Object DisplayName | ConvertTo-Html -Fragment -Property DisplayName, PrimarySmtpAddress, Size, @{Name='Last Logon Time'; Expression={$_.LastLogon}} $reportBody += [System.Net.WebUtility]::HtmlDecode($htmlTable) # Decode the 'Never' span } else { # This case should be rare if $sharedMailboxesInDb.Count was > 0, unless all stats failed $reportBody += "

No shared mailbox data processed for this database (potential issue retrieving statistics).

" } } else { Write-Host " No shared mailboxes found in database '$dbName'." $reportBody += "

No shared mailboxes found in this database.

" } } catch { Write-Warning "An error occurred processing database '$dbName': $($_.Exception.Message)" $reportBody += "

An error occurred while processing mailboxes for database '$dbName'. Some mailboxes may be missing. Error: $($_.Exception.Message)

" } # End try-catch for DB processing } # End foreach database Write-Progress -Activity "Processing Databases" -Completed $reportBody += "

Report generation complete.

" } catch { Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red # Append error details to the report body (which already has title/description) $reportBody += "

Script Execution Error

" $reportBody += "

An error occurred while generating the shared mailbox report.

" $reportBody += "

Error Details:
$([System.Net.WebUtility]::HtmlEncode($_.Exception.Message))

" if ($_.Exception.StackTrace) { $reportBody += "

Stack Trace:
$([System.Net.WebUtility]::HtmlEncode($_.Exception.StackTrace) -replace "`n", "
")

" } if ($_.InvocationInfo) { $reportBody += "

Script Line: $($_.InvocationInfo.ScriptLineNumber)

" } } finally { # --- Generate Final HTML Report --- $reportFooter = "

Report generated on: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') by $($env:USERNAME)

" # Assemble the full HTML page ConvertTo-Html -Head $cssStyle ` -Title "Shared Mailboxes Report by Database" ` -Body $reportBody ` -PostContent $reportFooter | Out-File $outputFile -Encoding UTF8 -ErrorAction SilentlyContinue # Check if the file was actually created before trying to open it if (Test-Path $outputFile) { Write-Host "HTML Report saved to: $outputFile" -ForegroundColor Green Invoke-Item -Path $outputFile } else { Write-Host "Failed to create the report file at: $outputFile" -ForegroundColor Red } # Clean up Exchange session if ($session -ne $null) { Write-Host "Removing Exchange PSSession..." Remove-PSSession $session Write-Host "Session removed." } }