# 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."
}
}