AD Group Membership Removal Script Not Working 100% - Need Assistance - powershell

I'll start off by saying that I am not a scripting expert at all. But I was tasked with creating a script to remove all group memberships of all users in a specified OU (called DISABLE) a few months ago.
Long story short, the script works great most of the time, but on random occasions it won't remove 1 (random) group for a user , or 2, or sometimes more, and I can't figure out what the reasoning is. The script used is:
#Remove User Group Memberships
$ou = Get-ADUser -SearchBase "OU=DISABLE,OU=CE,DC=ourdomain,DC=org" -Filter *
foreach ($user in $ou) {
$UserDN = $user.DistinguishedName
$user.PrimaryGroupID = 513
set-aduser -instance $user
Get-ADGroup -LDAPFilter "(member=$UserDN)" | foreach-object {
if ($_.name -ne "Domain Users") {remove-adgroupmember -identity $_.name -
member $UserDN -Confirm:$False} }
}
Any help that can be provided would be greatly appreciated, thank you!

Instead of searching for users, and then searching for all groups that user is a member of, just include that in your first search. This has the benefit of fewer AD searches, and less chance that an AD search misses the user.
#Remove User Group Memberships
$ou = Get-ADUser -SearchBase "OU=DISABLE,OU=CE,DC=ourdomain,DC=org" -Filter * -Property MemberOf
foreach ($user in $ou) {
$UserDN = $user.DistinguishedName
$user.PrimaryGroupID = 513
set-aduser -instance $user
$user.MemberOf |
Where{$_ -notmatch "Domain.?Users"} |
ForEach {remove-adgroupmember -identity $_ -member $UserDN -Confirm:$False}
}
Try that and see if you get better mileage out of it. Also, consider that perhaps some of these are protected groups, and the account that you run the script under doesn't have rights to affect those groups. Check these users that aren't being removed from groups and see if they have their AdminCount property set to 1.

Related

Find users who don't belong to multiple groups

My company uses Microsoft Intune. We've got 4 groups in an on-premise AD that controls the conditional access. We'll just call them AllowGroup1, AllowGroup2, BlockGroup1, and BlockGroup2. What I want know find is all users that are not in all of the groups. The result I'm wanting to find is any User object that is not in the mentioned groups. That way I can provide proof that our entire system is compliant. See below for the Powershell code I've borrowed from this post List AD Users who do not belong to one of several groups
I'm running these tests on my home domain controller. The problem I'm having is that the script isn't looking in the entire domain for users. Specifically, there is an OU in my personal DC that is called Home (I created the OU) and there are 2 user objects in a child OU called Users that this script isn't pulling from. I am running this script with a user that is in the Enterprise Admins group so I know it has sufficient privilege's. It's supposed to search AD via PowerShell for users not in multiple groups and place those users in a group called NotInGroup
To further elaborate, some users will be in AllowGroup1 and in BlockGroup2. Some users will be in BlockGroup1 and BlockGroup2. I want to find all users that are not in any of the groups listed above.
Import-Module ActiveDirectory
$groupname = "NotInGroup"
$members = Get-ADGroupMember -Identity $groupname
foreach($member in $members)
{
Remove-ADGroupMember -Identity $groupname -Member $member.samaccountname
}
$users = Get-ADUser -Filter
{
((memberof -notlike "CN=AllowGroup1,OU=Intune,OU=Groups,DC=domain,DC=local")
-AND (memberof -notlike "CN=AllowGroup2,OU=Intune,OU=Groups,DC=domain,DC=local")
-AND (memberof -notlike "CN=BlockGroup1,OU=Intune,OU=Groups,DC=domain,DC=local")
-AND (memberof -notlike "CN=BlockGroup2,OU=Intune,OU=Groups,DC=domain,DC=local"))
}
-SearchBase "dc=domain,dc=local" -SearchScope Subtree
foreach($user in $users)
{
Add-ADGroupMember -Identity $groupname -Members $user.samaccountname -ErrorAction SilentlyContinue
}
I don't think a complex filter like that would work and I would opt for using a regex.
Perhaps something like
# get users not in groups 'AllowGroup1', 'AllowGroup2', 'BlockGroup1', 'BlockGroup2'
$regex = 'CN=(AllowGroup[12]|BlockGroup[12])'
$users = Get-ADUser -Filter * -Properties MemberOf | Where-Object { ($_.MemberOf -join ';') -notmatch $regex }
Or you could try using the LDAPFilter parameter:
$filter = '(!(|(memberof=CN=AllowGroup1,OU=Intune,OU=Groups,DC=domain,DC=local)
(memberof=CN=AllowGroup2,OU=Intune,OU=Groups,DC=domain,DC=local)
(memberof=CN=BlockGroup1,OU=Intune,OU=Groups,DC=domain,DC=local)
(memberof=CN=BlockGroup2,OU=Intune,OU=Groups,DC=domain,DC=local)))'
$users = Get-ADUser -LDAPFilter $filter
Both parameters Filter and LDAPFilter are expecting a string, not a scriptblock

Nested ForEach loop running incredibly slowly

I have written a script That is designed to search an OU for all the users contained whiten. it then gets all groups that that user it finds are members of, and if the DistinguishedName of a group is found to match a string it will remove thate user from that group specifically.
However it is incredibly slow taking anywhere from 5-45 seconds an entry. is this normal or is there any way for me to expedite it?
$OUs = "OU=Terminated,OU=####,OU=####,DC=####,DC=####"
foreach ($ou in $OUs)
{
$users = Get-ADUser -SearchBase $ou -Filter *
foreach ($user in $users)
{
$groups = Get-ADPrincipalGroupMembership -Identity $User | ? {$_.distinguishedName -like "*Groups_I_Want_Removed*" }
foreach($group in $groups)
{
Remove-ADPrincipalGroupMembership -Identity $user -MemberOf $group -whatif
}
}
}
$results = foreach ($OU in $OUs)
{
get-aduser -SearchBase $OU -filter * -Properties MemberOf | ? MemberOf -like "*Distribution Lists*"
}
$results | Export-Csv .\Output_of_users_remaining.csv -NoTypeInformation
My concern is that this script is going through thousands of users and the last time I ran it it was unable to finish within 3 hours and I stopped it at the end of the day. in that time it said it had corrected about 5000~ish users.

Filter AD Groups By Organizational Unit

I am attempting to create a power shell script that prompts input for specific AD groups that a particular org manages that will essentially filter out users that are no longer in the said org and ONLY returns the 'samaccountname' and current department for those particular users. I have started but it does not seem to filter correctly. Any advice would be greatly appreciated.
Add-Type -AssemblyName Microsoft.VisualBasic
$groupName = [Microsoft.VisualBasic.Interaction]::InputBox("Please enter AD group name to display users no longer in department:", "XYZ", "$env")
Get-ADGroupMember -Identity $groupName -Recursive | Get-ADUser -Properties Department -Filter {enabled -eq $true} | ? {$_.DistinguishedName -notlike "*,OU=XYZ,*"} | Select-Object SamAccountName,Department
It appears it doesn't like the -Filter you have for Get-ADUser, I think (not 100% on this) it is because coming through the pipeline, it is sending one user at a time, so there isn't anything to filter.
Here's what I would do/try (maybe a better way, but testing this seems to get where you want to go):
$MatchedUsers=#{}
$GroupMembers=Get-ADGroupMember -Identity $groupName -Recursive | Get-ADUser -Properties Department
ForEach ($User in $GroupMembers) {
If (($User.Enabled -eq "True") -and ($User.DistinguishedName -notlike "*,OU=XYZ,*")) {
$MatchedUsers=#{
'SamAccountName'=$User.SamAccountName
'Department'=$User.Department
}
$obj=New-Object -TypeName PSObject -Property $MatchedUsers
Write-Output $obj
}
}

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
}

Dynamic AD Group based on Country Code

We are looking to create a Powershell script that will automatically sort our user base by Country Code into two AD groups, one for English speakers, and one for French speakers. We are having challenges in getting this to work.
Each account should only be on one list, based on their country. The original source list of members for our list is Staff All, and we are looking into having two groups, one called Staff All EN, and the other called Staff All FR. It should also be able to exclude those in a Disabled OU for accounts that are no longer valid. (see below)
This is what we have so far:
$frenchCC = Get-Content .\CCFrench.txt
$staffAll = "CN=Staff-ALL,OU=Internal,OU=DistributionLists,OU=SFCG,DC=sfcg,DC=org"
$staffAllEn = "CN=Staff ALL EN,OU=Internal,OU=DistributionLists,OU=SFCG,DC=sfcg,DC=org"
$staffAllFr = "CN=Staff ALL FR,OU=Internal,OU=DistributionLists,OU=SFCG,DC=sfcg,DC=org"
$Target = Get-ADGroupMember -Identity $staffAll
We have tried several different approaches. The idea is to populate the French list from the AD based on the country code list. Populate the Staff-EN by copying the Staff-ALL list and then removing everyone in the French list.
And somewhere in the process, Remove everyone who is in HR-Disabled.
foreach ($Person in $Target) {
Add-ADGroupMember -Identity $staffAllEn -Members $Person.distinguishedname -confirm:$false
}
foreach ($Country in $frenchCC) {
Add-ADGroupMember -Identity $staffAllFr -Members (Get-ADUser -Filter '"$country"' -eq '") -confirm:$false
}
foreach ($Country in $frenchCC) {
Remove-ADGroupMember "Staff-ALL-EN" -Members (Get-ADUser -Filter $Country) -confirm:$false
}
$searchOU = Specify the OU where your groups are here (OU=Groups,DC=domain,DC=local)
Get-ADGroupMember Staff-ALL-EN -Properties Disabled | Remove-ADGroupMember Staff-ALL-EN
Get-ADGroupMember Staff-ALL-FR -Properties Disabled | Remove-ADGroupMember Staff-ALL-FR
In the source file for the country code, we have put the country codes in single quotes, double quotes and no quotes. with no difference.
This has really caught us in a pickle. Any suggestions would be appreciated.
I was able to work on the following for Brad, but I am not being able to make it run for each line in the text files (ex: multiple country codes). Anyone know what to change?
$frenchCC = Get-Content .\Countries.txt
$OUs = Get-Content .\OUs.txt
$userListFR = Get-ADUser -Filter {country -eq $frenchCC} -SearchBase $OUs -SearchScope OneLevel
$userListEN = Get-ADUser -Filter {country -ne $frenchCC} -SearchBase $OUs -SearchScope OneLevel
foreach($user in $userListFR) {add-adgroupmember "Staff-ALL-FR" -Members $user}
foreach($user in $userListEN) {add-adgroupmember "Staff-ALL-EN" -Members $user}