After sending a search request to an LDAP server, it's a good idea to check that:
- The server successfully responded.
- At least one record was returned.
- The server responded to any submitted control requests.
So, before we iterate through the results in the $searchResponse variable, we perform these three checks, as callout A in Listing 3 shows.
Listing 3: Code That Checks and Processes the Search Results |
 |
First, we first check to see if the LDAP server returned a result code of "success". Then, we check to see if there's at least one record (i.e., an entry) in the search response. Finally, we check to see if the server returned a sort response control. If the server doesn't return a sort response control, it's probably incapable of sorting the results.
After performing these checks, we iterate through the returned entries, as callout B in Listing 3 shows. We take advantage of the last in/first out properties of a stack to maintain the parent/child relationship between the member groups. When a child member group is found, it's added to the stack after its parent. When it's time to render the relationships, the parent/child relationships are already in order and we just have to pop the entries off the stack. After the code adds the child member group to the stack, it calls the GetMembers function for each group.
The GetMembers function sets up a search request similar to the one used in Listing 2. However, the search scope and the control differ. Instead of setting the search scope to OneLevel, it's set to Base so that the member attribute of each AD group object is searched. The function uses an attribute scoped query (ASQ) control instead of the sort request control in order to search the member attribute of each group.
As we did in the first search operation, we then check to see if the LDAP server returned a result code of "success", returned at least one entry in the response, and returned an ASQ control. If the response contains any entries, we recursively call the GetMembers function.
If no entries are in the response, we render the output with the code in Listing 4.
Listing 4: Code That Outputs the Group Hierarchy |
 |
To properly render the group hierarchy, we invert the stack, as callout A in Listing 4 shows. Once that's completed, we use a .NET string object to assemble the output string. The entries are concatenated to the $stackOutputter variable as they are popped off the stack, as shown in callout B. This code renders the output to the screen, but you can easily modify it to send the output to a file or pipe the output to some other location.
Take Advantage of the .NET Framework
As GetGroupRelationships.ps1 demonstrates, you can extend PowerShell's capabilities by taking advantage of the .NET Framework. If you work with non-Microsoft LDAP directories, one particularly useful .NET tool is S.DS.P. This namespace frees you from the idiosyncrasies of ADSI and ultimately lets you work with any LDAP directory, not just AD.