powershell retrieve members of group recursively - powershell

I got a pretty big audit project that I'm hoping to automate.
I need to get every user's name, SamAccountName, Title and Department who are part of a group. Problem is, the group has groups within it and those groups have groups within them. Another problem is that about 99% of all the groups have an asterisk in it's Display Name (NOT SamAccountName).
Here is the code I currently have, it works fine until it receives a group with an asterisk in it's name.. (hence the .Replace("*","") part.... Anyone got any ideas on how to fix this?
function Get-NestedGroupMember {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Group
)
$broke = #();
## Find all members in the group specified
$members = Get-ADGroupMember -Identity $Group
foreach ($member in $members){
## If any member in that group is another group just call this function again
if ($member.objectClass -eq 'group'){
$memberGroup = $($member.Name).Replace("*", "")
try{
Get-NestedGroupMember -Group "$($memberGroup)"
}catch{
$broke += "$($memberGroup)`n"
}
}else{
## otherwise, just output the non-group object (probably a user account)
$member.Name
}
}
Write-Host "`nThe following groups could not be found automatically.`n`n$($broke)"
}
$getGroup = Read-Host -Prompt "Group name"
Get-NestedGroupMember $getGroup

As Bill_Stewart said, you are trying to implement functionality that already exists for you. Get-ADGroupMember has a -Recursive parameter that will search all of the nested groups and get the members for you. You can then pipe the output to a select and get only the properties you care about.
AD:
TopGroup
Betty
Bob
NestedGroup1
Joe
Frank
NestedGroup2
George
Herman
Using -Recursive
Get-AdGroupMember TopGroup -Recursive | Select-Object SamAccountName
Betty
Bob
Joe
Frank
George
Herman

Related

Check to See if User is part of any of certain AD groups

I'm fairly new to powershell. I'm hoping to get direction on how I can task to work.
So part of my new hire script is to ensure a user account that gets created is surely added to Office ALL group. If it doesn't get added for some reason, I need a way to identity which account didn't get added.
We have office group office name that starts with Province/state Like ON,BC,AB and end with -ALL#jbalatest.com
$ADgroupofAlloffices= $provincearray | ForEach-Object -process {Get-ADGroup -Filter "mail -like '$_-*'" -Properties Mail |
Where-Object {$_.Mail -LIKE "*-ALL#jbalatest.com"} |
Select-Object DistinguishedName | Sort-Object -Property Samaccountname}
This is my query to get all the groups by DistinguishedName. I have than more 30 groups.
Now I need away to check to see if the new hire account is part of this group as part of the final portion of over all script.
$user_groups = (Get-ADUser -Identity $ADUseraccount -Properties memberof | Select-Object memberof).memberof
I know with this I can get all the group of users.
How I check if AD user is part any of these office groups and let me know if user is not part any of these groups.
Let me know where need to do more search on finding solution for this. Scratching my head here.
Several ways to do this, here is one, first the way you're getting the Office Groups can be optimized using AD Filtering only, with some string manipulation. You can iterate over the $provArray to generate an array of strings which then gets joined and combined into a single LDAP Filter, in example with the Provinces / States provided in your question the filter would be something like this:
(|(mail=ON*-ALL#jbalatest.com)(mail=BC*-ALL#jbalatest.com)(mail=AB*-ALL#jbalatest.com))
Then after you query for all the groups, you can get an array with the DistinguishedName of all these groups ($officeGroups) which we can use to compare with the memberOf attribute of the user:
$provArray = 'ON', 'BC', 'AB' # and more here
$filter = foreach($i in $provArray) {
"(mail=$i*-ALL#jbalatest.com)"
}
$filter = "(|$(-join $filter))"
$officeGroups = (Get-ADGroup -LDAPFilter $filter).DistinguishedName
$user = (Get-ADUser 'someuser' -Properties memberof).memberof
if($user.where{ $officeGroups -contains $_ }) {
'user is a member of at least one office group'
# do something here
}
else {
'user is not a member of any office groups'
# do something here
}
Since you already have the distinguished name of all of the groups you're interested, you can just test if any of the groups that the user is a member of is in that list and go from there.
$OfficeGroup = $ADgroupofAlloffices | Where{$_.DistinguishedName -in $user_groups}
If(!$OfficeGroup){Write-Warning "User is not in an office group"}

Powershell Azure See Cannot See what groups a contact is in

I am trying to see the groups a contact is apart of. To provide some background, the contact is hidden in the directory of the tenet I am looking in because it is the alias of a sister tenet that the user is actually apart of. I am using PS and the cmd runs but displays no results. Can someone tell me where I may be going wrong? The users email address is stored in variable string called contact
Code:
Get-MsolGroup -All | Where-Object {$_.Members -contains $contact}
When I enter this command in PowerShell I do not get any results displayed
Untested but, I believe this should work. It should return the ObjectId and DisplayName of the Groups where $contact is a member of.
$groups = Get-MsolGroup -All
$contact = 'someUser#someCompany.com'
foreach($group in $groups)
{
$members = Get-MsolGroupMember -GroupObjectId $group.ObjectId
if($contact -in $members.EmailAddress)
{
$group | Select-Object ObjectId, DisplayName
}
}

Add user to group based on their attribute department with Powershell in AD

Hello guys I would like to ask for little help
I was struggling with this for a while so Il be really glad if someone could help me with this :)
The idea is:
We have users with the filled department attribute (ex. LCZ_10960-udrzba rezie, LCZ_40900-financni utvar, etc...) and we have created security groups that look like CZ-udrzba_rezie_10960, CZ-financni_utvar_40900, etc... And that what I want from the script is to search through specific OU for users and check their department attribute and if they have forex. LCZ_10960-udrzba rezie in it then add this user to group CZ-udrzba_rezie_10960 or if another user has CZ-financni_utvar_40900 in the department then add this user the group LCZ_40900-financni utvar...
Hope I explain it good :)
I already create the script but that is only for add single group...
Get-ADUser -SearchBase "XY,DC=local" -Filter 'Department -like "LCZ_10960-udrzba rezie"' | Foreach{Add-ADGroupMember -Identity "CZ-udrzba_rezie_10960" -Members $_}
Thanks for the help guys!
In this case, I would create a lookup Hashtable linking the department names and the group names together:
# lookup hashtable:
# Keys are the Department names from the user attributes, the Values contain the Group names
$deptGroups = #{
'LCZ_10960-udrzba rezie' = 'CZ-udrzba_rezie_10960'
'LCZ_40900-financni utvar' = 'CZ-financni_utvar_40900'
# more Department-to-Group mappings go here
}
Then you can use code like this:
foreach ($dept in $deptGroups.Keys) {
$users = Get-ADUser -SearchBase "XY,DC=local" -Filter "Department -eq '$dept'" -ErrorAction SilentlyContinue
if ($users) {
# $deptGroups[$dept] has the group name from the lookup table
# you can supply an array of members with Add-ADGroupMember, so no need for a ForEach-Object loop
Add-ADGroupMember -Identity $deptGroups[$dept] -Members $users
}
else {
Write-Warning "No users found for department '$dept'.."
}
}

Foreach in foreach (nested)

I'm trying to loop all disabled users through an array of groups to check if the users have membership in any of the listed groups. My thought is that for every user in the list loop them through and check if they are present in one of the listed groups. That would require nesting foreach loops, right? The output I get is like this:
...
user1
user2
user3
is not a member of group1
Here is the source code:
$dUsers = Get-ADUser -Filter {enabled -eq $false} |
FT samAccountName |
Out-String
$groups = 'Group1', 'Group2'
foreach ($dUser in $dUsers) {
foreach ($group in $groups) {
$members = Get-ADGroupMember -Identity $group -Recursive |
Select -ExpandProperty SamAccountName
if ($members -contains $dUsers) {
Write-Host "[+] $dUser is a member of $group"
} else {
Write-Host "[-] $dUser is not a member of $group"
}
}
}
I'm pulling my hair because I feel like there is a simple solution, but I'm lost.
Update:
I wanted to put all disabled users in variable $dUsers.
It actually works if I manually put users in the variable like this:
$dUsers = 'user1','user2','user3'
Which gives me the following output:
user1 is not a member of group1
user1 is not a member of group2
user2 is not a member of group1
user2 is not a member of group2
...
This makes me question how it gets "foreached" when the variable is:
$dUsers = Get-ADUser -Filter {enabled -eq $false} |
FT samAccountName |
Out-String
Anyone got a clarification on that?
Update:
This is the final code. It takes a long time to run, even with only two groups.
$dUsers = Get-ADUser -Filter {enabled -eq $false} | Select-Object -Expand SamAccountName
$groups = 'Group1', 'Group2'
Write-host '[+] Checking if any disabled user is member of any SSL groups'
Write-host '[+] This might take a while. Get a coffee!'
write-host '[+] Running...'`n
foreach ($dUser in $dUsers) {
foreach ($group in $groups) {
$members = Get-ADGroupMember -Identity $group -Recursive | Select -ExpandProperty SamAccountName
if($members -contains $dUser) {
Write-Host "$dUser is a member of $group"
} Else {
# Remove or comment out the line below to get a clutterfree list.
# Write-Host "$dUser is not a member of $group"
}
}
}
You have two issues in your code:
You're creating a single string from the Get-ADUser output. Piping the output of that cmdlet through Format-Table (alias ft) and then Out-String creates one string with a tabular display of all matching account names including the table header.
If you output $dUsers in a way that makes beginning and end of a string visible you'd see something like this (the leading and trailing == marking the beginning and end):
PS> $dUsers | ForEach-Object { "==$_==" }
==samAccountName
--------------
user1
user2
user3==
Since there is no account with a username matching this string no match can be found in any group and you're getting the output you observed.
This misuse of Format-* cmdlets is a common beginner's mistake. People get a nicely formatted string output and then try to work with that. ONLY use Format-* cmdlets when you're presenting data directly to a user, NEVER when further processing of the data is required or intended.
What you actually want is not a string with a tabular display of usernames, but an array of username strings. You get that by expanding the SamAccountName property of the user objects you get from Get-ADUser.
$dUsers = Get-ADUser ... | Select-Object -Expand SamAccountName
The second issue is probably just a typo. Your condition $members -contains $dUsers won't work, since both $members and $dUsers are arrays (after fixing the first issue, that is). The -contains operator expects an array as the first operand and a single value as the second operand.
Change
$members -contains $dUsers
to
$members -contains $dUser
Depending on what PowerShell version you are on, there is a cmdlet for this use case and others.
As for
I'm Trying to loop all disabled users
Just do...
Search-ADAccount -AccountDisabled |
Select-Object -Property Name, Enabled,
#{Name = 'GroupName';Expression = {$_.DistinguishedName.Split(',')[1] -replace 'CN='}}
# Results
Name Enabled GroupName
---- ------- ---------
...
testuser2 NewTest False Users
Guest False Users
Or different cmdlet…
# Get disabled users and their group membership, display user and group name
ForEach ($TargetUser in (Get-ADUser -Filter {Enabled -eq $false}))
{
"`n" + "-"*12 + " Showing group membership for " + $TargetUser.SamAccountName
Get-ADPrincipalGroupMembership -Identity $TargetUser.SamAccountName | Select Name
}
# Results
...
------------ Showing group membership for testuser1
Domain Users
Users
------------ Showing group membership for testuser2
Domain Users
As for ...
an array of Groups
Just select or filter the DN for the group name you want using the normal comparison operators.
As for...
Unfortunately I'm not well versed in powershell.
… be sure to spend the necessary time to get ramped up on it, to limit the amount of misconceptions, confusions, errors, etc. that you are going to encounter. There are plenty of no cost / free video and text-based training / presentations all over the web.
Example:
Videos
Use tools that will write the code for you that you can later tweak as needed.
Step-By-Step: Utilizing PowerShell History Viewer in Windows Server 2012 R2
Learning PowerShell with Active Directory Administrative Center (PowerShell History Viewer)
As well as plenty of sample scripts and modules via the MS PowerShell Script / Module Gallery.
There are two commands for the AD Groups.
First I see that you want the membership of the disabled users that is easy.
#Get the dissabled users from your AD with all their attributes (properties and select)
$dUsers = Get-ADUser -Filter {Enabled -eq $false} -Properties * | Select *
#Run a loop for each user to get the group membership
Foreach ($User in $dUsers) {
$User = $User.SamAccountName
Get-ADUser $User -Properties * | Select Name, SamAccountName, MemberOf | Format-Table -Wrap # > "D:\test\$user.txt" -HideTableHeaders
}
This one can work but I don't like the output that we get.
I prefer to run the groupmembership command and check the users.
$GroupMembers = Get-ADGroupMember "groupname"| Select Name, SamAccountName
ForEach ($User in $GroupMembers)
{
$UserProperties = Get-ADUser $User.SamAccountName -Properties * | select *
If ($UserProperties.Enabled -eq $False) {
Write-Host $UserProperties.SamAccountName
}
}
Edit:
Let me know if those fits you.
Kind regards.
The first thing you should try to check is whenever you are only interested in direct memberships or indirect ones as well. Depending on the answer the options you got availabel change a bit. You probably will encounter Distinguished Names while working on this so check out what they are if you don't know (mostly a path for an object).
If it's only direct memberships using memberOf with Get-ADUser should be sufficient. The memberOf attribute contains every direct group membership of the user with the full Distinguished Name of the group.
Get-ADUser test -Properties MemberOf | Select-Object -ExpandProperty memberOf
You can match the groups you're looking for in various ways. You could get the whole Distinguished Name of those groups or you could do a partial match. It's up to you to decide how to proceed.
If you need the indirect memberships as well you might want to split up your code to make it easier for yourself. For instance you could first find the users and save them. Afterwards find all group members of those groups (You already got that with Get-ADGroupMember) and finally compare the two.
Currently for every user you build the whole list of group members again. This approach would save a few resources as you wouldn't be doing the same queries over and over again.
Finally you could also use the MemberOf approach but get the list of every direct and indirect membership of a user using an LDAP query.
$dn = (Get-ADUser example).DistinguishedName
$userGroups = Get-ADGroup -LDAPFilter ("(member:1.2.840.113556.1.4.1941:={0})" -f $dn)
This approach uses a LDAP search query. It can be quite complex, you could also only check for one one of the groups by modifying it a bit.
In the end even your current approach should work. The problem is that you're comparing the AD object against the list of SAM Accountnames. You would need to check for the SAM Accountnames as well.
if($members -contains $dUsers.SamAccountName)
if($members -contains $dUsers | Select-Object -ExpandProperty SamAccountName)
One of these should work if you change your $dUsers as well. As it currently is you end up with a giant string. You probably can check that by checking $dUsers.length. Just drop the Format-Table and Out-String.

move AD group by file

I'm new with PS and doing my first steps..
I have a file named "C:\temp\used_groups.csv".
The file has email address of AD Groups populated by Powershell script to check which distributions group are being used in 365.
Now I want to be able to move them to different OU.
the file has some AD group's email address as follow:
"RecipientAddress"
"test#test.com"
"test1#test.com"
"test2#test.com"
is it possible to move the AD group only by their email address attribute?
how can I resolve the group's sAMAccountName attribute by their email address attribute?
this is what I tried with no success:
$Groups=import-csv "C:\temp\used_groups.csv"
ForEach ($Group in $Groups){
Get-ADGroup -Filter "mail -like $Group "
# rest of script.. not done yet.
}
I would do something like this:
# put the DistinghuishedName of the destination OU here
$destinationOU = "OU=Test,DC=Fabrikam,DC=COM"
# read the CSV and grab the 'RecipientAddress' fields in an array
$emailAddresses = (Import-Csv "C:\temp\used_groups.csv").RecipientAddress
foreach ($email in $emailAddresses){
$GroupToMove = Get-ADGroup -Filter "mail -like '$email'"
if ($GroupToMove) {
# Move-ADObject takes the 'DistinghuishedName' or the 'objectGUID' as Identity parameter
# but it also works when piping the group object itself to it.
$GroupToMove | Move-ADObject -TargetPath $destinationOU
Write-Host "Moved group '$($GroupToMove.Name)'."
}
else {
Write-Warning "Could not find group with email address '$email'"
}
}
When using a CSV you have to specify the fieldname. Another problem in your script is the absence of quotes in the AD filter.
Try this:
$Groups=import-csv "C:\temp\used_groups.csv"
ForEach ($Group in $Groups){
(Get-ADGroup -Filter "mail -like '$($Group.RecipientAddress)'").samaccountname
# rest of script.. not done yet.
}
Cheers,
Gert Jan