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
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:
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
0 comments