Active Directory Directory foundations

LDAP change tracking and synchronization

Choose between notifications, DirSync and USN polling, and design checkpoints that survive retries, moves, deletions and restores.

Decide whether you need notification or synchronization

An application might need a quick notification, a list of objects changed since its last poll, or a maintained copy of some directory data. Choose the mechanism for that job. Several edits can happen before the client reads an object, so none of these gives you a complete audit trail of every intermediate value.

Mechanism Useful for State the client must manage
LDAP notification control Refreshing a small configuration cache promptly A live connection, subscription and reconciliation after disconnect
DirSync Incrementally reconciling data from a naming context Opaque cookie, request definition and durable target data
uSNChanged polling A scoped application query with ordinary object permissions DC identity, invocation ID, USN checkpoint and move/deletion handling

Always identify the partition and replica. A change made on DC2 cannot appear in DC1’s query until it reaches DC1. Microsoft’s change-tracking overview describes the three approaches.

Discover the server’s controls

$dc = 'dc1.contoso.com'
$root = Get-ADRootDSE -Server $dc
$root.supportedControl | Sort-Object

Useful OIDs include 1.2.840.113556.1.4.841 for DirSync, 1.2.840.113556.1.4.528 for notifications, and 1.2.840.113556.1.4.417 for showing deleted objects. A server advertising a control does not grant the caller permission to read every object or attribute. Check the LDAP control definitions and the actual search response.

Notifications belong to a connection

The notification control registers an asynchronous search. Base scope watches one object; one-level scope watches its immediate children. It is not a general arbitrary-subtree watcher. Register first and then read the initial state, otherwise a change can fall between the initial read and subscription.

Request objectGUID so you can recognize the same object after a rename. If the connection breaks, register again and reconcile the current state; AD won’t replay everything you missed. A parent rename also needn’t produce a notification for every descendant whose DN changed. See Microsoft’s notification behavior and registration example.

DirSync returns a state delta

A DirSync search starts at a naming-context root with subtree scope. Start with an empty cookie, then use the returned cookie for subsequent requests. Keep going while the response says there’s more data. Once you’ve applied the results, durably commit both the target data and its checkpoint. Store the server and query definition with the cookie too.

The requested attributes influence which changes come back. A narrow filter can also stop matching an object after an edit, so the client needs a way to notice objects leaving its population. With raw LDAP, collecting deletions needs the Show Deleted control and requested attributes retained on deletion, such as isDeleted; some ADSI wrappers add controls automatically. Even then, a live-object filter can exclude tombstones once their other attributes are stripped. Use DirSync to reconcile your copy of the data, rather than as a stream of application events. DirSync search semantics explains the filtering.

Don’t try to decode a cookie as a timestamp. Reusing it on another replica of the same partition may replay data or return a much larger result; some cross-version cookies are rejected altogether. The client needs to tolerate duplicates and be able to start a full synchronization again.

DirSync permissions depend on its flags

Older descriptions say DirSync always requires replication privilege. The protocol includes LDAP_DIRSYNC_OBJECT_SECURITY (0x1), which restricts results to the objects and attributes accessible to the caller. Without that flag, the naming-context change-retrieval access check applies. Do not conflate that right with a requirement to grant the much broader replication rights used to retrieve secrets.

LDAP_DIRSYNC_INCREMENTAL_VALUES (0x80000000) changes how changes to forward-linked multivalued attributes are returned. A client consuming additions/removals must implement that response format; a client expecting whole replacement values cannot simply turn the flag on. The object-security flag does not permit a search base below the naming-context root. The MS-ADTS DirSync definition is the authority for the flags and access behavior.

USN checkpoints require a DC and an incarnation

uSNChanged is local to a DC. Keep its DNS name and database invocation ID with the checkpoint. A restored DC can reuse its hostname while changing its database incarnation. A checkpoint from DC1 must not silently become a checkpoint on DC2.

$root = Get-ADRootDSE -Server $dc
$service = Get-ADObject -Server $dc -Identity $root.dsServiceName `
    -Properties invocationId
[pscustomobject]@{
    Server = $root.dnsHostName
    ServiceObject = $root.dsServiceName
    InvocationId = $service.invocationId
    UpperUSN = [long]$root.highestCommittedUSN
}

Capture highestCommittedUSN before collecting a batch. Once the target data is successfully committed, retain that pre-query value as the next checkpoint. Saving only the maximum USN among matching results repeatedly examines unrelated changes and complicates empty-result handling. Microsoft’s USN polling algorithm specifies the affiliation and checkpoint rules.

This read-only diagnostic example shows a bounded window; it deliberately does not implement a production synchronizer’s persistence and recovery:

# Illustrative saved checkpoint from the SAME DC and invocation ID.
$previousCommittedUSN = [long]100000
$root = Get-ADRootDSE -Server $dc
$upper = [long]$root.highestCommittedUSN
if ($previousCommittedUSN -gt $upper) {
    throw 'Checkpoint is ahead of this replica. Investigate identity or restore.'
}
$lower = $previousCommittedUSN + 1
$filter = "(&(uSNChanged>=$lower)(uSNChanged<=$upper))"
Get-ADObject -Server $dc -SearchBase $root.defaultNamingContext `
    -SearchScope Subtree -LDAPFilter $filter -IncludeDeletedObjects `
    -ResultPageSize 500 -Properties uSNChanged,isDeleted,lastKnownParent `
    -ErrorAction Stop |
    Select-Object ObjectGUID,DistinguishedName,uSNChanged,isDeleted,lastKnownParent

An object updated again beyond the upper fence can be picked up in the next window; the directory isn’t frozen while you collect. Before committing a real client’s checkpoint, recheck the server and invocation ID. Failed batches need a retry path that keeps the earlier target state intact.

Plan for disappearance

Key target records by GUID rather than DN. A subtree-only search cannot see an object after it moves outside that subtree. A filter requiring an attribute can miss the object after that attribute is cleared. A deleted object may have lost the very attributes used in the ordinary live-object filter.

Depending on the application, you may need a partition-wide feed, a separate deletion query and periodic reconciliation. Check deleted-object permissions too. If the client has been offline longer than retention, the tombstones it needs may be gone. Losing permission to read an object can also make it disappear from the results, so don’t automatically treat absence as deletion.

← All Active Directory topics