Active Directory
banshee/active-directory-collection/active-directory/ssh/
replication
The inbound Active Directory replication of a domain controller: failures and lag per partner and partition, by SSH and PowerShell
by banshee1 installation2 versions
// The inbound Active Directory replication of a domain controller, by SSH: for each partner it replicates
// from and each partition (naming context) it replicates, the failures since the last success, the result
// of the last attempt, and the time since the last success.
// Runs one PowerShell command that only reads, through DomainController.GetAllReplicationNeighbors of .NET,
// which Microsoft documents: one ReplicationNeighbor per partner and partition, with SourceServer,
// PartitionName, ConsecutiveFailureCount, LastSyncResult and LastSuccessfulSync. LastSuccessfulSync is a local
// time of the domain controller: the minutes since it are counted there, against its own clock.
// A link is named "<partner>/<partition>": "dc02.corp.example/DC=corp,DC=example".
need('protocol:ssh');
per('link', 'Replication links');
output('failures', 'number', array('label' => 'Failures since the last success', 'per' => 'link'));
output('result', 'number', array('label' => 'Result of the last attempt', 'per' => 'link'));
output('lag', 'number', array('label' => 'Since the last success', 'unit' => 'min', 'per' => 'link'));
watch.above('failing', 'Replication failing', 'failures', array('warning' => 0, 'critical' => 2));
// Replication inside a site follows each change within minutes; between sites it follows the schedule of
// the site link, every 180 minutes by default.
watch.above('late', 'Replication late', 'lag', array('warning' => 360, 'critical' => 1440));
watch.missing('missing', 'Replication link missing', 'failures');
$read = ssh.run("powershell -NoProfile -NonInteractive -Command \""
. "$ErrorActionPreference = 'Stop'; "
. "[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding; "
. "$computer = Get-CimInstance Win32_ComputerSystem; "
. "$server = $computer.DNSHostName + '.' + $computer.Domain; "
. "$context = New-Object DirectoryServices.ActiveDirectory.DirectoryContext('DirectoryServer', $server); "
. "$dc = [DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($context); "
. "$now = Get-Date; "
. "$links = @($dc.GetAllReplicationNeighbors() | ForEach-Object {"
. " @{source = $_.SourceServer; partition = $_.PartitionName;"
. " failures = $_.ConsecutiveFailureCount; result = $_.LastSyncResult;"
. " minutes = [long]($now - $_.LastSuccessfulSync).TotalMinutes} }); "
. "ConvertTo-Json -Compress -Depth 4 -InputObject @{links = $links}\"");
$answer = text.trim($read['output']);
if (list.count(text.match($answer, '^\{')) == 0) {
fail('misconfigured', 'PowerShell did not return the replication partners', $answer . $read['errors']);
}
$replication = json.read($answer);
// A partner whose server object was deleted has no name any more.
$links = 0;
foreach ($replication['links'] as $link) {
$source = $link['source'];
if ($source == '') {
$source = '(deleted partner)';
}
$name = $source . '/' . $link['partition'];
$links = $links + 1;
value('failures', $link['failures'], $name);
value('result', $link['result'], $name);
value('lag', $link['minutes'], $name);
}
if ($links == 0) {
fail('misconfigured', 'This domain controller has no inbound replication partner', $answer);
}