PowerShell script to count members of multiple groups without duplicates - powershell

I need to write a PowerShell script that will count the users in 4 groups:
group1
group2
group3
group4
The script needs to skip duplicates if a user is in multiple groups.
Previously I was using the following script to count users in each group separately, but it is including duplicates and I need the accurate count of users from all groups.
$ADInfo = Get-ADGroup -Identity '<groupname>' -Properties Members
$ADInfo.Members |Where-Object {(Get-ADUser $_ -Properties extensionAttribute4).extensionAttribute4 -eq 'o365_facstaff'} |Select -Unique | Measure-Object

You could adjust this to send a collection down the pipeline. Something like the below should work:
$Groups = 'Group1','Group2','Group3','Group4'
$Groups |
Get-ADGroup -Properties Member |
Where-Object {(Get-ADUser $_ -Properties extensionAttribute4).extensionAttribute4 -eq 'o365_facstaff'} |
Select-Object -Unique |
Measure-Object
However, I would probably go about it a little different:
$Groups = 'Group1','Group2','Group3','Group4'
$Groups |
Get-ADGroupMember |
Where-Object{ $_.objectCLass -eq 'user'} |
Get-ADUser -Properties extensionAttribute4 |
Where-Object{ $_.extensionAttribute4 -eq 'o365_facstaff' } |
Select-Object -Unique |
Measure-Object
Note: In your original code the Get-ADUser command might error if you send it a group since groups can be members of other groups. That's why there's an extra Where{} clause in the above example to only looking for objectClass 'user'.
If you care to mess with the group Distinguished Names you could also try using an LDAP filter:
$Groups = #(
'Group1_DN'
'Group2_DN'
'Group3_DN'
'Group4_DN'
)
$LDAPFilter = "(&(extensionAttribute4=o365_facstaff)(|(memberOf={0})(memberOf={1})(memberOf={2})(memberOf={3})))" -f $Groups
Get-ADUser -LDAPFilter $LDAPFilter |
Select-Object -Unique |
Measure-Object
This will return all users where extensionAttribute4 is 'o365_facstaff' that are members of any 4 groups. Then it will run through Select -Unique & Measure-Object as before.
Note: Performance probably isn't a big factor in this case. However, it's always a best practice to move filtering left in the command as opposed to post filtering with a Where{} clause or other secondary processing. In this case, leveraging the cmdlet's built-in filtering parameters.

Related

The size limit for this request was exceeded [duplicate]

I am trying to pull groups in from a text file and one of my groups is too large, 80,000 people.
How do I get this to work l, it outputs how I want it.
$groups = Get-Content c:\temp\ADGroups.txt
foreach($group in $groups) {
#(Get-ADGroup $group -Properties Member| Select-Object -ExpandProperty Member).Count
Get-ADGroupMember -Identity $group |
Get-ADObject -Properties Name, DisplayName |
Select-Object -Property #{n="Username";e={$_.Name}}, DisplayName,
#{n="AD Group";e={$group}} |
Export-Csv C:\Users\Desktop\GroupsInfo.CSV -NoTypeInformation -Append
}
The number of objects that Get-ADGroupMember can return is restricted by a limit in the ADWS (Active Directory Web Services):
MaxGroupOrMemberEntries
5000
Specifies the maximum number of group members (recursive or non-recursive), group memberships, and authorization groups that can be retrieved by the Active Directory module Get-ADGroupMember, Get-ADPrincipalGroupMembership, and Get-ADAccountAuthorizationGroup cmdlets. Set this parameter to a higher value if you anticipate these cmdlets to return more than 5000 results in your environment.
According to this thread you should be able to work around it by querying group objects and expanding their member property (if you can't increase the limit on the service):
Get-ADGroup $group -Properties Member |
Select-Object -Expand Member |
Get-ADUser -Property Name, DisplayName
Beware, though, that this is likely to be slow, because you'll be sending thousands of requests. It might be better to build a hashtable of all users:
$users = #{}
Get-ADUser -Filter '*' -Property Name, DisplayName | ForEach-Object {
$users[$_.DistinguishedName] = $_
}
so that you can look them up by their distinguished name:
Get-ADGroup $group -Properties Member |
Select-Object -Expand Member |
ForEach-Object { $users[$_] }
I was hitting the 5000 limit with Get-ADGroupMember.
You can use Get-ADUser with the -LDAPFilter parameter to get group members. It's quick and supports >5000 entries.
$groups = #(
"group1"
"group2"
"group3"
)
Foreach ($group in $groups) {
Get-ADUser -LDAPFilter "(&(objectCategory=user)(memberof=CN=$group,OU=Groups,OU=rest,DC=of,DC=distinguished,DC=name))" | Export-Csv "C:\$group.csv"
}
It looks like you can build up complex filters with this method. I needed to quickly return enabled members from some extremely large groups. The filter I used for this was:
"(&(objectCategory=user)(!useraccountcontrol:1.2.840.113556.1.4.803:=2)(memberof=CN=$group,OU=Groups,OU=rest,DC=of,DC=distinguished,DC=name))"
I hear this is a limitation of the AD Webservices that actually service the requests from powershell cmdlets. The maximum size is 5000. But you can try the dsget command, although you will need to get a little creative.
$GroupDN = (Get-ADGroup -Identity $Group).DistinguishedName will give you the DN of the group.
Use the DSget like this.
$members = DSget group $GroupDN -members This will give you the list of DNs of all members.
Feed that to a Get-ADUser cmdlet in a pipe or foreach loop and you are good to go.
You would need to use the -resultpagesize parameter. The highest value you can specify is 2147483647.
So:
Get-ADGroupMember -Identity $group -resultpagesize 2147483647 |
Select-Object -Property #{n="Username";e={$_.Name}}, DisplayName,
#{n="AD Group";e={$group}} |
Export-Csv C:\Users\Desktop\GroupsInfo.CSV -NoTypeInformation -Append
This is how I did mine. I needed to extract more than 25k machines from a security group.
$Groups = gc C:\Temp\Groups.txt
$results = foreach ($Group in $Groups) {
Get-ADGroup $Group -Properties Member | Select-Object -ExpandProperty Member | Get-ADObject -Properties Name
}
$results | Export-csv "C:\Temp\Groups.csv" -NoTypeInformation
This will give you all of the members of a group quickly (mine had 85k members)
$groupMembers = Get-ADGroup -Identity $group -Server $domainGroupIsIn -Properties Member | Select-Object -ExpandProperty Member ;
or if you need to filter some
$whereMatch = $recipient.DistinguishedName.Remove(0, $index); # limits to a domain or container
$groupMembers = Get-ADGroup -Identity $group -Server $domainGroupIsIn -Properties Member | Select-Object -ExpandProperty Member | Where {$_ -match $whereMatch};
Just increase the limit from ADUC --> View --> Filter Option - Maximum number of options displayed per folder.
That's it. Try again running your command. It takes me 4 days to figure out this and finally it's working.

Powershell to count members of a large AD group that exceeds 5000 members [duplicate]

I am trying to pull groups in from a text file and one of my groups is too large, 80,000 people.
How do I get this to work l, it outputs how I want it.
$groups = Get-Content c:\temp\ADGroups.txt
foreach($group in $groups) {
#(Get-ADGroup $group -Properties Member| Select-Object -ExpandProperty Member).Count
Get-ADGroupMember -Identity $group |
Get-ADObject -Properties Name, DisplayName |
Select-Object -Property #{n="Username";e={$_.Name}}, DisplayName,
#{n="AD Group";e={$group}} |
Export-Csv C:\Users\Desktop\GroupsInfo.CSV -NoTypeInformation -Append
}
The number of objects that Get-ADGroupMember can return is restricted by a limit in the ADWS (Active Directory Web Services):
MaxGroupOrMemberEntries
5000
Specifies the maximum number of group members (recursive or non-recursive), group memberships, and authorization groups that can be retrieved by the Active Directory module Get-ADGroupMember, Get-ADPrincipalGroupMembership, and Get-ADAccountAuthorizationGroup cmdlets. Set this parameter to a higher value if you anticipate these cmdlets to return more than 5000 results in your environment.
According to this thread you should be able to work around it by querying group objects and expanding their member property (if you can't increase the limit on the service):
Get-ADGroup $group -Properties Member |
Select-Object -Expand Member |
Get-ADUser -Property Name, DisplayName
Beware, though, that this is likely to be slow, because you'll be sending thousands of requests. It might be better to build a hashtable of all users:
$users = #{}
Get-ADUser -Filter '*' -Property Name, DisplayName | ForEach-Object {
$users[$_.DistinguishedName] = $_
}
so that you can look them up by their distinguished name:
Get-ADGroup $group -Properties Member |
Select-Object -Expand Member |
ForEach-Object { $users[$_] }
I was hitting the 5000 limit with Get-ADGroupMember.
You can use Get-ADUser with the -LDAPFilter parameter to get group members. It's quick and supports >5000 entries.
$groups = #(
"group1"
"group2"
"group3"
)
Foreach ($group in $groups) {
Get-ADUser -LDAPFilter "(&(objectCategory=user)(memberof=CN=$group,OU=Groups,OU=rest,DC=of,DC=distinguished,DC=name))" | Export-Csv "C:\$group.csv"
}
It looks like you can build up complex filters with this method. I needed to quickly return enabled members from some extremely large groups. The filter I used for this was:
"(&(objectCategory=user)(!useraccountcontrol:1.2.840.113556.1.4.803:=2)(memberof=CN=$group,OU=Groups,OU=rest,DC=of,DC=distinguished,DC=name))"
I hear this is a limitation of the AD Webservices that actually service the requests from powershell cmdlets. The maximum size is 5000. But you can try the dsget command, although you will need to get a little creative.
$GroupDN = (Get-ADGroup -Identity $Group).DistinguishedName will give you the DN of the group.
Use the DSget like this.
$members = DSget group $GroupDN -members This will give you the list of DNs of all members.
Feed that to a Get-ADUser cmdlet in a pipe or foreach loop and you are good to go.
You would need to use the -resultpagesize parameter. The highest value you can specify is 2147483647.
So:
Get-ADGroupMember -Identity $group -resultpagesize 2147483647 |
Select-Object -Property #{n="Username";e={$_.Name}}, DisplayName,
#{n="AD Group";e={$group}} |
Export-Csv C:\Users\Desktop\GroupsInfo.CSV -NoTypeInformation -Append
This is how I did mine. I needed to extract more than 25k machines from a security group.
$Groups = gc C:\Temp\Groups.txt
$results = foreach ($Group in $Groups) {
Get-ADGroup $Group -Properties Member | Select-Object -ExpandProperty Member | Get-ADObject -Properties Name
}
$results | Export-csv "C:\Temp\Groups.csv" -NoTypeInformation
This will give you all of the members of a group quickly (mine had 85k members)
$groupMembers = Get-ADGroup -Identity $group -Server $domainGroupIsIn -Properties Member | Select-Object -ExpandProperty Member ;
or if you need to filter some
$whereMatch = $recipient.DistinguishedName.Remove(0, $index); # limits to a domain or container
$groupMembers = Get-ADGroup -Identity $group -Server $domainGroupIsIn -Properties Member | Select-Object -ExpandProperty Member | Where {$_ -match $whereMatch};
Just increase the limit from ADUC --> View --> Filter Option - Maximum number of options displayed per folder.
That's it. Try again running your command. It takes me 4 days to figure out this and finally it's working.

Export users from AD with a specific group membership

I'm working on a script that takes all the users in the AD and getting four specifics.
saMAccountName
Displayname
Comment
Specific group name (Group A)
Below is the code that I have now. It works, but it gives me all the groups, I only need one specific group (Group A) to be listed.
If the user is not a member of this group, the user must be listed in the export but without the listing of the group
Get-ADGroup -Filter {name -like "Domain Users"} |
Get-ADGroupMember | Where-Object { $_.objectClass -eq 'user' } |
Get-ADUser -Properties comment,displayname,MemberOf |
select saMAccountName,displayname,comment,#{Name="MemberOf";Expression={$_.MemberOf -Join ";"}} |
Sort-Object SamAccountName | Export-csv -path C:\Install\Export-AD.csv -NoTypeInformation
Hope you have some tips and pointers for me on how to filter on the group name.
You could just add a comparison operation (-like) to your expression for MemberOf. You can see an example of this below. However, I would recommend against that single augmentation because of the inefficient nature of the Where-Object and the unnecessary queries that are happening here.
Get-ADGroup -Filter {name -like "Domain Users"} | Get-ADGroupMember | Where-Object { $_.objectClass -eq 'user' } | Get-ADUser -Properties comment,displayname,MemberOf | select saMAccountName,displayname,comment,#{Name="MemberOf";Expression={($_.MemberOf -like "Group A") -join ";"}} | Sort-Object SamAccountName | Export-csv -path C:\Install\Export-AD.csv -NoTypeInformation
I don't know how efficiently this runs in your AD. I tested this with a 722 member group, and it took 22.221 seconds to run.
I would try something like this instead as it will be significantly faster:
$GroupFilterDN = (Get-ADGroup "DOMAIN users").DistinguishedName
$GroupCheck = (Get-ADGroup "Group A").DistinguishedName
Get-ADUser -filter {(memberof -eq $GroupFilterDN -or PrimaryGroup -eq $GroupFilterDN) -and (ObjectClass -eq "user")} -Properties comment,displayname,MemberOf |
select saMAccountName,displayname,comment,#{Name="MemberOf";Expression={$_.MemberOf.where({$_ -in $GroupCheck}) -join ";"}} |
Sort-Object SamAccountName | Export-csv -path C:\Install\Export-AD.csv -NoTypeInformation
You need to replace the Group A string with your group name in the $GroupCheck variable.
$GroupFilter contains the group you want to filter on. In your example, you wanted to filter on Domain Users. The variable holds the DN for that group.
$GroupCheck contains the group for which you want to find members. The variable holds the DN for that group. In your example, you called this Group A.
The PrimaryGroup check had to be added since in your example you are using Domain Users. Domain Users does not show up in the MemberOf property.
The where({$_ -in $GroupCheck}) method is for when $GroupCheck has multiple groups. $GroupCheck currently would only have one group, but it could be tweaked to have multiple.
The code removes the requirement of using the Get-ADGroupMember command, which contains the Where-Object. Then it adds a comparison operation (-eq) for the MemberOf expression.
I tested the second block of code and it completed in 3.847 seconds with the same 722 member group.

Get-ADGroupMember : The size limit for this request was exceeded

I am trying to pull groups in from a text file and one of my groups is too large, 80,000 people.
How do I get this to work l, it outputs how I want it.
$groups = Get-Content c:\temp\ADGroups.txt
foreach($group in $groups) {
#(Get-ADGroup $group -Properties Member| Select-Object -ExpandProperty Member).Count
Get-ADGroupMember -Identity $group |
Get-ADObject -Properties Name, DisplayName |
Select-Object -Property #{n="Username";e={$_.Name}}, DisplayName,
#{n="AD Group";e={$group}} |
Export-Csv C:\Users\Desktop\GroupsInfo.CSV -NoTypeInformation -Append
}
The number of objects that Get-ADGroupMember can return is restricted by a limit in the ADWS (Active Directory Web Services):
MaxGroupOrMemberEntries
5000
Specifies the maximum number of group members (recursive or non-recursive), group memberships, and authorization groups that can be retrieved by the Active Directory module Get-ADGroupMember, Get-ADPrincipalGroupMembership, and Get-ADAccountAuthorizationGroup cmdlets. Set this parameter to a higher value if you anticipate these cmdlets to return more than 5000 results in your environment.
According to this thread you should be able to work around it by querying group objects and expanding their member property (if you can't increase the limit on the service):
Get-ADGroup $group -Properties Member |
Select-Object -Expand Member |
Get-ADUser -Property Name, DisplayName
Beware, though, that this is likely to be slow, because you'll be sending thousands of requests. It might be better to build a hashtable of all users:
$users = #{}
Get-ADUser -Filter '*' -Property Name, DisplayName | ForEach-Object {
$users[$_.DistinguishedName] = $_
}
so that you can look them up by their distinguished name:
Get-ADGroup $group -Properties Member |
Select-Object -Expand Member |
ForEach-Object { $users[$_] }
I was hitting the 5000 limit with Get-ADGroupMember.
You can use Get-ADUser with the -LDAPFilter parameter to get group members. It's quick and supports >5000 entries.
$groups = #(
"group1"
"group2"
"group3"
)
Foreach ($group in $groups) {
Get-ADUser -LDAPFilter "(&(objectCategory=user)(memberof=CN=$group,OU=Groups,OU=rest,DC=of,DC=distinguished,DC=name))" | Export-Csv "C:\$group.csv"
}
It looks like you can build up complex filters with this method. I needed to quickly return enabled members from some extremely large groups. The filter I used for this was:
"(&(objectCategory=user)(!useraccountcontrol:1.2.840.113556.1.4.803:=2)(memberof=CN=$group,OU=Groups,OU=rest,DC=of,DC=distinguished,DC=name))"
I hear this is a limitation of the AD Webservices that actually service the requests from powershell cmdlets. The maximum size is 5000. But you can try the dsget command, although you will need to get a little creative.
$GroupDN = (Get-ADGroup -Identity $Group).DistinguishedName will give you the DN of the group.
Use the DSget like this.
$members = DSget group $GroupDN -members This will give you the list of DNs of all members.
Feed that to a Get-ADUser cmdlet in a pipe or foreach loop and you are good to go.
You would need to use the -resultpagesize parameter. The highest value you can specify is 2147483647.
So:
Get-ADGroupMember -Identity $group -resultpagesize 2147483647 |
Select-Object -Property #{n="Username";e={$_.Name}}, DisplayName,
#{n="AD Group";e={$group}} |
Export-Csv C:\Users\Desktop\GroupsInfo.CSV -NoTypeInformation -Append
This is how I did mine. I needed to extract more than 25k machines from a security group.
$Groups = gc C:\Temp\Groups.txt
$results = foreach ($Group in $Groups) {
Get-ADGroup $Group -Properties Member | Select-Object -ExpandProperty Member | Get-ADObject -Properties Name
}
$results | Export-csv "C:\Temp\Groups.csv" -NoTypeInformation
This will give you all of the members of a group quickly (mine had 85k members)
$groupMembers = Get-ADGroup -Identity $group -Server $domainGroupIsIn -Properties Member | Select-Object -ExpandProperty Member ;
or if you need to filter some
$whereMatch = $recipient.DistinguishedName.Remove(0, $index); # limits to a domain or container
$groupMembers = Get-ADGroup -Identity $group -Server $domainGroupIsIn -Properties Member | Select-Object -ExpandProperty Member | Where {$_ -match $whereMatch};
Just increase the limit from ADUC --> View --> Filter Option - Maximum number of options displayed per folder.
That's it. Try again running your command. It takes me 4 days to figure out this and finally it's working.

Output list of all Active Directory users and all groups each user is a member of

I'm trying to query Active Directory to get a list of all users and all groups each user is a member of. I only need the direct groups each user is a member of, not the nested groups. The end-goal is to output this list to a CSV file. I'm attempting to do this using PowerShell in Windows Server 2012 R2.
UPDATE
So I've now managed to output a list of all users' names, however only some of the users's groups are included in the output, using the following command:
Get-ADuser -LDAPFilter "(objectClass=user)" -property "memberOf" |
select -Property #{n='name';e={$_.name}},#{n='groups';e
{$($_.MemberOf | Get-adgroup | % {$_.name}) -join ','}}
I'm unable to determine why only some of the users output (probably only 5-10 total) include the groups the user is a member of, while the rest (95%) of the users output only display the name of the user, without any groups at all.
Any ideas from here?
First of all I'am afraid that Get-ADuser -Filter {group -eq 'Domain Users'} just give nothing.
You can try to begin :
Get-ADuser -LDAPFilter "(objectClass=user)" -property "memberof" | select -Property #{n='name';e={$_.SamAccountName}},#{n='groups';e={$_.MemberOf -join ','}}
Then you can modify the filter to also take InetOrgPerson.
Get-ADuser -LDAPFilter "(|(objectClass=user)(objectClass=inetOrgPerson))" -property "memberof" | select -Property #{n='name';e={$_.SamAccountName}},#{n='groups';e={$_.MemberOf -join ','}}
Then you can take the samAccountName of the group DN
Get-ADuser -LDAPFilter "(|(objectClass=user)(objectClass=inetOrgPerson))" -property "memberof" | select -Property #{n='name';e={$_.SamAccountName}},#{n='groups';e={$($_.MemberOf | Get-adgroup | % {$_.SamAccountname}) -join ','}}
Late reply to this post, but I built a script that output all Groups in a specific OU and all users of each group. Only downside is that the "owner" of each group is also a member, so there is a bit of redundancy, but nothing breaking for my purpose. The output is formatted into two columns.
$mGroups=#(
Get-ADGroup -filter * -SearchBase "OU=,OU=,OU=,DC=,DC=" | select name);
$col = #()
for ($i=0
$i -lt $mGroups.Count;
$i++)
{
$agents=#(
Get-ADGroupMember $mGroups[$i].name | select sAMAccountName)
for ($n=0
$n -lt $agents.Count;
$n++)
{
$agentList = [PSCustomObject]#{
Group = $mGroups[$i].name
Agents = $agents[$n].sAMAccountName
}
$col+=$agentList;
}
}
$col
$col | Export-CSV -NoTypeInformation C:\Path\to\file.type