Active Directory Operations and recovery

Domain controller health and FSMO roles

Find the DCs and role holders, check whether they're ready to serve clients, and narrow down a slow or unhealthy DC.

Inventory every domain in the forest

Get-ADDomainController -Filter * normally works within one domain. Iterate the forest’s domains when your question is forest-wide.

Import-Module ActiveDirectory
$forest = Get-ADForest
$dcs = foreach ($domain in $forest.Domains) {
    Get-ADDomainController -Filter * -Server $domain
}
$dcs | Sort-Object Domain,Site,HostName |
    Select-Object HostName,Domain,Site,OperatingSystem,
        IsGlobalCatalog,IsReadOnly,IPv4Address

Compare this inventory with the replication health results. Make sure every DC is accounted for: an unreachable server missing from the summary can look deceptively similar to one with no failures.

Save an inventory that keeps failed domains in the report

An old or incomplete DC list can make every later check miss the same servers. This version records each domain’s collection status and writes a local JSON report. The directory queries are read-only.

$forest = Get-ADForest -Identity 'contoso.com' -Server 'dc1.contoso.com' `
    -ErrorAction Stop
$results = foreach ($domain in $forest.Domains) {
    $started = [datetime]::UtcNow.ToString('o')
    try {
        $controllers = @(Get-ADDomainController -Filter * -Server $domain `
            -ErrorAction Stop)
        [pscustomobject]@{
            Domain = $domain
            StartedUtc = $started
            CompletedUtc = [datetime]::UtcNow.ToString('o')
            Status = 'Collected'
            Controllers = @($controllers | Select-Object HostName,Site,
                IsGlobalCatalog,IsReadOnly,NTDSSettingsObjectDN)
            Error = $null
        }
    }
    catch {
        [pscustomobject]@{
            Domain = $domain
            StartedUtc = $started
            CompletedUtc = [datetime]::UtcNow.ToString('o')
            Status = 'Failed'
            Controllers = @()
            Error = $_.ToString()
        }
    }
}
$results | ConvertTo-Json -Depth 6 |
    Set-Content -LiteralPath '.\dc-inventory.json' -Encoding UTF8

Get-ADDomainController -Filter * queries a domain; Get-ADForest supplies the forest’s domains. The DC cmdlet and forest cmdlet describe their targets. A domain name lets the module select a DC, so pin and record the source DC if that choice matters. Investigate a zero-controller result even if no exception was returned.

OS, schema and functional level are different

Get-ADForest | Select-Object Name,ForestMode
foreach ($domain in $forest.Domains) {
    Get-ADDomain -Server $domain | Select-Object DNSRoot,DomainMode
}
$root = Get-ADRootDSE
Get-ADObject -Identity $root.schemaNamingContext -Properties objectVersion |
    Select-Object objectVersion

A DC’s OS version, schema version, domain functional level and forest functional level answer different questions. Installing a newer DC does not automatically raise the functional levels. Check the feature’s specific prerequisite instead of assuming all four move together. Microsoft’s functional level documentation includes the current version matrix.

For a patch-sensitive incident, collect the actual build too. From a permitted remote-management session, Get-CimInstance Win32_OperatingSystem -ComputerName dc1.contoso.com can report version/build information, but a base build alone is not the complete cumulative-update revision. Correlate the installed update history with the KB defining the behavior. Two DCs with the same friendly OS name can enforce different authentication rules.

Locate the five FSMO roles

Get-ADForest | Format-List SchemaMaster,DomainNamingMaster
foreach ($domain in $forest.Domains) {
    Get-ADDomain -Server $domain |
        Select-Object DNSRoot,PDCEmulator,RIDMaster,InfrastructureMaster
}

The quick command-line equivalent for the current domain is:

netdom query fsmo

The schema and domain naming masters are forest roles. PDC emulator, RID master and infrastructure master are domain roles. Find out why each role lives where it does and who looks after it. An unexpected host or remote site may have a good explanation.

Transfer means the existing owner cooperates. Seizure is recovery when it cannot. Choose the procedure for the failed role and plan the former holder’s disposition; do not treat -Force as a routine transfer shortcut. Microsoft’s transfer and seizure procedure explains the difference.

FSMO transfer and recovery explains which operations a role gates, its initial-replication requirement, target selection and post-move validation.

Check that the DC advertises its services

dcdiag /s:dc1.contoso.com /test:Advertising /test:Services /test:SysVolCheck /v
dcdiag /s:dc1.contoso.com /test:DNS /v

Run the tests with the required access from an administrative session, then read the failed test and its error. A listening LDAP port doesn’t tell you whether the DC advertises, publishes SYSVOL or can resolve its replication partners. DCDiag’s reference explains what each test checks and which optional switches make changes.

SysVolCheck checks readiness state, not every file, share permission or DFSR replication path. If clients cannot open policy files, inspect the actual SYSVOL/NETLOGON shares and a representative UNC from the affected client. Do not set SysvolReady=1 merely to make a test pass while initial synchronization is incomplete. See SYSVOL event and state diagnosis.

Distinguish configuration from readiness

Get-ADRootDSE -Server 'dc1.contoso.com' |
    Format-List dnsHostName,isSynchronized,isGlobalCatalogReady,
        defaultNamingContext,namingContexts

A configured GC can still be waiting for its initial data before advertising as ready. That explains why a DC object can say GC while a caller looking for a usable GC can’t select it. The RootDSE properties show readiness, but don’t measure every partition’s current convergence. RootDSE’s attributes define the properties.

Collect the right event window

$since = (Get-Date).AddHours(-4)
Get-WinEvent -ComputerName 'dc1.contoso.com' -FilterHashtable @{
    LogName = 'Directory Service'
    StartTime = $since
    Level = 2,3
} | Select-Object TimeCreated,Id,ProviderName,Message

Correlate Directory Service with System, DNS Server, DFS Replication and the affected client’s events. Record the time zone and original event timestamp. Filtering to warnings and errors is useful for triage, but include surrounding informational events when reconstructing a restore or topology change.

A slow DC needs a time window

On a DC, Performance Monitor’s System → Active Directory Diagnostics data collector is a useful starting point where available. Capture during the symptom. Look at LSASS CPU, available memory, paging, database/log disk latency, queueing, LDAP workload and replication together. A five-minute quiet sample cannot explain yesterday’s peak.

Having enough RAM to cache a working set helps, but “RAM must exceed the entire DIT” is not a complete sizing method. Microsoft’s AD DS performance guidance connects counters to bottlenecks. Use LDAP search diagnostics when one application generates expensive queries.

Use the symptom to choose what to collect next:

Observed pattern Evidence to add
LSASS CPU rises with LDAP requests Event 1644/statistics, client IPs, filter frequency and query cost
Low CPU but long authentication waits Netlogon semaphore wait/hold/timeouts on every hop
Latency with disk reads and a cold cache Database cache behavior, available memory and storage latency over time
Free disk rapidly disappears Database/log volumes, checkpoint progress and the exact storage workload
Only one application is slow Its selected DC, bind type, referrals, connection pooling and follow-up requests

Don’t choose the cause first and collect only counters that agree with it. LDAP query performance and NTLM queues cover those two investigations in more detail.

The slow-DC note walks through a short counter capture, CPU normalization and the cache-to-disk chain. Capacity planning covers restarts and the load on surviving DCs. For update work, use the checks in DC patch validation.

Best Practices Analyzer is evidence for a discussion

On the target DC, with the role and BestPractices module installed:

Import-Module BestPractices
$model = 'Microsoft/Windows/DirectoryServices'
Invoke-BpaModel -ModelId $model
Get-BpaResult -ModelId $model |
    Where-Object Severity -in 'Error','Warning' |
    Select-Object Severity,Title,Problem,Impact,Resolution

The scan runs on the target. Assigning a remote computer’s name to a script variable doesn’t redirect it there. Read each finding in the context of the deployment before making the suggested change.

← All Active Directory topics