Advanced Windows PowerShell Scripting Video Training

Advanced Windows PowerShell Scripting Video Training
Advanced Windows PowerShell Scripting Video Training

Wednesday, May 18, 2011

Use PowerShell to determine which groups a set of users have in common

From my PowerShell class in May, here is one of the problems a student wanted PowerShell to help with.  He needed a script that will let him feed in a list of user names and output which groups that all the users were members of.  Below is the code to do that.  Remember that you can Dot Source this into your shell or turn it into a module.

Takes a list of users and displays the groups that
they have in common.

Utilizes Active Directory to take a list of users
and enumerates all the groups that the users have in

Comma delimited list of user names that are to
be processed.

Displays not only the groups that all users
are members of, but also the groups that at
least one user is not a member of.

compare-group g:\userlist.txt | FT -AutoSize

Name         UserPresent
----         -----------
Domain Users        True
Marketing           True

Imports a txt file containing the SAMAccountName of
the users that you want to examine.  It is then piped to
Format-Table and auto sized it to make the data more readable.

F:\mod09\democode> compare-Group g:\UserList.txt -ListAll | FT -AutoSize

Name                                    UserPresent
----                                    -----------
Administrators                                False
Users                                         False
Guests                                        False
Print Operators                               False
Backup Operators                              False
Replicator                                    False
Remote Desktop Users                          False
Network Configuration Operators               False
Performance Monitor Users                     False
Performance Log Users                         False
Distributed COM Users                         False
IIS_IUSRS                                     False
Cryptographic Operators                       False
Event Log Readers                             False
Certificate Service DCOM Access               False
Domain Computers                              False
Domain Controllers                            False
Schema Admins                                 False
Enterprise Admins                             False
Cert Publishers                               False
Domain Admins                                 False
Domain Users                                   True
Domain Guests                                 False
Group Policy Creator Owners                   False
RAS and IAS Servers                           False
Server Operators                              False
Account Operators                             False
Pre-Windows 2000 Compatible Access            False
Incoming Forest Trust Builders                False
Windows Authorization Access Group            False
Terminal Server License Servers               False
Allowed RODC Password Replication Group       False
Denied RODC Password Replication Group        False
Read-only Domain Controllers                  False
Enterprise Read-only Domain Controllers       False
DnsAdmins                                     False
DnsUpdateProxy                                False
Marketing                                      True
Research                                      False
Production                                    False
IT                                            False
demo                                          False

Loads a list of SAMAccountNames from a text file and
examines their group membership.  It outputs
a list off all groups listed in Active Director with
a $True/$False value.  If the value is $True, all the
users in the list are members of that group.  If it
is false, at least one member of that list is not in
that group.

The Active Directory Module for PowerShell must be

The input file must contain only SAMAccountNames.

Each line of the file must only contain one


Function Compare-Group
param (

# Test to verify that the Active Directory module is
# available.
If ((Confirm-module ActiveDirectory) -eq $True)
# Import the Active Directory module.
   Import-Module ActiveDirectory -cmdlet Get-ADUser, Get-ADGroup,
Write-Host "Active Directory Module is not availible."
-ForegroundColor Red -BackgroundColor Yellow

# Load list of users
Try {
$Users = Get-Content $UserList -ErrorAction Stop
# If the file or path is not correct or does not
   # Exist, Warn the user and stop execution.
   Write-Host "The file or path is not correct" -ForegroundColor Red
-BackgroundColor Black

# Create an object of users names
$UserObj = @()
ForEach ($U in $Users)
$UObj = New-Object PSObject
$UObj | Add-Member NoteProperty -name Name -Value $U
$UserObj += $UObj

# Load list of Groups
$Groups = Get-ADGroup -Filter *

# Only show groups that all users in the list are
# members of.

$MatchAll = $True
If ($MatchAll = $True)
# Create a custom object to hold the Group
   # names.  Include a Boolean property to
   # be used to determine if a user is a member of
   # that group.

$GroupObj = @()

Foreach ($G in $Groups)
# Create an instance of a PSObject
       $Group = New-Object PSObject

# Add the name value from the group being
       # processed to the object.
       $Group | Add-Member NoteProperty -Name Name -Value $G.Name

# Add the Boolean value property to the object.
       $Group | Add-Member NoteProperty -Name UserPresent -Value $false

# Add this instance of the object to the group of objects.
       $GroupObj += $Group

# Cycle through each group and compare the members of
   # Each group.  If a member if in the group, set the
   # 'UserPresent' property to $True.  If not, set the
   # the value to $False.  Any group tat is marked $true
   # at the end of this process contains all the users from the
   # list.

ForEach ($G in $GroupObj)
# Get the list of user Names

$MemberList = Get-ADGroupMember $G.Name

# Loop through the list of users and determine
       # if they are members of the group.  If they
       # are, set the value of 'UserPresent' to $True
       # If not, set it to $False.

ForEach ($Member in $MemberList)

ForEach ($User in $Users)
# Format each variable that will be
                # tested to make sure there are no
                # carriage returns.  Also, make sure they
                # are of data type [String] and have
                # no leading or trailing spaces.
                $Testmember = ($Member.SamAccountName) -as [String]
$User = (($User -Replace "\n","") -as [String]).trim()

# Test to see if the user is a member of the
               # group.
               If ($User -ieq $Testmember)

If ($User -ne "")
# Set the UserPresent property to
                       # $True if the user and member
                       # properties match.
                       $G.UserPresent = $True



} # End of forEach ($G in $Groups)

} # End If ($MatchAll = $True)

# Send the output to the Pipeline.

# Send all the results to the pipeline if
# the user request it.
If ($ListAll -eq $True)
Write-Output $GroupObj

# Send only the groups that all users are
# members of to the pipeline by default.
If ($ListAll -eq $False)
ForEach ($obj in $GroupObj)
If ($Obj.UserPresent -eq $True)
Write-Output $Obj



lordlimecat said...

Your script taught me some things I hadnt known-- thanks for sharing.
It has a few issues which I wanted to correct--for instance in a very large domain (like ours) the initial group enumeration can take a VERY long time.

This is what I ended up with. (Repace any "QAD" commands with "AD"-- its just a different toolkit, but functions the same) Basically, it lists the first user's groups; then it checks each of those to see if User 2 is a member of them. If not, that group is removed from the list of shared groups. Then it checks user 3, and so on.

Function Compare-Group
param (

#create some pretty arrays
$ResultsArray= New-Object System.Collections.ArrayList
$RemoveList= New-Object System.Collections.ArrayList
$users = New-Object System.Collections.ArrayList

# Load list of users
Try {
Get-content $UserList | Foreach {$users.Add($_)}
# If the file or path is not correct or does not
# Exist, Warn the user and stop execution.
Write-Host "The file or path is not correct" -ForegroundColor Red
-BackgroundColor Black

#populate initial list; we will subtract from there.
get-qadmemberof $Users[0] | foreach {$ResultsArray.add($}

#we have already checked the 0th element, start on element 1 if it exists
for ($c=1;$c -lt $users.count;$c++)
$_curGroupList = @()
$_curGroupList = (get-qadmemberof $users[$c]).name

#iterate through ResultsArray (which shrinks each time); remove non-common groups
foreach ($G in $ResultsArray)
if (!($_curGroupList.contains($G)))
#Done. Now lets do the removal.
foreach ($R in $RemoveList) {$ResultsArray.remove($R)}


Chris Macaluso said...

When running the original script above I get the error "Get-ADGroupMember : The size limit for this request was exceeded"

Is there a way I can work around this?

Jason Yoder, MCT said...


The default limit is 5000 objects. This is a limitation imposed by the Active Directory Web Service. ADWS is a requirement for utilizing the ActiveDirectoy module for PowerShell. If you have multiple instances on ADWS on multiple Domain Controllers, you will need to perform this procedure on each one. Since you do not know for sure which DC your client will bind to, changing this setting on all of the ADWS services will prevent random issues from happening in the future.

On the file c:\Windows\ADWS\Microsoft.ActiveDirectory.WebServices.exe.config

After the tag, place this entry:

This assumes that you need to return up to 10000 objects from these cmdlets. Also take note that you will still have a 5 minute timeout imposed on all your requests. If you cannot recover the information in 5 minutes, the request will fail. Filter your request to contain only the information that you need to work with.

Your next need to stop and then restart the ADWS service on the Domain Controller.