PowerShell How-To

Working with Recursive Functions in PowerShell

Recursive functions are designed perfectly to manage what could be a hierarchical mess in Active Directory.

When working with Active Directory groups, a common requirement is to enumerate all members of several different groups. This is done to typically figure out what kind of permissions each user in that group has. However, an administrator soon realizes that just checking group membership is not enough since user accounts are not the only object inside of groups but other groups can be members as well. This means that in order to get an accurate representation of all of the groups a user account is in, she must also enumerate those nested groups as well.

This is one of the most common examples of the need for recursive functions. Recursive functions, at their simplest, are functions that call themselves. These types of functions are used in the example above to enumerate a hierarchical structure like AD groups inside of other AD groups with user accounts inside of those. A recursive function is an excellent way to walk through each of these objects.

Using the example provided above, let's get to coding. Let's first develop a small script that queries for all user accounts in a group. This is an easy, one-liner for the Get-AdGroupMember command.

Get-ADGroupMember  -Identity 'Domain Admins' | Select-Object name,objectClass
Figure 1.

You can see that my Domain Admins group has a user and a computer account inside. Let's now make things interesting and nest a group inside of Domain Admins. I'll add my IT Admins group since I need all users in that group to be a domain admin and query the group membership again.

Get-ADGroupMember -Identity 'Domain Admins' | Select-Object  name,objectClass
Figure 2.

If I'm building a report to investigate who has domain admin rights in the domain, I now don't have a complete list. Since all nested groups inherit permissions from parent groups, this means that all users inside of the IT Admins group are now members of the Domain Admins group. How can I modify my code to do this? One way would be first to detect if the object inside of the group is another group and, if so, then enumerate that group.

$daMembers = Get-ADGroupMember -Identity 'Domain Admins' |  Select-Object name,objectClass
$daMembers | foreach {  if ($_.objectClass  -eq 'group') { Get-AdGroupMember -Identity $_.name } else { $_ } }

Below you can see that jschmoe and bfischer are members of the IT Admins group.

Figure 3.

This would work but what if there's another nested group inside of the IT Admins group? Then another group inside of that, and another group inside of that.... You get my point. By trying to use your previous strategy of just calling Get-ADGroupMember again could turn into something like this. Then would only work if no one nests another group inside of the smallest child! This isn't sustainable.

$daMembers = Get-ADGroupMember -Identity 'Domain Admins'
$daMembers | foreach { 
if ($_.objectClass  -eq 'group') {
Get-AdGroupMember -Identity $_.name | foreach {
if ($_.objectClass  -eq 'group') {
Get-AdGroupMember -Identity $_.name | foreach {
...........
}
} else {
$_
}
}
}
} else {
$_
}
}

Instead, let's build a recursive function around this so that it can call upon itself if it detects a group. This way it doesn't matter how many nested groups exist in this chain. It will just recognize the group, enumerate the members and continue on.

function  Get-DomainAdminUserRights {
param (
[Parameter()]
[string]$GroupName = 'Domain Admins'
)

    $daMembers = Get-ADGroupMember -Identity $GroupName
$daMembers | foreach { 
if ($_.objectClass  -eq 'group') {
## Run the function again which will check for the group, call the function
## again, if necessary and output the non-group objects
Get-DomainAdminUserRights -GroupName $_.name
} else {
## Send the non-group object out
[pscustomobject]@{
Name = $_.Name
FromGroup = $GroupName
}
}
}
}

I can now run this function and get the exact same result as last time but this time I've added a little code to see what group each user is from. The only difference now is that I can nest as many groups as I want without changing a line of code!

Get-DomainAdminRights
Figure 4.

Notice now that by just querying for membership of the Domain Admins group that somehow the Accounting groups is in that chain! Ouch!

This is an excellent example of recursive functions. As you write more PowerShell scripts, there will be times when recursive functions come in handy. Recursive functions work best when working with hierarchical relationships like this. These functions allow you to write code once and, no matter what's changed in the environment, no further changes will be necessary for your code.

About the Author

Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.


comments powered by Disqus
Most   Popular