Active Directory - list full subgroup dependency of a given group and omit subgroups without any user

210 views Asked by At

I have to list full subgroup dependency of specific group - filter -> only subgroups which contain at least 1 user.

I have tried this approach:

dsquery group -samid <specific_group> | dsget group -members -expand  | dsquery * -filter "(&(objectclass=group))"
2

There are 2 answers

2
Santiago Squarzon On

Here is an easy alternative leveraging Active Directory filtering capabilities. See inline comments to understand the logic.

Do note, this answer requires the ActiveDirectory Module available.

# Get the DN of the parent group (this is the initial group)
$parent = (Get-ADGroup parentGroup).distinguishedName
$param = @{
    LDAPFilter = "(memberof:1.2.840.113556.1.4.1941:=$parent)"
    Properties = "member"
}
# Find, recursively, all child groups Members Of this parent group
# and filter
Get-ADGroup @param | Where-Object {
    # For each member of this child group,
    # check if this member is an object of the Class `user`,
    # if it is, we already can break the pipeline and return
    # this child group, since we already know it has
    # at least 1 user member
    $_.member | Get-ADObject | Where-Object ObjectClass -EQ user |
        Select-Object -First 1
}
4
Theo On

This is an adapted version of my answer here to omit groups without any user object.

Unfortunately, using Get-ADGroupMember together with switch -Recursive will not return members that are groups.
As the docs state:

If the Recursive parameter is specified, the cmdlet gets all members in the hierarchy of the group that do not contain child objects.

To get an array of nested group objects within a certain parent group, you will need a recursive function like below:

function Get-NestedADGroup {
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [Alias ('Identity')]
        [string]$Group,
        # the other parameters are optional
        [string]$Server      = $null,
        [string]$SearchBase  = $null,
        [ValidateSet('Base', 'OneLevel', 'Subtree')]
        [string]$SearchScope = 'Subtree'
    )

    $params = @{
        Identity    = $Group
        SearchScope = $SearchScope
        Properties  = 'Members'
        ErrorAction = 'SilentlyContinue'
    }  
    if (![string]::IsNullOrWhiteSpace($Server))     { $params['Server'] = $Server }
    if (![string]::IsNullOrWhiteSpace($SearchBase)) { $params['SearchBase'] = $SearchBase }

    $adGroup = Get-ADGroup @params
    if ($adGroup) {
        if (-not $script:groupsHash.ContainsKey($Group)) {
            # output this group object only if it has at least one user object
            if (@($adGroup.Members | Where-Object {$_.objectClass -eq 'user'}).Count -gt 0) {
                $adGroup
            }
            # avoid circular group references
            $script:groupsHash[$Group] = $true
            # and recurse to get the nested groups
            foreach ($group in ($adGroup.Members | Where-Object {$_.objectClass -eq 'group'})) {
                Get-NestedADGroup -Group $group.DistinguishedName -Server $Server -SearchBase $SearchBase
            }
        }
    }
    else {
        Write-Warning "Group '$($Group)' could not be found.."
    }
}

# create a Hashtable to avoid circular nested groups
$groupsHash = @{}

# call the function
$result = Get-NestedADGroup -Group 'SpecificGroup'

# output just the names if you like
$result.Name

# save to CSV
$result | Export-Csv -Path 'X:\Somewhere\SubgroupsWithAtLeastOneUser.csv' -NoTypeInformation