Functions/Test-ADReplicationHealth.ps1
|
<# .SYNOPSIS Checks Active Directory replication health across all domain controllers in the domain. .DESCRIPTION This function queries AD replication partner metadata and replication failure data to identify domain controllers with replication issues, stale replication, or consecutive failures. Outputs a summary report to the console and optionally to HTML. .PARAMETER OutputPath Optional path to export an HTML report. If omitted, results are only shown in console. .PARAMETER MaxReplicationAgeHours Threshold (in hours) beyond which last successful replication is flagged as stale. Default is 24 hours. .EXAMPLE Test-ADReplicationHealth .EXAMPLE Test-ADReplicationHealth -OutputPath "C:\Reports\ADReplHealth.html" -MaxReplicationAgeHours 12 .NOTES Requires: ActiveDirectory PowerShell module (RSAT-AD-PowerShell) Requires: Domain Admin or delegated read access to replication metadata #> function Test-ADReplicationHealth { [CmdletBinding()] param( [Parameter(Mandatory = $false)] [string]$OutputPath, [Parameter(Mandatory = $false)] [int]$MaxReplicationAgeHours = 24 ) # Ensure the ActiveDirectory module is available if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) { Write-Error "ActiveDirectory module not found. Install RSAT-AD-PowerShell and try again." return } Import-Module ActiveDirectory -ErrorAction Stop Write-Verbose "Retrieving list of domain controllers..." $domainControllers = Get-ADDomainController -Filter * if (-not $domainControllers) { Write-Error "No domain controllers found. Check connectivity and permissions." return } $results = @() $now = Get-Date foreach ($dc in $domainControllers) { Write-Verbose "Checking replication metadata for $($dc.HostName)..." try { $partnerMetadata = Get-ADReplicationPartnerMetadata -Target $dc.HostName -ErrorAction Stop } catch { # DC unreachable or replication metadata query failed $results += [PSCustomObject]@{ SourceDC = $dc.HostName Partner = "N/A" LastReplicationSuccess = "N/A" LastReplicationResult = "ERROR" ConsecutiveFailures = "N/A" Status = "UNREACHABLE" Detail = $_.Exception.Message } continue } foreach ($partner in $partnerMetadata) { $hoursSinceSuccess = if ($partner.LastReplicationSuccess) { [math]::Round((New-TimeSpan -Start $partner.LastReplicationSuccess -End $now).TotalHours, 1) } else { $null } $status = "OK" $detail = "" if ($partner.ConsecutiveReplicationFailures -gt 0) { $status = "FAILING" $detail = "Consecutive failures: $($partner.ConsecutiveReplicationFailures). Last result: $($partner.LastReplicationResult)" } elseif ($null -eq $hoursSinceSuccess) { $status = "UNKNOWN" $detail = "No successful replication recorded." } elseif ($hoursSinceSuccess -gt $MaxReplicationAgeHours) { $status = "STALE" $detail = "Last success was $hoursSinceSuccess hours ago (threshold: $MaxReplicationAgeHours hrs)." } $results += [PSCustomObject]@{ SourceDC = $dc.HostName Partner = $partner.Partner LastReplicationSuccess = $partner.LastReplicationSuccess LastReplicationResult = $partner.LastReplicationResult ConsecutiveFailures = $partner.ConsecutiveReplicationFailures Status = $status Detail = $detail } } } # Console summary $failing = $results | Where-Object { $_.Status -in @("FAILING", "STALE", "UNREACHABLE", "UNKNOWN") } Write-Host "`n=== AD Replication Health Summary ===" -ForegroundColor Cyan Write-Host "Domain Controllers checked: $($domainControllers.Count)" Write-Host "Replication links checked: $($results.Count)" Write-Host "Issues found: $($failing.Count)`n" if ($failing.Count -gt 0) { Write-Host "Issues detected:" -ForegroundColor Yellow $failing | Format-Table SourceDC, Partner, Status, Detail -AutoSize } else { Write-Host "No replication issues detected." -ForegroundColor Green } # Optional HTML export if ($OutputPath) { $htmlHeader = @" <style> body { font-family: Segoe UI, Arial, sans-serif; } table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid #ccc; padding: 6px 10px; text-align: left; } th { background-color: #2c3e50; color: white; } tr.OK { background-color: #eafaf1; } tr.FAILING, tr.UNREACHABLE { background-color: #fdecea; } tr.STALE, tr.UNKNOWN { background-color: #fff8e1; } </style> "@ $htmlBody = $results | ConvertTo-Html -Head $htmlHeader -Title "AD Replication Health Report - $now" -PreContent "<h2>AD Replication Health Report</h2><p>Generated: $now</p>" $htmlBody | Out-File -FilePath $OutputPath -Encoding UTF8 Write-Host "`nHTML report saved to: $OutputPath" -ForegroundColor Cyan } return $results } |