PowerShell Active Directory Replication Status Check Script
─□✕

PowerShell Active Directory Replication Status Check Script

PowerShell Tips Editor 5 min read
PowerShell Active Directory Replication Status Check Script

Why AD Replication Failures Go Unnoticed

Active Directory replication failures rarely announce themselves with obvious symptoms. Password changes made on one domain controller may take minutes or hours to reach others, causing authentication failures that users report as intermittent. Group Policy updates fail to apply consistently. Newly created accounts do not appear on some DCs. By the time an administrator investigates, the replication failure may be days old. Proactive monitoring with PowerShell using the ActiveDirectory module cmdlets catches these failures early, before they cascade into directory inconsistencies that require manual reconciliation.

Quick Answer

Use Get-ADDomainController -Filter * to enumerate all DCs, then run Get-ADReplicationFailure and Get-ADReplicationPartnerMetadata against each one. Flag any failures older than your acceptable threshold and email an HTML status table to the operations team as a daily health check.

Getting All Domain Controllers

Start by enumerating every DC in the domain. Get-ADDomainController with -Filter * returns all DCs with their hostnames, sites, and roles. Filter by site if you want to scope the check to a specific location. Store the hostname list for use in subsequent replication queries.

Import-Module ActiveDirectory -ErrorAction Stop

$domain = Get-ADDomain
$allDCs = Get-ADDomainController -Filter * | Sort-Object Name

Write-Host "Domain      : $($domain.DNSRoot)"
Write-Host "Domain controllers found: $($allDCs.Count)"

$allDCs | Select-Object Name, Site, IPv4Address, IsGlobalCatalog, OperationMasterRoles |
    Format-Table -AutoSize

Checking Replication Failures with Get-ADReplicationFailure

Get-ADReplicationFailure queries each DC for replication link failures. It returns objects with the failure count, the first and last failure timestamps, and the replication partner that failed. Querying all DCs in a single pipeline pass using -Target $allDCs.Name is the most efficient approach. Any DC with a non-zero FailureCount warrants immediate investigation.

$failures = Get-ADReplicationFailure -Target $allDCs.Name -Scope Domain 2>&1 |
    Where-Object { $_ -is [Microsoft.ActiveDirectory.Management.ADReplicationFailure] }

if ($failures.Count -eq 0) {
    Write-Host "No replication failures detected across $($allDCs.Count) domain controllers."
}
else {
    Write-Warning "Replication failures detected: $($failures.Count)"
    $failures | Select-Object Server, Partner, FirstFailureTime, LastFailureTime,
        FailureCount, FailureType | Format-Table -AutoSize
}

Reading Partner Metadata with Get-ADReplicationPartnerMetadata

Get-ADReplicationPartnerMetadata returns detailed information about each replication partnership, including the last successful replication time and the consecutive replication failures counter. This cmdlet gives you the data needed to calculate replication lag — the time since the last successful sync. Partnerships that have not successfully replicated within a defined threshold are flagged for escalation.

$partnerData = foreach ($dc in $allDCs) {
    try {
        Get-ADReplicationPartnerMetadata -Target $dc.Name -Scope Server -ErrorAction Stop
    }
    catch {
        Write-Warning "Could not query $($dc.Name): $_"
    }
}

$partnerData | Select-Object Server, Partner,
    @{N="LastReplicationSuccess"; E={ $_.LastReplicationSuccess }},
    @{N="LastReplicationAttempt"; E={ $_.LastReplicationAttempt }},
    ConsecutiveReplicationFailures |
    Format-Table -AutoSize

Calculating Replication Lag Between Partners

Replication lag is the time between the current moment and the last successful replication for each DC-to-partner pair. A lag under 15 minutes is typically acceptable for multi-site topologies with scheduled replication. Site-internal replication should complete within seconds to minutes. Calculate lag by subtracting LastReplicationSuccess from the current time and comparing to your defined threshold.

$lagThresholdMinutes = 60  # Alert if lag exceeds this value

$lagReport = $partnerData | ForEach-Object {
    $lag = (Get-Date) - $_.LastReplicationSuccess
    [PSCustomObject]@{
        SourceDC   = $_.Server
        Partner    = $_.Partner
        LagMinutes = [math]::Round($lag.TotalMinutes, 1)
        Status     = if ($lag.TotalMinutes -gt $lagThresholdMinutes) { "ALERT" } else { "OK" }
        LastSuccess = $_.LastReplicationSuccess
        Failures    = $_.ConsecutiveReplicationFailures
    }
}

$lagReport | Sort-Object LagMinutes -Descending | Format-Table -AutoSize

Flagging DCs with Failures Older Than Threshold

Not all replication failures require the same urgency. A single transient failure that cleared in the last cycle is different from a failure that has persisted for 48 hours. Filtering on failure age gives you a tiered view: recent failures for awareness and aged failures for immediate action. The threshold below uses 4 hours as the escalation boundary, but adjust to match your replication topology’s expected convergence time.

$escalationThreshold = (Get-Date).AddHours(-4)

$criticalFailures = $failures | Where-Object {
    $_.FirstFailureTime -lt $escalationThreshold -and $_.FailureCount -gt 0
}

if ($criticalFailures.Count -gt 0) {
    Write-Warning "CRITICAL: $($criticalFailures.Count) aged replication failures require attention:"
    $criticalFailures | Select-Object Server, Partner, FirstFailureTime, FailureCount |
        Format-Table -AutoSize
}
else {
    Write-Host "No critical aged failures found."
}

Daily Email Report with HTML Status Table

Combining the lag report and failure data into an HTML email gives the operations team a daily digest without needing to run the script manually. Use ConvertTo-Html to produce the table and Send-MailMessage to deliver it. Color-code the status column in the HTML template to make ALERT rows visually distinct from OK rows, so critical issues are immediately obvious in the email client.

$htmlBody = $lagReport |
    ConvertTo-Html -Property SourceDC, Partner, LagMinutes, Status, LastSuccess, Failures `
    -PreContent "<h2>AD Replication Status — $(Get-Date -Format 'yyyy-MM-dd')</h2>" `
    -PostContent "<p>Generated by scheduled replication monitor</p>" |
    Out-String

Send-MailMessage `
    -To "[email protected]" `
    -From "[email protected]" `
    -Subject "AD Replication Status $(Get-Date -Format 'yyyy-MM-dd')" `
    -Body $htmlBody `
    -BodyAsHtml `
    -SmtpServer "mail.contoso.com"

Write-Host "AD replication report emailed."

Common Errors

  • Get-ADReplicationFailure requires RSAT AD DS tools. This cmdlet is part of the ActiveDirectory module, which is installed as part of RSAT (Remote Server Administration Tools). It is not available on workstations by default. Install RSAT via Add-WindowsCapability -Name Rsat.ActiveDirectory.DS-LDS.Tools* on Windows 10/11 management stations, or run the script from a domain controller where the module is always present.
  • Cleared failures still appear until replication succeeds. A replication failure that appears to have resolved at the OS level may still show in Get-ADReplicationFailure with a non-zero count until the next successful replication cycle completes and AD updates the internal counters. Do not assume a failure is cleared until ConsecutiveReplicationFailures returns to zero after a verified successful replication.

Related Cmdlets / See Also

Wrapping Up

Scheduled AD replication monitoring catches failures days before they generate user-visible symptoms. Query all DCs for failures and partner metadata, calculate lag against your topology’s expected convergence time, escalate aged failures, and deliver a daily HTML report to the operations team. Ten minutes of scripting investment provides continuous replication visibility that no manual process can match.

Send-Item -To