Finding Orphaned Domain Controllers in Active Directory Sites and Services

Hi all. Dougga here after a brief silence of not blogging. Not short on ideas, but writing to such an astute audience is a fun challenge. Your feedback and following is greatly appreciated. Enough of the accolades – you are all great.

I am writing this blog about orphaned domain controllers. So, what is an orphaned domain controller and how does it happen? An orphaned domain controller is a domain controller that has a server object in the Configuration container (that is where sites are stored and what the KCC uses to calculate the topology), but does not have a corresponding computer account in the domain controller organizational unit. I have written a script to locate these orphaned domain controllers.

This is not something that stops replication or causes any major headaches. However, here are some potential impacts:

The Knowledge Consistency Checker (KCC). The KCC runs every 15 minutes on every domain controller.

The KCC has to consider all servers in the configuration partition – including the orphaned domain controller.

If the KCC decides to move a connection object due to connectivity issues, it could elect the orphaned domain controller, but will fail and eventually find a working domain controller.

Another possible impact is the Inter-Site Topology Generator (ISTG) when electing bridgehead servers.

Similar to the KCC role, the ISTG in this case is electing partners in other sites to fullfill the replication needs of its site. So, again, the orphaned domain controller is on that list of potential candidates.

Promoting a replacement domain controller can have mixed result from working to failing.

This orphan condition can occur easily with a failed hard drive and an unsuccessful attempt to remove the metadata from the forest. It could also be from someone accidentally deleting the computer account from the domain controller organizational unit and not cleaning up the metadata. These are easy to spot in Sites and Services if you have a small environment. But consider if you have several hundred domain controllers spread over hundreds of sites. This script will find the orphaned domain controllers very quickly for you.

I created an orphan in my lab to demo an orphaned domain controller. Here is a sample of what the output of the script with an orphaned domain controller might look like:

PS C:\PS> .\FindingOrphanedDCs.ps1

Looking for orphaned domain controllers in the contoso.com domain

4 server objects were found in the Configuration partition.
3 domain controllers were found in the domain partition.
===
1 potentially orphaned domain controllers

Orphaned (found in config, but not domain)

Domain controller
———————
DG12R2-CLONE

PS C:\PS>>

This is sample of what a clean output from the script might look like:

.\FindingOrphanedDCs.

Looking for orphaned domain controllers in the contoso.com domain

4 server objects were found in the Configuration partition.
4 domain controllers were found in the domain partition.
===
0 potentially orphaned domain controllers

No orphaned domain controllers were found
PS C:\PS>>

If you promote a domain controller in the domain and reuse a domain controller’s name with its metadata still in sites and services, you can get mixed results. I was just able to build a new machine with the same name as my orphaned domain controller (DG12R2-CLONE ) and promote it successfully.   However, the replication topology took about 30 minutes to settle down (meaning it was working, but with one error).  In normal circumstances, I would have expected a domain controller to be promoted without any of these errors.  However, I was seeing this error when running repadmin /showrepl and still did several hours later. But, oddly, replication was working otherwise.

DsReplicaGetInfo() failed with status 8453 (0x2305):
Replication access was denied.

I would be reluctant to be “okay” with errors from a newly promoted domain controller. If I found this condition, I would recommend demoting the domain controller and verify all the metadata is removed. Then follow promotion process again with a clean sites and services (aka: no orphaned domain controllers).

As with any and all scripts, this script is provided as sample and should be approved by your processes and tested.

Finally, to the script.

Dougga “don’t like orphaned domain controllers”


#Disclaimer The sample scripts are not supported under any
#Microsoft standard support program or service. The sample
#scripts are provided AS IS without warranty of any kind.
#Microsoft further disclaims all implied warranties
#including, without limitation, any implied warranties of
#merchantability or of fitness for a particular purpose.
#The entire risk arising out of the use or performance of
#the sample scripts and documentation remains with you.
#In no event shall Microsoft, its authors, or anyone else
#involved in the creation, production, or delivery of the
#scripts be liable for any damages whatsoever (including,
#without limitation, damages for loss of business profits,
#business interruption, loss of business information, or
#other pecuniary loss) arising out of the use of or
#inability to use the sample scripts or documentation,
#even if Microsoft has been advised of the possibility of
#such damages.
#
#
#Find Orphaned Server objects in Sites and services.
#This script first finds all the server objects in the
#Sites container in the configuration partition.
#It then collects all the domain controllers in the
#domain partition. I place these two in hash tables
#and compare the contents using the Server Objects
#from the Configuration partition as the reference.
#
#So the simple math here is if it in the Configuration
#partition, I expect it to also be in the Domain partition.
#
import-module activedirectory
$DomainName=(Get-ADDomain).DNSRoot
$d="*$DomainName"
$cp=(get-adrootdse).configurationNamingContext # getting configuration partition

Write-Output "`nLooking for orphaned domain controllers in the $DomainName domain'n"

$svrs=Get-ADObject -Filter {(ObjectClass -eq 'server') -and `
(dNSHostName -like $d) } -SearchBase "CN=Sites,$cp" | select name | sort name
$svrs_H = $svrs | Group-Object -Property name -AsHashTable -AsString -NoElement

$DCs = Get-ADDomainController -filter * | sort name
$DCs_H = $DCs | Group-Object -Property name -AsHashTable -AsString -NoElement

$i = $svrs_H.keys | Measure-Object
$j = $DCS_H.keys | Measure-Object

$x= $i.count
$y= $j.count
$NumOrph= $x-$y

Write-Output "$x server object(s) were found in the configuration partition."
Write-Output "$y domain controller(s) were found in the domain partition."
Write-Output "===="
Write-Output "$NumOrph potentially orphaned domain controllers"

<# Write text copies of Servers and DCs #>

Get-ADDomainController -Filter * | Select name | Sort-Object name | `
Out-File ".\alldcs.txt"

Get-ADObject -Filter {(ObjectClass -eq 'server') -and `
(dNSHostName -like $d) } -properties * -SearchBase "CN=Sites,$cp" | `
select Name | sort-object Name | Out-File ".\allsvrs.txt"

(get-content .\alldcs.txt) | foreach {$_ -replace " ", ""} | `
Set-Content .\alldcs.txt
(get-content .\allsvrs.txt) | foreach {$_ -replace " ", ""} | `
Set-Content .\allsvrs.txt

$compare=compare-object -ReferenceObject (get-content .\allsvrs.txt) `
-DifferenceObject (Get-Content .\alldcs.txt)

if($compare)
{
Write-Output ""
Write-Output "Orphaned (found in config, but not domain)"
$compare | select @{Name="Domain Controller";Expression={$_.inputobject}}
}
Else
{

Write-Output "'nNo orphaned domain controllers were found"
}

#===