So today's post is going to be about using powershell to look at and compare group memberships. In this case we have some users and we want to figure out what groups they have in common.
LDAP_MATCHING_RULE_IN_CHAIN
To accomplish this we're going to have to use LDAP_MATCHING_RULE_IN_CHAIN. You can read some details on this and other search filters at this link.There are two types of LDAP_MATCHING_RULE_IN_CHAIN queries:
memberof - formatted as (memberof:1.2.840.113556.1.4.1941:=cn=Group1,OU=groups,OU,DC=contoso,DC=com) is used to find all the members of a target group.
member - formatted as (member:1.2.840.113556.1.4.1941:=cn=User1,cn=users,DC=contoso,DC=com) is used to find all groups that include the user as one of it's members.
Recursive Membership Example
Here's an example where we get every group that mike from contoso.com is a member of.
Get-ADGroup -LDAPFilter "(member:1.2.840.113556.1.4.1941:=CN=mike,OU=users,DC=contoso,DC=com)"
So now that we understand how to use LDAP_MATCHING_RULE_IN_CHAIN to list out recursive group membership, here's how you would use the output to compare two user's groups:
$groupsa=Get-ADGroup -LDAPFilter "(member:1.2.840.113556.1.4.1941:=CN=mike,OU=Users,DC=contoso,DC=com)"
$groupsb=Get-ADGroup -LDAPFilter "(member:1.2.840.113556.1.4.1941:=CN=jason,OU=Users,DC=contoso,DC=com)"
Compare-Object $groupsa $groupsb -IncludeEqual
Running this would give us a list of groups and a "SideIndicator". The <= indicator would show that the group is exclusive to our first input object, in this case the groups mike is a member of. Conversely => would show that only jason is a member, and == would be groups that both are a member of.
$groupsb=Get-ADGroup -LDAPFilter "(member:1.2.840.113556.1.4.1941:=CN=jason,OU=Users,DC=contoso,DC=com)"
Compare-Object $groupsa $groupsb -IncludeEqual
Logic For Multiple Users
So as you've seen it's pretty easy when you just have one or two users and you want to look at their groups, but what if we have a long list of like 10 users and we want to figure out which group is the most prolific amongst those users?This is quite a bit harder to do than a direct side by side comparison, and to do this effectively I'm going to use a dictionary. I'll write another post about using dictionaries effectively later, which I'll link to here. But I'll walk through what I'm doing step by step here, and then write the complete script at the bottom. Per usual we'll assume you have a flat input file of hard return separated users at C:\users.txt on a Windows system.
First we instantiate a dictionary object using the following syntax:
$groups=@{}
Then we get the content from our users.txt and initiate a foreach
ForEach ($user in Get-Content C:\users.txt) {
And then go and get the distinguished name for the user in the loop
$UserDN=(Get-ADUser $user).distinguishedname
Then we get their recursive group membership and pass it into another foreach
Get-ADGroup -LDAPFilter "(member:1.2.840.113556.1.4.1941:=$UserDN)" |
ForEach {
In this loop we'll look to see if the dictionary already contains the group we find. If it doesn't, we'll add the group to the dictionary, and if it does, we'll increment the count and add the user name.
ForEach {
If ($groups[$_.name] -eq $null) {
$groups.Add($_.name,@{groupname=$_.name;count=1;members=@($user)})
}else{
$groups[$_.name].count++;$groups[$_.name].members+=$user
}}}
$groups.Add($_.name,@{groupname=$_.name;count=1;members=@($user)})
}else{
$groups[$_.name].count++;$groups[$_.name].members+=$user
}}}
Viewing The Results
So the result of running this will be to load up the $groups dictionary we declared with your group memberships. Now if we wanted to see a list of our groups we could look at $groups.keys and it'll list all our groups. If we wanted to check just one of those groups we could look at $groups["TheNameOfOneOfTheGroups"] and it would show us some result likeName        Value
groupname   Group1
members     {mike, jason}
count       2
groupname   Group1
members     {mike, jason}
count       2
So lets say we wanted to look at each of these dictionary entries and sort it to see the most common shared groups and their members we were looking for. We could run the following:
$data=ForEach ($key in $groups.keys) {
New-Object psobject -Property @{
  name=$groups[$key]['groupname']
  count=$groups[$key]['count']
  members=$groups[$key]['members'] -join ","}
}
$data | Sort-Object count
New-Object psobject -Property @{
  name=$groups[$key]['groupname']
  count=$groups[$key]['count']
  members=$groups[$key]['members'] -join ","}
}
$data | Sort-Object count
Putting It All Together
So here's the script in it's entirety, again, assuming that you have your users in a C:\users.txt file.$groups=@{}
ForEach ($user in Get-Content C:\users.txt) {
  $UserDN=(Get-ADUser $user).distinguishedname
  Get-ADGroup -LDAPFilter "(member:1.2.840.113556.1.4.1941:=$UserDN)" |
  ForEach {
    If ($groups[$_.name] -eq $null) {
      $groups.Add($_.name,@{groupname=$_.name;count=1;members=@($user)})
    }else{
      $groups[$_.name].count++;$groups[$_.name].members+=$user
    }
  }
}
$data=foreach ($key in $groups.keys) {
New-Object psobject -Property @{
  name=$groups[$key]['groupname']
  count=$groups[$key]['count']
  members=$groups[$key]['members'] -join ","
}}
$data | Sort-Object count
ForEach ($user in Get-Content C:\users.txt) {
  $UserDN=(Get-ADUser $user).distinguishedname
  Get-ADGroup -LDAPFilter "(member:1.2.840.113556.1.4.1941:=$UserDN)" |
  ForEach {
    If ($groups[$_.name] -eq $null) {
      $groups.Add($_.name,@{groupname=$_.name;count=1;members=@($user)})
    }else{
      $groups[$_.name].count++;$groups[$_.name].members+=$user
    }
  }
}
$data=foreach ($key in $groups.keys) {
New-Object psobject -Property @{
  name=$groups[$key]['groupname']
  count=$groups[$key]['count']
  members=$groups[$key]['members'] -join ","
}}
$data | Sort-Object count