Do you know the structure of your Active Directory forest? You could use a tool such as ADSI Edit and expand all of the OUs and containers in each domain, but if you have a lot of OUs, this would be very time consuming.
With a pretty simple script, you can enumerate all the domains, OUs, and containers in a forest. And you don’t need any type of privileged rights to do it. Here is the script:
' This code prints out the forest tree hierarchy
' BEGIN SECTION 1
set objRootDSE = GetObject("LDAP://RootDSE")
strBase = "
objRootDSE.Get("ConfigurationNamingContext") & ">;"
strFilter = "(&(objectcategory=crossRef)(systemFlags=3));"
strAttrs = "name,trustParent,nCName,dnsRoot,distinguishedName;"
strScope = "onelevel"
set objConn = CreateObject("ADODB.Connection")
objConn.Provider = "ADsDSOObject"
objConn.Open "Active Directory Provider"
set objRS = objConn.Execute(strBase & strFilter & strAttrs & strScope)
objRS.MoveFirst
' END SECTION 1
' BEGIN SECTION 2
set dicSubDomainTrue = CreateObject("Scripting.Dictionary")
set dicDomainHierarchy = CreateObject("Scripting.Dictionary")
set dicDomainRoot = CreateObject("Scripting.Dictionary")
' END SECTION 2
' BEGIN SECTION 3
while not objRS.EOF
dicDomainRoot.Add objRS.Fields("name").Value, objRS.Fields("nCName").Value
if objRS.Fields("trustParent").Value <> "" then
dicSubDomainTrue.Add objRS.Fields("name").Value, 0
set objDomainParent = GetObject("LDAP://" & _
objRS.Fields("trustParent").Value)
dicDomainHierarchy.Add objRS.Fields("name").Value, _
objDomainParent.Get("name")
else
dicSubDomainTrue.Add objRS.Fields("name").Value, 1
end if
objRS.MoveNext
wend
' END SECTION 3
' BEGIN SECTION 4
for each strDomain in dicSubDomainTrue
if dicSubDomainTrue(strDomain) = 1 then
DisplayDomains strDomain, "", dicDomainHierarchy, dicDomainRoot
end if
next
Function DisplayDomains ( strDomain, strSpaces, dicDomainHierachy, dicDomainRoot)
WScript.Echo strSpaces & strDomain
DisplayObjects "LDAP://" & dicDomainRoot(strDomain), " " & strSpaces
for each strD in dicDomainHierarchy
if dicDomainHierarchy(strD) = strDomain then
DisplayDomains strD, " " & strSpaces, dicDomainHierarchy, _
dicDomainRoot
end if
next
End Function
' DisplayObjects takes the ADsPath of the object to display
' child objects for and the number of spaces (indention) to
' use when printing the first parameter
Function DisplayObjects( strADsPath, strSpace)
set objObject = GetObject(strADsPath)
Wscript.Echo strSpace & objObject.Name
objObject.Filter = Array("container","organizationalUnit")
for each objChildObject in objObject
DisplayObjects objChildObject.ADsPath, strSpace & " "
next
End Function
' END SECTION 4
Let’s walk through it.
In SECTION 1, I set up an ADO query to find all domains in a forest. This is a little trickier than you might imagine. Domains are represented in the Configuration naming context as crossRef objects; but since LDAP referrals can also be created as crossRef objects, I have to look for a specific type of crossRef object, ones that have a systemFlags attribute equal to 3. This signifies Active Directory domains.
In SECTION 2, I set up three dictionary objects that I’ll use throughout the script. Here is a brief overview of each dictionary object:
dicSubDomainTrue
This is used to identify whether domains contain subdomains. The keys will be domain names and values will be 0 if the domain has no subdomains or 1 if it does. This dictionary will be used later to enumerate the domain hierarchy.
dicDomainHierarchy
This is used to store the parent domain of a subdomain. The keys will be the domain names and the values will be each domain’s parent domain name.
dicDomainRoot
This is used to store the default naming context for each domain. The keys will be the domain names and the values will be the DN of each domain’s root.
In SECTION 3, I enumerate over each of the values returned by the ADO query started in SECTION 1. I first set the domain root in the dicDomainRoot dictionary. Next, I evaluate if the trustParent attribute contains a value. If it does, then I know the domain I’m currently on has a parent domain. If TRustParent does not contain a value, I know that it is the root domain of the forest or domain tree. If it does have a parent, I set an entry for the domain in dicSubDomainTrue to 0 to signify that I haven’t found a subdomain for this domain yet. I then set an entry in dicDomainHierarchy to contain the domain and parent domain as key value pairs.
At this point, I’ve set up all the data structures I need to start printing out the structure of a forest. In SECTION 4, I start iterating over each domain in the forest. I’ll enter only the domains that are roots of their forests. Then I call the DisplayDomains function. DisplayDomains prints the name of the current domain and calls DisplayObjects to print each container and organizationalUnit object in the domain. This effectively prints the structure of that domain. After it is done with that, it starts to loop over the keys in dicDomainHierarchy to find all child domains that have the current domain set as their parent. If it finds a subdomain of the current domain, it calls DisplayDomains (recursively) on that domain and the process repeats.
Tags: forest