Files
PowerShell-scripts/Exchange/Compare-MailboxDatabases.ps1
Martien de Kleijn 62134801aa Add 12 AI-generated PowerShell scripts with documentation
⚠️ IMPORTANT: These scripts are AI-GENERATED and UNTESTED

Exchange Scripts (5):
- Get-MailboxPermissions.ps1: Audit delegate access permissions
- Get-InactiveMailboxes.ps1: Identify stale mailboxes
- Compare-MailboxDatabases.ps1: Database health comparison
- Export-DistributionGroups.ps1: Distribution group inventory
- Get-MailflowStats.ps1: Transport log analysis

Active Directory Scripts (3):
- Get-ADUserLastLogon.ps1: True LastLogon across all DCs
- Export-OUStructure.ps1: OU hierarchy with GPO links
- Compare-ADGroupMemberships.ps1: Compare user group memberships

System Maintenance Scripts (4):
- Get-ServerInventory.ps1: Hardware/software inventory report
- Monitor-DiskSpace.ps1: Disk space monitoring with alerts
- Backup-ExchangeCertificates.ps1: Certificate backup to PFX
- Test-ExchangeHealth.ps1: Aggregated Exchange health checks

Documentation:
- Updated CLAUDE.md with AI-generated scripts section
- Added AI-GENERATED-SCRIPTS.md with warnings and testing guide

All scripts include prominent warnings and follow established patterns
from existing scripts. Require thorough testing before production use.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 10:52:44 +02:00

191 lines
6.7 KiB
PowerShell

<#
.SYNOPSIS
Compare health metrics across all Exchange mailbox databases
.DESCRIPTION
Generates a comparison report of database sizes, whitespace, mount status,
backup age, and circular logging settings. Alerts on databases exceeding
thresholds or with configuration issues.
.PARAMETER WhitespaceThresholdGB
Alert if available whitespace exceeds this value (default: 50)
.PARAMETER BackupAgeThresholdDays
Alert if last backup is older than this many days (default: 2)
.PARAMETER OutputFolder
Destination folder for reports. Default: .\DatabaseComparison-<date>
.NOTES
⚠️ AI-GENERATED SCRIPT - UNTESTED
This script was generated by Claude AI and has not been tested in production.
Review and test thoroughly in a non-production environment before use.
- Run in Exchange Management Shell with appropriate RBAC permissions
- Requires -Status parameter support (Get-MailboxDatabase -Status)
- Tested compatibility: Exchange 2013/2016/2019 (not validated)
.EXAMPLE
.\Compare-MailboxDatabases.ps1
.EXAMPLE
.\Compare-MailboxDatabases.ps1 -WhitespaceThresholdGB 100 -BackupAgeThresholdDays 1
#>
[CmdletBinding()]
param(
[int]$WhitespaceThresholdGB = 50,
[int]$BackupAgeThresholdDays = 2,
[string]$OutputFolder = (Join-Path -Path (Get-Location) -ChildPath ("DatabaseComparison-" + (Get-Date -Format "yyyyMMdd-HHmm")))
)
function NowTag { (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") }
function Convert-BytesToGB([string]$sizeStr) {
if (-not $sizeStr) { return 0 }
try {
if ($sizeStr -match "([\d\.,]+)\s*(KB|MB|GB|TB)") {
$num = [double]($matches[1] -replace ',', '.')
$bytes = switch ($matches[2].ToUpper()) {
"KB" { $num * 1KB }
"MB" { $num * 1MB }
"GB" { $num * 1GB }
"TB" { $num * 1TB }
}
return [math]::Round($bytes / 1GB, 2)
}
} catch {}
return 0
}
Write-Host "[$(NowTag)] ⚠️ AI-GENERATED SCRIPT - UNTESTED" -ForegroundColor Yellow
Write-Host "[$(NowTag)] Starting database comparison analysis..." -ForegroundColor Green
# Create output folder
New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null
# Get all databases with status
Write-Host "[$(NowTag)] Retrieving mailbox databases..."
$databases = Get-MailboxDatabase -Status -ErrorAction SilentlyContinue | Sort-Object Name
$dbCount = ($databases | Measure-Object).Count
Write-Host "[$(NowTag)] Found $dbCount databases"
# Analyze each database
$dbAnalysis = @()
$alerts = @()
foreach ($db in $databases) {
Write-Host "[$(NowTag)] Analyzing $($db.Name)..."
$dbSizeGB = Convert-BytesToGB ([string]$db.DatabaseSize)
$whitespaceGB = Convert-BytesToGB ([string]$db.AvailableNewMailboxSpace)
$backupAge = $null
$backupStatus = "Unknown"
if ($db.LastFullBackup) {
$backupAge = [int]((Get-Date) - $db.LastFullBackup).TotalDays
$backupStatus = if ($backupAge -le $BackupAgeThresholdDays) { "OK" } else { "OLD" }
} else {
$backupStatus = "NEVER"
}
# Generate alerts
$dbAlerts = @()
if (-not $db.Mounted) {
$dbAlerts += "Database is DISMOUNTED"
$alerts += [PSCustomObject]@{
Database = $db.Name
Severity = "CRITICAL"
Alert = "Database is dismounted"
}
}
if ($whitespaceGB -gt $WhitespaceThresholdGB) {
$dbAlerts += "Excessive whitespace: $whitespaceGB GB"
$alerts += [PSCustomObject]@{
Database = $db.Name
Severity = "WARNING"
Alert = "Excessive whitespace: $whitespaceGB GB (threshold: $WhitespaceThresholdGB GB)"
}
}
if ($backupStatus -eq "NEVER") {
$dbAlerts += "Never backed up"
$alerts += [PSCustomObject]@{
Database = $db.Name
Severity = "CRITICAL"
Alert = "Database has never been backed up"
}
} elseif ($backupStatus -eq "OLD") {
$dbAlerts += "Backup is $backupAge days old"
$alerts += [PSCustomObject]@{
Database = $db.Name
Severity = "WARNING"
Alert = "Last backup is $backupAge days old (threshold: $BackupAgeThresholdDays days)"
}
}
if ($db.CircularLoggingEnabled) {
$dbAlerts += "Circular logging enabled"
$alerts += [PSCustomObject]@{
Database = $db.Name
Severity = "INFO"
Alert = "Circular logging is enabled"
}
}
$dbAnalysis += [PSCustomObject]@{
Database = $db.Name
Server = $db.Server
Mounted = $db.Mounted
DatabaseSizeGB = $dbSizeGB
WhitespaceGB = $whitespaceGB
WhitespacePercent = if ($dbSizeGB -gt 0) { [math]::Round(($whitespaceGB / $dbSizeGB) * 100, 2) } else { 0 }
CircularLogging = $db.CircularLoggingEnabled
LastFullBackup = $db.LastFullBackup
BackupAgeDays = $backupAge
BackupStatus = $backupStatus
EdbFilePath = $db.EdbFilePath
LogFolderPath = $db.LogFolderPath
Alerts = ($dbAlerts -join "; ")
}
}
# Export full analysis
$csvFile = Join-Path $OutputFolder "Database-Comparison.csv"
$dbAnalysis | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $csvFile
Write-Host "[$(NowTag)] Database analysis exported: $csvFile" -ForegroundColor Green
# Export alerts
if ($alerts.Count -gt 0) {
$alertsFile = Join-Path $OutputFolder "Database-Alerts.csv"
$alerts | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $alertsFile
Write-Host "[$(NowTag)] Alerts exported: $alertsFile" -ForegroundColor Yellow
Write-Host "`nALERTS FOUND:" -ForegroundColor Yellow
$alerts | Group-Object Severity | ForEach-Object {
Write-Host " $($_.Name): $($_.Count) alert(s)" -ForegroundColor $(
switch ($_.Name) {
"CRITICAL" { "Red" }
"WARNING" { "Yellow" }
default { "Cyan" }
}
)
}
} else {
Write-Host "`n[$(NowTag)] No alerts found - all databases healthy!" -ForegroundColor Green
}
# Summary statistics
Write-Host "`nDATABASE SUMMARY:" -ForegroundColor Cyan
Write-Host " Total Databases: $dbCount"
Write-Host " Mounted: $(($dbAnalysis | Where-Object Mounted).Count)"
Write-Host " Dismounted: $(($dbAnalysis | Where-Object { -not $_.Mounted }).Count)"
Write-Host " Total Size: $([math]::Round(($dbAnalysis | Measure-Object DatabaseSizeGB -Sum).Sum, 2)) GB"
Write-Host " Total Whitespace: $([math]::Round(($dbAnalysis | Measure-Object WhitespaceGB -Sum).Sum, 2)) GB"
Write-Host " Circular Logging Enabled: $(($dbAnalysis | Where-Object CircularLogging).Count)"
Write-Host "`n[$(NowTag)] Analysis complete! Output folder: $OutputFolder"