23 June, 2015

Find PDC in a domain (not just current domain) - AD

Here's a question for a nice outage on a quiet spring evening, you've got issues with a PDC, you want to make sure you check out all the PDCs in all your Active Directory domains, how can you find the PDCs quickly in each domain without running nltest query fsmo against all domains one by one?

Side note: The Active Directory Domain Controller which holds the PDC (Primary Domain Controller) role in a Domain is not uber-critical to be up and running in every minute, but if it's up and not performing well...well that's a different issue. If the PDC is down, that only means the password change sync is slow, so users may get weird behavior when changing password and using that against another domain controller in a short period of time. The other roles like primary time source, GPO and DFS master server in case of replication conflicts...etc. are not massively important.
The biggest issue is when it's sporadically responding or sometimes doesn't, that makes the behavior of applications contacting the PDC unpredictable and hard to troubleshoot.

First step, let's list our PDCs in all domains. If you have several forests and domains it's not that easy as it sounds first. There are several ways and if you have Windows 2012 R2 with AD tools installed, it's fairly easy:

PS C:\> Import-Module ActiveDirectory
PS C:\> (Get-ADForest -identity tatooine.com).domains | %{(Get-ADDomain -server $_).PDCEmulator

But if you want to make sure you have a script which gets the PDC of a specified domain without dependency on the ActiveDirectory PowerShell Module, here are two native ways to do it


With a normal LDAP search:
PS C:\> $domainDN = "dc=tatooine,dc=com"
PS C:\> $searchRoot = new-object System.DirectoryServices.DirectoryEntry(LDAP://$domainDN)
PS C:\> $searcherObj = new-object Systen.DirectoryServices.DirectorySearcher
PS C:\> $searcherObj.Filter = ("(objClass=top)")
PS C:\> $tmpstr = $searcherObj.SearchRoot.Properties.Item("fsmoroleowner").Value
PS C:\> $pdc = $tmpstr.split(",")[1].split("=")[1]


Via .net Directory context:
PS C:\> $domainFQDN = "tatooine.com"
PS C:\> $context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$domainFQDN)
PS C:\> $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($context)
PS C:\> $domain.pdcRoleOwner


Advanced stuff, list all PDCs of all domains in 1 forest:
PS C:\> $ForestRootDomainFQDN = "tatooine.com"
PS C:\> $context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest",$ForestRootDomainFQDN)
PS C:\> $forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($context)
PS C:\> $forest.Domains | %{$_.pdcRoleOwner.name}

You've got the names of the PDCs, so you can go and fix them ;)

t