October 5th, 2009

Hey, Scripting Guy! How Can I Get a List of Domain Controllers in My Domain? (Part 1)

Bookmark and Share

Hey, Scripting Guy! Question

Hey, Scripting Guy! I hope you do not think I am clueless, but I need to obtain a list of all the domain controllers in my domain. Can you help me out by writing a Windows PowerShell script to list all of the domain controllers in my domain?

<

p style=”MARGIN: 0in 0in 8pt” class=”MsoNormal”>– KH

Hey, Scripting Guy! Answer

Hello KH,

Microsoft Scripting Guy Ed Wilson here. Fall has come to Charlotte, North Carolina, in the United States. This morning it was 60 degrees Fahrenheit (15.5 degrees Celsius) (I determined this by using my temperature conversion HTA). The skies are blue, and the leaves are rustling around on the ground. It is my favorite time of the year. Couple this with the fact I was on vacation last week (I attended a wood working class in the Great Smoky Mountains), and I am feeling relaxed and susceptible to suggestion. I most certainly do not think you are clueless. In fact, I have heard this question before.

Sometime ago, the Scripting Guys wrote a script in VBScript that listed domain controllers by querying the schema configuration container. The How Can I Get a List of All the Domain Controllers in My Domain? article was a huge success, with calls from all the major news services for permission for reprints (if you count our moms as major news services). The FindDomainControllersFromNamingContext.vbs script from the Hey Scripting Guy! article is seen here.

FindDomainControllersFromNamingContext.vbs

On Error Resume Next

Const ADS_SCOPE_SUBTREE = 2

Set objRootDSE = GetObject(“LDAP://RootDSE”)
strConfigurationNC = objRootDSE.Get(“configurationNamingContext”)

Set objConnection = CreateObject(“ADODB.Connection”)
Set objCommand =   CreateObject(“ADODB.Command”)
objConnection.Provider = “ADsDSOObject”
objConnection.Open “Active Directory Provider”
Set objCommand.ActiveConnection = objConnection

objCommand.Properties(“Page Size”) = 1000
objCommand.Properties(“Searchscope”) = ADS_SCOPE_SUBTREE

objCommand.CommandText = _
    “SELECT ADsPath FROM ‘LDAP://” & strConfigurationNC & “‘ WHERE objectClass=’nTDSDSA'” 
Set objRecordSet = objCommand.Execute

objRecordSet.MoveFirst
Do Until objRecordSet.EOF
    Set objParent = GetObject(GetObject(objRecordset.Fields(“ADsPath”)).Parent)
    WScript.Echo objParent.CN
    objRecordSet.MoveNext
Loop

The FindDomainControllersFromSchema.vbs script uses ADO to query the configurationNamingContext from Active Directory. The same technique can be done using Windows PowerShell. The technique of using the configurationNamingContext is illustrated in the FindDomainControllerFromNamingContext.ps1 script seen here.

FindDomainControllerFromNamingContext.ps1

$Config = ([adsi]”LDAP://rootdse”).ConfigurationNamingContext
$dse = [adsi]”LDAP://$config”
$searcher = New-Object DirectoryServices.DirectorySearcher ($dse, “ObjectClass=nTDSDSA”)
$searcher.findall() |
ForEach-Object{ ([adsi]$_.path).parent }

The first thing that is done in the FindDomainControllerFromNamingContext.ps1 script is retrieve the ConfigurationNamingContext. To do this, the ADSI type accelerator is used to retrieve the rootdse from the LDAP provider. When the ADSI type accelerator is used to retrieve the rootdse from the LDAP provider, an instance of the DirectoryServices.DirectoryEntry class is returned. As seen here, the DirectoryEntry object for the rootdse has the ConfigurationNamingContext property:

PS C:> [adsi]”LDAP://rootdse”


distinguishedName : {}
Path              : LDAP://rootdse

PS C:> [adsi]”LDAP://rootdse” | Get-Member


   TypeName: System.DirectoryServices.DirectoryEntry

Name                          MemberType Definition
—-                          ———- ———-
ConvertDNWithBinaryToString   CodeMethod static string ConvertDNWithBinaryToStrin…
ConvertLargeIntegerToInt64    CodeMethod static long ConvertLargeIntegerToInt64(p…
configurationNamingContext    Property   System.DirectoryServices.PropertyValueCo…
currentTime                   Property   System.DirectoryServices.PropertyValueCo…
defaultNamingContext          Property   System.DirectoryServices.PropertyValueCo…
dnsHostName                   Property   System.DirectoryServices.PropertyValueCo…
domainControllerFunctionality Property   System.DirectoryServices.PropertyValueCo…
domainFunctionality           Property   System.DirectoryServices.PropertyValueCo…
dsServiceName                 Property   System.DirectoryServices.PropertyValueCo…
forestFunctionality           Property   System.DirectoryServices.PropertyValueCo…
highestCommittedUSN           Property   System.DirectoryServices.PropertyValueCo…
isGlobalCatalogReady          Property   System.DirectoryServices.PropertyValueCo…
isSynchronized                Property   System.DirectoryServices.PropertyValueCo…
ldapServiceName               Property   System.DirectoryServices.PropertyValueCo…
namingContexts                Property   System.DirectoryServices.PropertyValueCo…
rootDomainNamingContext       Property   System.DirectoryServices.PropertyValueCo…
schemaNamingContext           Property   System.DirectoryServices.PropertyValueCo…
serverName                    Property   System.DirectoryServices.PropertyValueCo…
subschemaSubentry             Property   System.DirectoryServices.PropertyValueCo…
supportedCapabilities         Property   System.DirectoryServices.PropertyValueCo…
supportedControl              Property   System.DirectoryServices.PropertyValueCo…
supportedLDAPPolicies         Property   System.DirectoryServices.PropertyValueCo…
supportedLDAPVersion          Property   System.DirectoryServices.PropertyValueCo…
supportedSASLMechanisms       Property   System.DirectoryServices.PropertyValueCo…

When the ConfigurationNamingContext property is queried, a string is returned. This is shown here:

PS C:>

 

PS C:> ([adsi]”LDAP://rootdse”).ConfigurationNamingContext
CN=Configuration,DC=NWTraders,DC=Com
PS C:> ([adsi]”LDAP://rootdse”).ConfigurationNamingContext | Get-Member


   TypeName: System.String

Name             MemberType            Definition
—-             ———-            ———-
Clone            Method                System.Object Clone()
CompareTo        Method                int CompareTo(System.Object value), int Co…
Contains         Method                bool Contains(string value)
CopyTo           Method                System.Void CopyTo(int sourceIndex, char[]…
EndsWith         Method                bool EndsWith(string value), bool EndsWith…
Equals           Method                bool Equals(System.Object obj), bool Equal…
GetEnumerator    Method                System.CharEnumerator GetEnumerator()
GetHashCode      Method                int GetHashCode()
GetType          Method                type GetType()
GetTypeCode      Method                System.TypeCode GetTypeCode()
IndexOf          Method                int IndexOf(char value), int IndexOf(char …
IndexOfAny       Method                int IndexOfAny(char[] anyOf), int IndexOfA…
Insert           Method                string Insert(int startIndex, string value)
IsNormalized     Method                bool IsNormalized(), bool IsNormalized(Sys…
LastIndexOf      Method                int LastIndexOf(char value), int LastIndex…
LastIndexOfAny   Method                int LastIndexOfAny(char[] anyOf), int Last…
Normalize        Method                string Normalize(), string Normalize(Syste…
PadLeft          Method                string PadLeft(int totalWidth), string Pad…
PadRight         Method                string PadRight(int totalWidth), string Pa…
Remove           Method                string Remove(int startIndex, int count), …
Replace          Method                string Replace(char oldChar, char newChar)…
Split            Method                string[] Split(Params char[] separator), s…
StartsWith       Method                bool StartsWith(string value), bool Starts…
Substring        Method                string Substring(int startIndex), string S…
ToCharArray      Method                char[] ToCharArray(), char[] ToCharArray(i…
ToLower          Method                string ToLower(), string ToLower(System.Gl…
ToLowerInvariant Method                string ToLowerInvariant()
ToString         Method                string ToString(), string ToString(System….
ToUpper          Method                string ToUpper(), string ToUpper(System.Gl…
ToUpperInvariant Method                string ToUpperInvariant()
Trim             Method                string Trim(Params char[] trimChars), stri…
TrimEnd          Method                string TrimEnd(Params char[] trimChars)
TrimStart        Method                string TrimStart(Params char[] trimChars)
Chars            ParameterizedProperty char Chars(int index) {get;}
Length           Property              System.Int32 Length {get;}


PS C:>

The line of code that retrieves the ConfigurationNamingContext from rootdse is seen here:

$Config = ([adsi]”LDAP://rootdse”).ConfigurationNamingContext

To be able to use the ConfigurationNamingContext, the string needs to be translated to a DirectoryEntry object. To do this, the ADSI type accelerator is once again used:

$dse = [adsi]”LDAP://$config”

After the DirectoryEntry object has been created for the ConfigurationNamingContext, it is used with the DirectoryServices.DirectorySearcher .NET Framework class to retrieve instances of the nTDSDSA object class. The nTDSDSA object represents the replication agent on a domain controller. Because this object is invariant for the lifetime of the domain controller, the object GUID value can be used as an alternative identifier for the domain controller. More information about this object can be found on MSDN. Because the nTDSDSA object is found in the ConfigurationNamingContext, you will need to tell the DirectorySearcher to begin looking in that context. To do this, you use an overloaded constructor that accepts a DirectoryEntry class in the first position and a string for the filter in the second position. The constructors for the DirectorySearcher class are documented on MSDN.

$searcher = New-Object DirectoryServices.DirectorySearcher ($dse, “ObjectClass=nTDSDSA”)

After you have created the searcher object, the findall method is used to retrieve all instances of objects in Active Directory that meet the filter criteria. Other methods from the DirectorySearcher object are documented on MSDN. The results from the findall method are pipelined to the Foreach-Object cmdlet. This is seen here:

$searcher.findall() |

The Foreach-Object cmdlet is used to examine each object as it comes down the Windows PowerShell pipeline. When an object enters the pipeline, the ADSI type accelerator is used to retrieve the parent property of each nTDSDSA object that is retrieved. This is seen here:

ForEach-Object { ([adsi]$_.path).parent }

As seen in the following image, two domain controllers are returned for the NWTraders.com domain. The first domain controller is named HYPERV and the second domain controller is named SERVER1:

Image of the two domain controllers thare are returned


KH, that is all there is to querying the configuration context in Active Directory to retrieve a list of the domain controllers. Join us tomorrow for Part 2, as we continue working with Active Directory.

If you want to know exactly what we will be scripting tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

 

Author

0 comments

Discussion are closed.