Active Directory Replication Health Diagnostic Script - ToddMaxey/Technical-Documentation GitHub Wiki

Active Directory Replication Health Diagnostic Script

This PowerShell script automates a comprehensive replication-health check for all Domain Controllers (including RODCs) in the domain. It uses built-in tools to collect detailed information and attempt basic remediation. Key components are:

The overall workflow per DC is: enumerate DCs, run diagnostics (dcdiag, repadmin, nslookup, share/service checks), identify issues, attempt remediation (restart NTDS/DFSR services, force replication), then re-check. All results (issues found, actions taken, final status, and suggestions if unresolved) are written to the console and to C:\Temp\AD_Replication_Report.txt.

Workflow

  1. Initialization: Import the ActiveDirectory module, prepare the output log file (C:\Temp\AD_Replication_Report.txt), and define a logging function.
  2. Enumerate DCs: Use Get-ADDomainController -Filter * to list all domain controllers (including RODCs).
  3. Diagnostics per DC: For each DC:
  4. Remediation: If any issues are detected:
    • Attempt to restart the NTDS service on the target DC (to recover the AD DS engine).
    • Attempt to restart DFSR service if it exists and was problematic.
    • Force replication with repadmin /syncall <DC> /AeP (sync all naming contexts, all sites, push changes) Repadmin -syncall | Microsoft Learn.
    • Wait briefly, then rerun repadmin /showrepl to see if errors are cleared.
  5. Summary and Logging: For each DC, the script logs: issues found, actions taken, and final status (e.g. “resolved” or warnings if still failing). Unresolved issues are flagged with suggestions (e.g. check network, event logs, firewall). All output is timestamped, printed to the screen, and appended to AD_Replication_Report.txt.

Below is the complete PowerShell script with section headings and inline comments for clarity. It uses only non-deprecated cmdlets and is compatible with Windows Server 2016, 2019, 2022 (current supported versions).

<#
.SYNOPSIS
    Comprehensive AD replication health check script for all domain controllers.
.DESCRIPTION
    Runs DCDiag, Repadmin, DNS, and SYSVOL/DFSR checks on each DC, attempts basic remediation, 
    and logs a detailed report per DC.
.NOTES
    Run as Administrator on a Domain Controller. Requires the ActiveDirectory PowerShell module.
#>

# Section: Initialization (Imports, variables, helper functions)
Import-Module ActiveDirectory -ErrorAction Stop  # AD module for Get-ADDomainController

# Prepare report file
$reportFile = 'C:\Temp\AD_Replication_Report.txt'
$reportDir  = Split-Path $reportFile
If (!(Test-Path $reportDir) { New-Item -ItemType Directory -Path $reportDir -Force | Out-Null }
"========== AD Replication Health Report ==========" | Out-File -FilePath $reportFile -Force -Encoding UTF8
"Report generated: $(Get-Date)" | Out-File -FilePath $reportFile -Append -Encoding UTF8

# Logging function to output to host and file
function Log {
    param[string]$text)
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $line = "[$timestamp] $text"
    Write-Host $line
    Add-Content -Path $reportFile -Value $line
}

# Section: Get all Domain Controllers (including RODCs)
Log "Enumerating all Domain Controllers in the domain..."
try {
    $DCs = Get-ADDomainController -Filter * | Select-Object -Property HostName,OperatingSystem,IsReadOnly
} catch {
    Log "ERROR: Failed to retrieve domain controllers. Ensure AD module is loaded and you have privileges."
    exit 1
}

# Section: Diagnostics per DC
foreach ($dc in $DCs) {
    $dcName = $dc.HostName
    Log "==================================================="
    Log "Checking DC: $dcName (Read-Only: $($dc.IsReadOnly) - OS: $($dc.OperatingSystem)"
    Log "---------------------------------------------------"
    $issues = @()  # collect issues for summary

    # DCDiag replication and SYSVOL tests
    Log "Running DCDiag tests (replication, SYSVOL, DNS) on $dcName..."
    try {
        $tests = 'Replications','SysVolCheck','DNS','Join','Advertising','KCC'
        $args  = "/s:$dcName /test:$($tests -join ',') /skip:DNSBasic /q"
        $dcdiagOutput = & dcdiag.exe $args 2>&1
    } catch {
        $dcdiagOutput = $_.Exception.Message
    }
    Log "DCDiag output for $dcName:`n$dcdiagOutput"

    # Parse DCDiag for failures/warnings
    if ($dcdiagOutput -match '(FAILED|Error|WARNING)') {
        $dcdiagOutput -split "`n" | ForEach-Object {
            if ($_ -match 'FAILED|Error|WARNING') {
                $issues += $_.Trim()
            }
        }
    }
    if ($issues.Count -gt 0) {
        Log "DCDiag reported issues on $dcName:"
        foreach ($issue in $issues) { Log "  - $issue" }
    } else {
        Log "No errors in DCDiag tests for $dcName."
    }

    # Repadmin checks
    Log "Running Repadmin /replsummary on $dcName..."
    $replSummary = & repadmin.exe /replsummary $dcName 2>&1
    Log "Repadmin /replsummary $dcName:`n$replSummary"

    Log "Running Repadmin /showrepl (errors only) on $dcName..."
    $showRepl = & repadmin.exe /showrepl $dcName /errorsonly 2>&1
    Log "Repadmin /showrepl $dcName (errors only):`n$showRepl"
    if ($showRepl -match '\w') {
        $showRepl -split "`n" | Where-Object { $_ -and ($_ -notmatch '^$') } | ForEach-Object {
            $issues += $_.Trim()
        }
        if ($showRepl.Trim() {
            Log "Replication errors found on $dcName:"
            $showRepl -split "`n" | Where-Object {$_ -ne ""} | ForEach-Object { Log "  $_" }
        } else {
            Log "No replication errors reported by Repadmin on $dcName."
        }
    }

    Log "Running Repadmin /showconn on $dcName..."
    $showConn = & repadmin.exe /showconn $dcName 2>&1
    Log "Repadmin /showconn $dcName:`n$showConn"

    # DNS SRV record check
    Log "Checking DNS SRV records for $dcName..."
    try {
        $domain = (Get-ADDomain -Server $dcName).DNSRoot
    } catch {
        $domain = (Get-ADForest -Server $dcName).Name
    }
    Log "Domain DNS name: $domain"
    $srvQuery = "_ldap._tcp.dc._msdcs.$domain"
    $nsResult = & nslookup -type=SRV $srvQuery 2>&1
    Log "Nslookup for $srvQuery:`n$nsResult"
    if ($nsResult -match [regex]::Escape($dcName) {
        Log "Found SRV record for $dcName in DNS."
    } else {
        $issues += "Missing SRV record for $dcName"
        Log "WARNING: SRV record for $dcName not found."
    }

    # SYSVOL share check
    Log "Checking SYSVOL share on $dcName..."
    try {
        $sysvol = Invoke-Command -ComputerName $dcName -ScriptBlock { Get-SmbShare -Name SYSVOL -ErrorAction SilentlyContinue }
        if ($sysvol) {
            Log "SYSVOL share exists on $dcName."
        } else {
            $issues += "SYSVOL share missing on $dcName"
            Log "WARNING: SYSVOL share not found on $dcName."
        }
    } catch {
        $issues += "Failed to query SYSVOL share on $dcName"
        Log "ERROR: Could not query SYSVOL share on $dcName."
    }

    # DFSR service check
    Log "Checking DFSR service on $dcName..."
    try {
        $dfsrSvc = Invoke-Command -ComputerName $dcName -ScriptBlock { Get-Service -Name DFSR -ErrorAction SilentlyContinue }
    } catch {
        $dfsrSvc = $null
    }
    if ($dfsrSvc) {
        if ($dfsrSvc.Status -ne 'Running') {
            $issues += "DFSR service is $($dfsrSvc.Status) on $dcName"
            Log "WARNING: DFSR service status on $dcName: $($dfsrSvc.Status)"
        } else {
            Log "DFSR service is running on $dcName."
        }
    } else {
        Log "DFSR service not present on $dcName (may be using FRS)."
    }

    # Section: Remediation if issues detected
    if ($issues.Count -gt 0) {
        Log "Issues detected on $dcName. Attempting remediation..."
        # Restart NTDS service if needed
        Log "Checking NTDS (AD DS) service on $dcName..."
        try {
            $ntdsSvc = Invoke-Command -ComputerName $dcName -ScriptBlock { Get-Service -Name NTDS }
            if ($ntdsSvc.Status -ne 'Running') {
                Log "NTDS service is $($ntdsSvc.Status). Restarting..."
                Invoke-Command -ComputerName $dcName -ScriptBlock { Restart-Service NTDS -Force } -ErrorAction SilentlyContinue
                Log "NTDS service restart initiated on $dcName."
            }
        } catch {
            Log "ERROR: Could not access NTDS service on $dcName."
        }

        # Restart DFSR if necessary
        if ($dfsrSvc -and $dfsrSvc.Status -ne 'Running') {
            Log "Attempting to start DFSR service on $dcName..."
            Invoke-Command -ComputerName $dcName -ScriptBlock { Restart-Service DFSR -Force } -ErrorAction SilentlyContinue
            Log "DFSR service restart initiated on $dcName."
        }

        # Force replication
        Log "Forcing AD replication with repadmin /syncall on $dcName..."
        try {
            $syncRes = & repadmin.exe /syncall $dcName /AeP /d 2>&1
            Log "Repadmin /syncall output:`n$syncRes"
        } catch {
            Log "ERROR: repadmin /syncall failed on $dcName."
        }

        Start-Sleep -Seconds 5  # brief pause

        # Re-check replication status after remediation
        Log "Re-checking replication on $dcName after remediation..."
        $showReplAfter = & repadmin.exe /showrepl $dcName /errorsonly 2>&1
        if ($showReplAfter.Trim() -eq "") {
            Log "No replication errors found on $dcName after remediation."
            $resolved = $true
        } else {
            $showReplAfter -split "`n" | Where-Object { $_ -and ($_ -notmatch '^$') } | ForEach-Object {
                Log "  $_"
            }
            $resolved = $false
        }

        # Final remediation status
        if ($resolved) {
            Log "SUCCESS: Replication issues resolved on $dcName."
        } else {
            Log "WARNING: Replication issues still exist on $dcName after remediation."
            Log "Suggestion: Check network connectivity, DNS, firewall, and event logs manually."
        }
    } else {
        Log "No issues detected on $dcName. Skipping remediation."
    }

    Log "Completed checks for $dcName.`n"
}

Log "========== End of AD Replication Health Report =========="

Notes:

  • Ensure you run this script with Domain Admin rights on a DC.
  • The script uses Invoke-Command to query remote DC services/share; if WinRM is disabled, you may skip those checks or run the script locally on each DC.
  • The DFSR health report (not scripted above) can be generated with the Write-DfsrHealthReport cmdlet for deeper SYSVOL diagnostics Write-DfsrHealthReport (DFSR) | Microsoft Learn if needed.
  • This script provides verbose logging; you can adjust which diagnostics to run or how aggressively to remediate as suits your environment.
⚠️ **GitHub.com Fallback** ⚠️