The following version is not less verbose (could probably be written a lot more terse, but I'm hoping the script is at least semi-readable), but it does a search for the group and returns the Active Directory group objects for each group along the branch in which the group was found.
function Get-GroupConnection
{
[CmdletBinding()]
PARAM (
$Username,
$GroupName
)
$User = Get-AdUser -Identity $Username -Properties MemberOf
if (-Not ($User))
{
return;
}
$SearchedGroups = @()
function Find-GroupBranches
{
[CmdletBinding()]
PARAM (
$GroupNameList,
$SearchForGroupName
)
$ADGroups = $GroupNameList | Foreach { Get-ADGroup -Identity $_ -Properties MemberOf }
foreach($group in $ADGroups)
{
Write-Verbose "Testing if either '$($Group.SamAccountName)' or '$($Group.DistinguishedName)' are equal to '$SearchForGroupName'"
if ($Group.SamAccountName -eq $SearchForGroupName -OR $Group.DistinguishedName -eq $SearchForGroupName)
{
Write-Verbose "Found $($Group.DistinguishedName)"
Write-Output $Group
return
}
}
Write-Verbose "No match in current collection, checking children"
foreach ($currentGroup in $ADGroups)
{
if ($SearchedGroups -Contains $currentGroup.DistinguishedName)
{
Write-Verbose "Already checked children of '$($currentGroup.DistinguishedName)', ignoring it to avoid endless loops"
continue
}
$SearchedGroups += $currentGroup.DistinguishedName
if ($currentGroup.MemberOf)
{
Write-Verbose "Checking groups which $($currentGroup.DistinguishedName) is member of"
$foundGroupInTree = Find-GroupBranches -GroupNameList $currentGroup.MemberOf -SearchForGroupName $SearchForGroupName
if ($foundGroupInTree)
{
Write-Output $currentGroup
Write-Output $foundGroupInTree
break
}
}
else
{
Write-Verbose "$($currentGroup.DistinguishedName) is not member of any group, branch ignored"
}
}
}
Write-Verbose "Searching immediate group membership"
Find-GroupBranches -GroupNameList $User.MemberOf -SearchForGroupName $GroupName
}
Get-GroupConnection -Username MyUser -GroupName SubSubGroup -Verbose
A description of how it searches follows.
Given the following Active Directory structure:
MyUser
Domain Admins
AnotherSubGroup
Other Group
DirectMemberGroup
Domain Admins (same group as MyUser is direct member of, above)
AnotherSubGroup (which is of course the same as above too)
SubGroup
SubSubGroup
Some Other Group
If we search for the connection between MyUser and 'SubSubGroup' the script would search first the direct membership of the MyUser user, i.e. 'Domain Admins', 'Other Group', 'DirectMemberGroup' and 'Some Other Group'. None of these match the 'SubSubGroup' we search for, so it starts checking 'child'groups.
'Domain Admins' is member of 'AnotherSubGroup', but that does not match 'SubSubGroup'. 'AnotherSubGroup' is not member of any group, so that branch is ignored.
'Other Group' is not member of any group, so that branch are ignored.
'DirectMemberGroup' is member of other groups, so it iterates through those groups. It has already checked 'Domain Admins' for children, so that group is skipped to avoid getting stuck in a circular search. Therefore it check 'SubGroup'.
'SubGroup' does not match 'SubSubGroup' so it check the groups which 'SubGroup' is member of. 'SubGroup' is member of 'SubSubGroup', so it checks that group.
'SubSubGroup' does match 'SubSubGroup' and will therefore be chosen as a match.
In the above example, the output group object will be branches which lead to the 'SubSubGroup' group, in the following order:
DirectMemberGroup
SubGroup
SubSubGroup
Observe that this method will return the first connection it finds between the user and the group. If, for example, the 'Some Other Group' group would also be a member of 'SubSubGroup' this would not change the output, nor the search process, mentioned above.