Active Directory
banshee/active-directory-collection/active-directory/winrm/
fsmo
The holder of each of the five FSMO roles and whether it answers on the LDAP port, by WinRM and PowerShell
by banshee1 installation2 versions
// The five FSMO roles of the forest and of the domain of a domain controller, by WinRM: the domain controller
// that holds each one, as this domain controller knows it, and whether that holder accepts a connection on
// the LDAP port, 389, from this domain controller.
// Runs one PowerShell script that only reads, through the classes of .NET that Microsoft documents:
// DomainController.GetDomainController, then Forest.SchemaRoleOwner and NamingRoleOwner, Domain.PdcRoleOwner,
// RidRoleOwner and InfrastructureRoleOwner. A holder is reached by a TCP connection that sends nothing, given
// up after 3 seconds: a bind with the account would also depend on whether the remote session can hand its
// credentials on to another server, which a WinRM session authenticated by NTLM cannot.
// The script is the one of active-directory/ssh/fsmo, without the powershell -Command "..." that cmd.exe
// needs there.
need('protocol:winrm');
per('role', 'Roles');
output('holder', 'text', array('label' => 'Holder', 'per' => 'role'));
output('reachable', 'text', array('label' => 'Reachable', 'per' => 'role'));
watch.state('unreachable', 'Role holder unreachable', 'reachable', array('critical' => array('no')));
$read = winrm.run("$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); "
. "$holders = [ordered]@{"
. "schema = $dc.Forest.SchemaRoleOwner.Name; "
. "'domain-naming' = $dc.Forest.NamingRoleOwner.Name; "
. "pdc = $dc.Domain.PdcRoleOwner.Name; "
. "rid = $dc.Domain.RidRoleOwner.Name; "
. "infrastructure = $dc.Domain.InfrastructureRoleOwner.Name}; "
. "$roles = @(foreach ($role in $holders.Keys) {"
. " $reachable = $false;"
. " try { $tcp = New-Object Net.Sockets.TcpClient;"
. " $reachable = $tcp.BeginConnect($holders[$role], 389, $null, $null).AsyncWaitHandle.WaitOne(3000) -and $tcp.Connected;"
. " $tcp.Close() } catch { };"
. " @{role = $role; holder = $holders[$role]; reachable = $reachable} }); "
. "ConvertTo-Json -Compress -Depth 4 -InputObject @{roles = $roles}");
$answer = text.trim($read['output']);
if (list.count(text.match($answer, '^\{')) == 0) {
fail('misconfigured', 'PowerShell did not return the FSMO roles', $answer . $read['errors']);
}
$fsmo = json.read($answer);
$roles = 0;
foreach ($fsmo['roles'] as $role) {
$roles = $roles + 1;
value('holder', $role['holder'], $role['role']);
if ($role['reachable']) {
value('reachable', 'yes', $role['role']);
} else {
value('reachable', 'no', $role['role']);
}
}
if ($roles != 5) {
fail('misconfigured', 'PowerShell did not return the five FSMO roles', $answer);
}