powershellactive-directory

How to Compare Sets of Users


I am trying to query a domain for all users from an OU, in a specific group, and not in another specific group.

First, I query Active Directory for all three sets of users: the OU, group to include, and group to exclude. The group to exclude happens to be a distribution group, so I had to query for it a different way.

$OU = Get-ADUser -Filter * -SearchBase "OU=Developers,OU=Everyone,DC=Organization,DC=com" -Properties MemberOf
$include = Get-ADGroupMember -Identity "this group"
$exclude = Get-ADObject -Filter 'Name -eq "that group"' -Properties member | Select-Object -ExpandProperty member | ForEach-Object { Get-ADUser -Identity $_ }

I tested the command for each variable, and they all work on their own. It must be the logic comparing them that is bad, or the output is too different to compare.

I tried comparing them with a foreach loop.

foreach ($user in $OU) {
    if (($include -contains $user.DistinguishedName) -and (-not $exclude -contains $user.DistinguishedName)) {
        Write-Host $user.SAMAccountName
    }
}

..and by piping them through Where-Object.

$filteredUsers = $OU | Where-Object {
    ($include -contains $_.DistinguishedName) -and
    -not ($exclude -contains $_.DistinguishedName)
}

But neither of these statements return output. What am I doing wrong? Is my logic flawed, or am I comparing the wrong values?


Solution

  • It's much easier and more efficient if you use LDAP to get your users instead of filtering client side:

    $groupToInclude = (Get-ADGroup groupToInclude).DistinguishedName
    $groupToExclude = (Get-ADGroup groupToExclude).DistinguishedName
    $getADUserSplat = @{
        LDAPFilter = "(&(memberOf=$groupToInclude)(!memberOf=$groupToExclude))"
        SearchBase = 'OU=Developers,OU=Everyone,DC=Organization,DC=com'
    }
    # All users in OU `Developers` that are a member of `$groupToInclude`
    # AND NOT a member of `$groupToExclude`
    Get-ADUser @getADUserSplat
    

    If you need to get users that are a member of $groupToInclude and not member of $groupToExclude recursively you just need to add the LDAP_MATCHING_RULE_IN_CHAIN operator to your clauses:

    "(&(memberOf:1.2.840.113556.1.4.1941:=$groupToInclude)(!memberOf:1.2.840.113556.1.4.1941:=$groupToExclude))"
    

    If you wanted to filter client side, which again, is much more inefficient, you could've done:

    $groupToInclude = (Get-ADGroup groupToInclude).DistinguishedName
    $groupToExclude = (Get-ADGroup groupToExclude).DistinguishedName
    $getADUserSplat = @{
        Filter     = '*'
        SearchBase = 'OU=Developers,OU=Everyone,DC=Organization,DC=com'
        Properties = 'MemberOf'
    }
    Get-ADUser @getADUserSplat | Where-Object {
        $_.MemberOf -notcontains $groupToExclude -and
        $_.MemberOf -contains $groupToInclude
    }