Loop through nested AD groups until all members are found - powershell

I need help figuring out the logic for looping through Active Directory security groups to find all members of any nested groups using Get-ADGroupMember. If I use the recursive switch on that cmdlet, it doesn't capture the name of the nested group, just the members. I need to capture everything. I feel like this needs to be done using a while or do/while.
This section of code gets me most of the way there. However, I can't figure out how to prevent it from running the Get-AdGroupMember inside of the while if the objectclass is User. I'm testing with 4 AD groups called "testgroup1", "testgroup2", "testgroup3" and "testgroup4". Testgropus 1,2 and 3 each have a single user as a member. Testgroup2 is a member of Testgroup1. Testgroup3 is a member of Testgroup2. Testgroup4 is a member of Testgroup 3 and it also has 4 users as members.
$groups = Get-ADGroupMember 'testgroup1'
$allGroups =$null
While($groups){
$allGroups += $groups.SamAccountName
$groups = $groups.SamAccountName | Get-ADGroupMember
}
$allGroups

We solved this issue by creating a function that gets the members of an AD group and adds only users to an array. If the object class returns as a group it calls the same function again on that new group and then adds users under it to the same array. Code is below.
function Get-MBusers {
Param (
$Group,
$adserver
)
$users=#()
$members = Get-Adgroup -Identity $Group -Server $adserver -Properties members | Select-Object -ExpandProperty Members | Where-Object {$_ -notmatch "ForeignSecurityPrincipals"} | ForEach-Object {Get-ADObject $_ -Server $adserver}
foreach ($member in $members) {
Write-Debug "$($member.Name)"
$type = Get-ADObject $member -server $ADServer -Properties samAccountname
if ($type.ObjectClass -eq 'user') {
$users += Get-Aduser $type.samaccountname -Server $ADServer
}
# If it's a group
if ($type.ObjectClass -eq 'group') {
Write-Debug "Breaking out group $($type.Name)"
$users += Get-MBUsers $member $adserver
}
}
return $users
}

Related

List groups and number of users in AD using Powershell

I am trying to pull a list of groups from AD that start with "pegp" and a count of how many users are in each group and performing this action in PowerShell. This script will give me a list of the all the groups, but I also need how many users are in each group:
$groups = Get-ADGroup -Filter "Name -like 'pegp*'"
$Output = forEach($group in $groups) {
Get-ADGroup -Identity $group | Select-Object name
}
$Output | Export-Csv C:\temp\file_test2.csv
I then tried this code, but it's not giving me a count of the users in each group and is actually inserting an additional row after each group name in the CSV:
$groups = Get-ADGroup -Filter "Name -like 'pegp*'"
$Output = forEach($group in $groups) {
Get-ADGroup -Identity $group | Select-Object name
(Get-ADGroupMember -Identity $group).count
}
$Output | Export-Csv C:\temp\file_test4.csv
Since I'm still new to PowerShell and programming in general, I thought I'd reach out to the well of knowledge to help me figure out where I'm going wrong. Thanks!
Your current code produces an alternating stream of 1 object with a Name property, and 1 integer, which is why Export-Csv is not producing the results you want - it's expecting uniform input.
What you'll want to do is produce 1 object with 2 properties - for that you could use the Select-Object cmdlet with a calculated property for the member count:
$groupsWithMemberCount = Get-ADGroup -Filter "Name -like 'pegp*'" |Select Name,#{Name='MemberCount';Expression={#(Get-ADGroupMember -Identity $_).Count }}
# no need to call Get-ADGroup again, we already have all the information we need
$groupsWithMemberCount |Export-Csv C:\temp\file_test4.csv -NoTypeInformation
Beware that this counts the total number of members (principals AND nested groups).
If you want only users, filter the ouput from Get-ADGroupMember based on their objectClass:
$groupsWithMemberCount = Get-ADGroup -Filter "Name -like 'pegp*'" |Select Name,#{Name='MemberCount';Expression={#(Get-ADGroupMember -Identity $_ |Where-Object objectClass -eq 'user').Count}}

powershell script to export all groups and members and the groups with in the group members

I'm new to scripting but I'm trying to get an export.xlsx of the all of the AD groups and their members and the groups within the group.
AD group name> group Members>... and if there are AD group(s) with in the group> export the members of that AD group
So far i have the following.
$groups = Get-adgroup -filter * -searchbase 'OU...DC=com'
foreach($group in $groups){
Get-adgroupmember $group |
select samaccountname |
export-csv C:\Temp\$group.csv -notype
}
Any help is greatly appreciated.
You can try something like this:
$groups = Get-ADGroup -Filter *
foreach($group in $groups){
$FileName = $group.Name
Get-Adgroupmember -Identity $group |
Export-CSV C:\Temp\$FileName.csv -notype
}
This is a good time for a recursive function.
Function Get-GroupMember
{
Param
(
$Group
)
$allmembers = foreach($member in Get-ADGroupMember $Group)
{
if($member.objectClass -eq 'Group')
{
Get-GroupMember -Group $member
}
else
{
[PSCustomObject]#{
Group = $Group.name
Member = $member.SamAccountName
Type = $member.objectClass
}
}
}
$allmembers | Export-Csv "C:\Temp\$($Group.name).csv" -notype
}
Call it like this
$groups = Get-adgroup -filter * -searchbase 'OU...DC=com'
Get-GroupMember -Group $groups
You didn't clearly specify what all info you wanted to export. The default object would have distinguishedname, name, objectClass, objectGUID, SamAccountName, and SID. If you want all those you could change the else portion to simply "$member". Otherwise, you can adjust what the custom object returns. This will create 1 file per group named groupname.csv. You could parameterize the export path as a next step.

Powershell - Get User information from AD list

I'm a beginner in programming in general..
What I'm trying to do is to create a powershell script that will:
Get information on each user on an Active Directory group.
Inside each group there may be another group, so I would want it to get the list of users from each nested group as well.
Only give me the information for each group once.
This is what I have so far:
$list = Get-ADGroupMember Admins
foreach($u in $list) {
Get-ADObject $u
}
foreach ($_ in $u) {
if ($u.ObjectClass -eq 'user') {
Get-ADUser $u -Properties * | select givenname, surname, samaccountname | ft -autosize
} else {
Get-ADGroupMember $u -Recursive | select name, samaccountname | ft -autosize
}
}
So far I'm trying to get it to work with that one group 'Admins' and then if it does I would want to run the code for more groups at the same time.
Any help or guidance would be appreciated.
You seem to want only properties that are returned by default by Get-ADUser aswell as Get-ADGroup, so in both cases, there is no need to specify the -Properties parameter.
Get-ADGroupMember can return user, computer and group objects, so at the moment, your else condition expects groups, where you could end up with a computer object..
In your code, you output to console with ft -autosize both in the if and the else, but it would be simpler to capture both types of resulting objects in a variable at the start of the loop and output it as a whole afterwards:
# you can load a list of group names from a predefined array:
$Groups = 'Admins', 'Users'
# or load from a file, each group name listed on a separate line:
# $Groups = Get-Content -Path 'D:\Test\ADGroups.txt'
# or get all AD groups in the domain:
# $Groups = (Get-ADGroup -Filter *).Name
$result = foreach ($group in $Groups) {
Get-ADGroup -Filter "Name -eq '$group'" | ForEach-Object {
# we could use the $group variable, but this ensures correct casing
$groupName = $_.Name
$members = $_ | Get-ADGroupMember -Recursive
foreach ($member in $members) {
if ($member.objectClass -eq 'user') {
Get-ADUser -Identity $member.DistinguishedName |
Select-Object #{Name="GroupName"; Expression={$groupName}},
#{Name="MemberType";Expression={'User'}},
Name,
GivenName,
Surname,
SamAccountName
}
elseif ($member.objectClass -eq 'group') {
Get-ADGroup -Identity $member.DistinguishedName |
Select-Object #{Name="GroupName";Expression={$groupName}},
#{Name="MemberType";Expression={'Group'}},
Name,
#{Name="GivenName";Expression={''}}, # groups don't have this property
#{Name="Surname";Expression={''}}, # groups don't have this property
SamAccountName
}
}
}
}
# output is console
$result | Format-Table -AutoSize
# write to CSV file
$result | Export-Csv -Path 'D:\Test\GroupsInfo.csv' -NoTypeInformation
The trick is here to output objects with equal properties for both a user and a group object

Target all users in two OU's and remove Distribution Lists

hoping to get a little help here – I looked around the site but didn’t see anything quite like this (please direct me if there IS and I missed it).
I need to incorporate a new step in our user offboarding process, which would remove them from any AD Distribution Lists. I would like to set this up as a scheduled task to run once a night against two OU’s where the inactivated user accounts can be found.
I’d like to run this by pointing it at the USERS instead of the OU where the Distro Lists live, because I suspect that we’ll ultimately get the request to remove these users from OTHER types of group as well.
This snippet will remove AD Distro Lists from a single user, but leave all other types of AD groups alone:
# GroupCategory 0 = Distro List
# GroupCategory 1 = Security Group
# GroupScope 0 = DomainLocal
# GroupScope 1 = Global
# GroupScope 2 = Universal
$user = "userlogon"
Get-ADPrincipalGroupMembership -Identity $user|
Where {$_.GroupCategory -eq 0} |
ForEach {Remove-ADPrincipalGroupMembership -Identity $user -MemberOf $_ -Confirm:$false}
THIS snippet will look at an OU and return some info (just my example for using a variable with -searchbase):
$OUs = 'OU=PendingDeletion,OU=Users,DC=Stuff,DC=Place,DC=net','OU=HoldForReview,OU=Users,DC=Stuff,DC=Place,DC=net'
$OU | ForEach {Get-ADGroup -Filter * -Properties ManagedBy -SearchBase $_ } |
Select Name, ManagedBy |
Sort -Property Name
Out-GridView
BUT – Does it hold together that in order to complete my objective, I would do something like this?! I'm a bit out of my depth here, any advice for a re-write is appreciated:
$OUs = 'OU=PendingDeletion,OU=Users,DC=Stuff,DC=Place,DC=net','OU=HoldForReview,OU=Users,DC=Stuff,DC=Place,DC=net'
$user = "*"
$OUs | ForEach {
Get-ADPrincipalGroupMembership -Identity $user|
Where {$_.GroupCategory -eq 0} |
ForEach {Remove-ADPrincipalGroupMembership -Identity $user -MemberOf $_ -Confirm:$false}
}
There’s always a couple of ways to do stuff in PoSh, so I’m sure there’s a less-complicated way to do the same thing. If anyone has a different approach please feel free to suggest an alternative.
Thanks for taking a look!
So it sounds like you need three loops.
First, you will need to loop over the OU list to get the Users. We'll store the user objects in $Users
$OUs = 'OU=PendingDeletion,OU=Users,DC=Stuff,DC=Place,DC=net','OU=HoldForReview,OU=Users,DC=Stuff,DC=Place,DC=net'
$Users = ForEach ($OU in $OUs) {
Get-ADUser -Filter * -SearchBase $OU
}
Next loop over the users to get the groups that you want to remove. Then loop over the groups to remove each one.
ForEach ($User in $Users) {
Get-ADPrincipalGroupMembership -Identity $user |
Where-Object {$_.GroupCategory -eq 0} |
ForEach-Object {
Remove-ADPrincipalGroupMembership -Identity $user -MemberOf $_
}
}
I think I'd take this a little differently, by getting the group membership of all users, then grouping by AD group, and processing each group that way. Seems like it would be a lot fewer calls to AD. So I'd start out getting all of the users, just like BenH, except I would include their MemberOf property. Then I'd build a list of potential groups and filter down to just the Distribution Lists. I'd make a Hashtable of those as the keys, and make the value an array of each user that is in that group. Then loop through that removing the value of each from the associated key.
$OUs = 'OU=PendingDeletion,OU=Users,DC=Stuff,DC=Place,DC=net','OU=HoldForReview,OU=Users,DC=Stuff,DC=Place,DC=net'
$Users = ForEach ($OU in $OUs) {
Get-ADUser -Filter * -SearchBase $OU -Properties MemberOf
}
$UsersByGroup = #{}
ForEach($Group in ($Users.MemberOf | Select -Unique | Get-ADGroup | Where{ $_.GroupCategory -eq 0 })) {
$UsersByGroup.Add($Group.DistinguishedName,($Users | Where{ $Group.DistinguishedName -in $_.MemberOf}))
}
$UsersByGroup.Keys | ForEach{
Remove-ADGroupMember -Identity $_ -Members $UsersByGroup[$_] -Confirm:$false
}

Powershell command to compare AD user group memberships to a baseline

I'm trying to figure out the logic to do something like this:
Query all AD groups in a specific OU
Query all the users in a specific OU
Query all the user's group memberships
If any user belongs to one or more groups in the initial group query, output that information
If any user belongs to none of the groups in the initial group query, also output that information
I've dug around on this site and found a script that works for the most part, but I'm stuck on how I can compare the user's group membership to the original group query that I'm pulling. It looks like I could use the compare-object cmdlet but the parameters don't seem to include anything that would let me keep track of how many groups the two objects have in common.
The code I found online is below:
$groups = Get-ADGroup -Filter * | where {$_.distinguishedname -like "*,OU=TUNE_TEST_GROUPS,OU=TUNE_TEST,DC=tune,DC=priv"}
$users = Get-ADUser -Filter * | where {$_.distinguishedname -like "*,OU=TUNE_TEST_USERS,OU=TUNE_TEST,DC=tune,DC=priv"}
foreach ( $User in $Users ) {
$userGroups = Get-ADPrincipalGroupMembership $User
if ( $userGroups.Count -gt 1 ) {
"{0} is a member of the following {1} groups:" -f $User.SamAccountName, $userGroups.Count
foreach ( $group in $userGroups ) {
"`t{0}" -f $group.Name
}
} elseif ( $userGroups.Count -lt 1 ) {
"{0} is a member of the following {1} groups:" -f $User.SamAccountName, $userGroups.Count
foreach ( $group in $userGroups ) {
"`t{0}" -f $group.Name
}
}
}
The problem with this is that I don't have a way of comparing the user group names to the names of the group query in line 1. I also can't determine that a user belongs to 1 or more groups from that list. I'm not sure if I can use the same count method.
You can validate that accounts are member of at least one group from your reference list by using Compare-Object:
foreach ( $User in $Users ) {
$userGroups = Get-ADPrincipalGroupMembership $User
if (!(Compare-Object $userGroups $groups -IncludeEqual -ExcludeDifferent)) {
"{0} doesn't belong to any reference group." -f $User.SamAccountName
}
}
Side note: use the -SearchBase parameter instead of filtering the results of Get-ADUser and Get-ADGroup by a wildcard match on the distinguished name:
$groups = Get-ADGroup -Filter * -SearchBase 'OU=TUNE_TEST_GROUPS,OU=TUNE_TEST,DC=tune,DC=priv' -SearchScope Subtree
$users = Get-ADUser -Filter * -SearchBase 'OU=TUNE_TEST_USERS,OU=TUNE_TEST,DC=tune,DC=priv' -SearchScope Subtree
I ended up doing the following and it works well for what I need. In case anyone is interested, sample code is below:
#gets a list of all groups in a given OU and stores the objects in the $groups variable
$groups = Get-ADGroup -Filter * -SearchBase 'OU=TUNE_TEST_GROUPS,OU=TUNE_TEST,DC=tune,DC=priv' -Properties name | select name
#pipe each group object into a foreach loop and output a string value of the same group name and stores it into the $groups_string variable
$groups_string = $groups | % {$_.name}
#gets a list of all users in a given OU and stores the objects in the $users variable
$users = Get-ADUser -Filter * -SearchBase 'OU=TUNE_TEST_USERS,OU=TUNE_TEST,DC=tune,DC=priv'
$results=#{
"Username" = ""
"Groupname" = ""
}
$table=#()
#iterates through every user in the $users variable and retrieves their group memberships
foreach ($user in $users) {
#selects each group name and stores it in the $groupMembership variable
$groupMembership = Get-ADPrincipalGroupMembership $user | select name
#compares the names of each user's group to the baseline group name.
$groupMembership | foreach ($_) {
#If there is a match add the group name and the username to the $results hash table
if ($groups_string -contains $_.name) {
$results."Groupname" = $_.name
$results."Username" = $user.Name
#create a new PS object and supply the properties of the $results hash table to each object
$objresults = New-Object psobject -Property $results
#add each object to the $table array
$table += $objresults
}
}
}
#display/output the $table array and format it to fit
$table | ft -AutoSize