⚠️ 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>
291 lines
10 KiB
PowerShell
291 lines
10 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Quick Exchange server health check with aggregated results
|
|
|
|
.DESCRIPTION
|
|
Runs multiple Exchange health cmdlets and aggregates results into a single
|
|
report. Includes service health, replication health, MAPI connectivity,
|
|
and database mount status.
|
|
|
|
.PARAMETER OutputFolder
|
|
Destination folder for reports. Default: .\ExchangeHealth-<date>
|
|
|
|
.PARAMETER IncludeMailflow
|
|
Test mail flow using Test-Mailflow cmdlet (default: $false, can be slow)
|
|
|
|
.PARAMETER ServerName
|
|
Specific server to test (default: local server)
|
|
|
|
.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
|
|
- Some tests require specific server roles
|
|
- Tests are non-intrusive (read-only)
|
|
|
|
.EXAMPLE
|
|
.\Test-ExchangeHealth.ps1
|
|
|
|
.EXAMPLE
|
|
.\Test-ExchangeHealth.ps1 -ServerName "EXCH01" -IncludeMailflow $true
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$OutputFolder = (Join-Path -Path (Get-Location) -ChildPath ("ExchangeHealth-" + (Get-Date -Format "yyyyMMdd-HHmm"))),
|
|
[bool]$IncludeMailflow = $false,
|
|
[string]$ServerName = $env:COMPUTERNAME
|
|
)
|
|
|
|
function NowTag { (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") }
|
|
|
|
Write-Host "[$(NowTag)] ⚠️ AI-GENERATED SCRIPT - UNTESTED" -ForegroundColor Yellow
|
|
Write-Host "[$(NowTag)] Starting Exchange health check on $ServerName..." -ForegroundColor Green
|
|
|
|
# Create output folder
|
|
New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null
|
|
|
|
$allResults = @()
|
|
$criticalIssues = @()
|
|
$warnings = @()
|
|
|
|
# Test 1: Service Health
|
|
Write-Host "[$(NowTag)] Testing service health..."
|
|
try {
|
|
$serviceHealth = Test-ServiceHealth -Server $ServerName -ErrorAction SilentlyContinue
|
|
if ($serviceHealth) {
|
|
foreach ($svc in $serviceHealth) {
|
|
$result = [PSCustomObject]@{
|
|
TestCategory = "ServiceHealth"
|
|
TestName = $svc.Role
|
|
Server = $ServerName
|
|
Status = if ($svc.RequiredServicesRunning) { "PASS" } else { "FAIL" }
|
|
Details = "Required services: $($svc.RequiredServicesRunning)"
|
|
}
|
|
$allResults += $result
|
|
|
|
if (-not $svc.RequiredServicesRunning) {
|
|
$criticalIssues += "Service Health FAILED for role: $($svc.Role)"
|
|
}
|
|
}
|
|
Write-Host " Service Health: COMPLETED" -ForegroundColor Green
|
|
} else {
|
|
Write-Host " Service Health: No results (cmdlet may not be available)" -ForegroundColor Yellow
|
|
}
|
|
} catch {
|
|
Write-Host " Service Health: ERROR - $($_.Exception.Message)" -ForegroundColor Red
|
|
$allResults += [PSCustomObject]@{
|
|
TestCategory = "ServiceHealth"
|
|
TestName = "Test-ServiceHealth"
|
|
Server = $ServerName
|
|
Status = "ERROR"
|
|
Details = $_.Exception.Message
|
|
}
|
|
}
|
|
|
|
# Test 2: Replication Health (DAG servers only)
|
|
Write-Host "[$(NowTag)] Testing replication health..."
|
|
try {
|
|
$replHealth = Test-ReplicationHealth -Server $ServerName -ErrorAction SilentlyContinue
|
|
if ($replHealth) {
|
|
foreach ($test in $replHealth) {
|
|
$status = if ($test.Result -eq "Passed") { "PASS" } elseif ($test.Result -eq "Failed") { "FAIL" } else { "WARNING" }
|
|
|
|
$result = [PSCustomObject]@{
|
|
TestCategory = "ReplicationHealth"
|
|
TestName = $test.Check
|
|
Server = $ServerName
|
|
Status = $status
|
|
Details = $test.Error
|
|
}
|
|
$allResults += $result
|
|
|
|
if ($status -eq "FAIL") {
|
|
$criticalIssues += "Replication Health FAILED: $($test.Check) - $($test.Error)"
|
|
} elseif ($status -eq "WARNING") {
|
|
$warnings += "Replication Health WARNING: $($test.Check) - $($test.Error)"
|
|
}
|
|
}
|
|
Write-Host " Replication Health: COMPLETED" -ForegroundColor Green
|
|
} else {
|
|
Write-Host " Replication Health: Not applicable (not a DAG member)" -ForegroundColor Yellow
|
|
}
|
|
} catch {
|
|
Write-Host " Replication Health: ERROR or not applicable - $($_.Exception.Message)" -ForegroundColor Yellow
|
|
$allResults += [PSCustomObject]@{
|
|
TestCategory = "ReplicationHealth"
|
|
TestName = "Test-ReplicationHealth"
|
|
Server = $ServerName
|
|
Status = "N/A"
|
|
Details = "Not applicable or error: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# Test 3: MAPI Connectivity
|
|
Write-Host "[$(NowTag)] Testing MAPI connectivity..."
|
|
try {
|
|
$databases = Get-MailboxDatabase -Server $ServerName -Status -ErrorAction SilentlyContinue
|
|
foreach ($db in $databases) {
|
|
if ($db.Mounted) {
|
|
try {
|
|
$mapiTest = Test-MapiConnectivity -Database $db.Name -ErrorAction Stop
|
|
$status = if ($mapiTest.Result -eq "Success") { "PASS" } else { "FAIL" }
|
|
|
|
$result = [PSCustomObject]@{
|
|
TestCategory = "MapiConnectivity"
|
|
TestName = "Database: $($db.Name)"
|
|
Server = $ServerName
|
|
Status = $status
|
|
Details = "Latency: $($mapiTest.Latency.TotalMilliseconds) ms"
|
|
}
|
|
$allResults += $result
|
|
|
|
if ($status -eq "FAIL") {
|
|
$criticalIssues += "MAPI Connectivity FAILED for database: $($db.Name)"
|
|
}
|
|
} catch {
|
|
$result = [PSCustomObject]@{
|
|
TestCategory = "MapiConnectivity"
|
|
TestName = "Database: $($db.Name)"
|
|
Server = $ServerName
|
|
Status = "ERROR"
|
|
Details = $_.Exception.Message
|
|
}
|
|
$allResults += $result
|
|
$warnings += "MAPI test error for $($db.Name): $($_.Exception.Message)"
|
|
}
|
|
}
|
|
}
|
|
Write-Host " MAPI Connectivity: COMPLETED" -ForegroundColor Green
|
|
} catch {
|
|
Write-Host " MAPI Connectivity: ERROR - $($_.Exception.Message)" -ForegroundColor Red
|
|
}
|
|
|
|
# Test 4: Database Mount Status
|
|
Write-Host "[$(NowTag)] Checking database mount status..."
|
|
try {
|
|
$databases = Get-MailboxDatabase -Server $ServerName -Status -ErrorAction SilentlyContinue
|
|
foreach ($db in $databases) {
|
|
$status = if ($db.Mounted) { "PASS" } else { "FAIL" }
|
|
|
|
$result = [PSCustomObject]@{
|
|
TestCategory = "DatabaseMount"
|
|
TestName = "Database: $($db.Name)"
|
|
Server = $ServerName
|
|
Status = $status
|
|
Details = "Mounted: $($db.Mounted)"
|
|
}
|
|
$allResults += $result
|
|
|
|
if (-not $db.Mounted) {
|
|
$criticalIssues += "Database DISMOUNTED: $($db.Name)"
|
|
}
|
|
}
|
|
Write-Host " Database Mount Status: COMPLETED" -ForegroundColor Green
|
|
} catch {
|
|
Write-Host " Database Mount Status: ERROR - $($_.Exception.Message)" -ForegroundColor Red
|
|
}
|
|
|
|
# Test 5: Mail Flow (optional)
|
|
if ($IncludeMailflow) {
|
|
Write-Host "[$(NowTag)] Testing mail flow (this may take a minute)..."
|
|
try {
|
|
$mailflowTest = Test-Mailflow -TargetMailboxServer $ServerName -ErrorAction Stop
|
|
$status = if ($mailflowTest.TestMailflowResult -eq "Success") { "PASS" } else { "FAIL" }
|
|
|
|
$result = [PSCustomObject]@{
|
|
TestCategory = "Mailflow"
|
|
TestName = "Test-Mailflow"
|
|
Server = $ServerName
|
|
Status = $status
|
|
Details = "Result: $($mailflowTest.TestMailflowResult), Latency: $($mailflowTest.MessageLatencyTime.TotalMilliseconds) ms"
|
|
}
|
|
$allResults += $result
|
|
|
|
if ($status -eq "FAIL") {
|
|
$criticalIssues += "Mail Flow test FAILED"
|
|
}
|
|
Write-Host " Mail Flow: COMPLETED" -ForegroundColor Green
|
|
} catch {
|
|
Write-Host " Mail Flow: ERROR - $($_.Exception.Message)" -ForegroundColor Red
|
|
$allResults += [PSCustomObject]@{
|
|
TestCategory = "Mailflow"
|
|
TestName = "Test-Mailflow"
|
|
Server = $ServerName
|
|
Status = "ERROR"
|
|
Details = $_.Exception.Message
|
|
}
|
|
}
|
|
}
|
|
|
|
# Export results
|
|
Write-Host "[$(NowTag)] Exporting results..."
|
|
|
|
$csvFile = Join-Path $OutputFolder "Health-Check-Results.csv"
|
|
$allResults | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $csvFile
|
|
Write-Host "[$(NowTag)] Results exported: $csvFile" -ForegroundColor Green
|
|
|
|
# Summary
|
|
$passCount = ($allResults | Where-Object Status -eq "PASS").Count
|
|
$failCount = ($allResults | Where-Object Status -eq "FAIL").Count
|
|
$errorCount = ($allResults | Where-Object Status -eq "ERROR").Count
|
|
$totalTests = $allResults.Count
|
|
|
|
$summaryFile = Join-Path $OutputFolder "Health-Check-Summary.txt"
|
|
$summary = @"
|
|
Exchange Health Check Summary
|
|
Server: $ServerName
|
|
Generated: $(Get-Date)
|
|
|
|
TEST RESULTS:
|
|
Total Tests: $totalTests
|
|
Passed: $passCount
|
|
Failed: $failCount
|
|
Errors: $errorCount
|
|
Overall Status: $(if ($failCount -eq 0 -and $errorCount -eq 0) { "HEALTHY" } else { "ISSUES DETECTED" })
|
|
|
|
$(if ($criticalIssues.Count -gt 0) {
|
|
"CRITICAL ISSUES:
|
|
$($criticalIssues | ForEach-Object { " - $_" } | Out-String)
|
|
"} else { "" })
|
|
|
|
$(if ($warnings.Count -gt 0) {
|
|
"WARNINGS:
|
|
$($warnings | ForEach-Object { " - $_" } | Out-String)
|
|
"} else { "" })
|
|
|
|
DETAILED RESULTS:
|
|
$($allResults | ForEach-Object { " [$($_.Status)] $($_.TestCategory) - $($_.TestName): $($_.Details)" } | Out-String)
|
|
"@
|
|
|
|
$summary | Out-File -FilePath $summaryFile -Encoding UTF8
|
|
Write-Host "[$(NowTag)] Summary: $summaryFile" -ForegroundColor Green
|
|
|
|
# Console output
|
|
Write-Host "`nHEALTH CHECK SUMMARY:" -ForegroundColor Cyan
|
|
Write-Host " Total Tests: $totalTests"
|
|
Write-Host " Passed: $passCount" -ForegroundColor Green
|
|
Write-Host " Failed: $failCount" -ForegroundColor $(if ($failCount -gt 0) { "Red" } else { "Green" })
|
|
Write-Host " Errors: $errorCount" -ForegroundColor $(if ($errorCount -gt 0) { "Red" } else { "Green" })
|
|
|
|
if ($criticalIssues.Count -gt 0) {
|
|
Write-Host "`nCRITICAL ISSUES DETECTED:" -ForegroundColor Red
|
|
$criticalIssues | ForEach-Object { Write-Host " - $_" -ForegroundColor Red }
|
|
}
|
|
|
|
if ($warnings.Count -gt 0) {
|
|
Write-Host "`nWARNINGS:" -ForegroundColor Yellow
|
|
$warnings | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow }
|
|
}
|
|
|
|
Write-Host "`n[$(NowTag)] Health check complete! Output folder: $OutputFolder"
|
|
|
|
# Exit code
|
|
if ($failCount -gt 0 -or $errorCount -gt 0) {
|
|
exit 1
|
|
} else {
|
|
exit 0
|
|
}
|