I'm attempting to make an AD cleanup script that will go through a terminated OU and verify all users are removed from specific OU's. currently if I run it it will remove all users in the terminated OU from all OU's. I might just be blind but is there an easy way to have it only remove groups from selected OU's?
$OUs = "OU=Terminated,OU=####,OU=####,DC=####,DC=####"
$results = foreach ($OU in $OUs) {
get-aduser -SearchBase $OU -filter * -properties MemberOf | foreach-object {
? $_.MemberOf -like "*OU I want removed*" | Remove-ADGroupMember -Members $_.DistinguishedName -Confirm:$false -whatif
}
}
$results | Export-Csv '.\Users groups have been remoed from.csv' -NoTypeInformation
I thought it would work, however all it gives me is:
Where-Object : A positional parameter cannot be found that accepts argument 'Microsoft.ActiveDirectory.Management.ADPropertyValueCollection'.
At C:\###\###\###\accounts script.ps1:8 char:13
+ ? $_.MemberOf -like "*Distrobution Lists*" | <#%{$keep -n ...
Given that you have a separate OU for groups, you can iterate over the groups that a terminated user has and see if any of the groups belong to that specific OU. If thats the case, then remove all those groups.
$results = ""
foreach ($ou in $OUs)
{
$users = Get-ADUser -SearchBase $ou -Filter *
foreach ($user in $users)
{
$groups = Get-ADPrincipalGroupMembership -Identity $User | ? {$_.distinguishedName -like "*OU I WANT TO REMOVE FROM*" }
foreach($group in $groups)
{
Remove-ADPrincipalGroupMembership -Identity $user -MemberOf $group -whatif
results += "$user removed from its Groups: $($groups | % { $_.name })\r\n"
}
}
}
results | Out-File -Append C:\temp\new.txt
$groups will have members in this format. You can use distinguishedName as a filter type and use something like "OU=Groups,DC=this,DC=com" instead of "OU=Groups" that might be considered broad.
distinguishedName : CN=GroupName,OU=****,DC=****,DC=****
GroupCategory : Security
GroupScope : Global
name : <Name Of The Group>
objectClass : group
objectGUID : <Object Guid>
SamAccountName : <Name Of The Group>
SID : <SID>
I like to keep the variables so i can use them to log what changes are being performed.
NOTE: I used -whatif to make sure it doesnt do what you intend to for testing reasons. Remove-ADPrincipalGroupMembership also updates user with one group.
Another Way to go about it
foreach ($ou in $OUs)
{
$users = Get-ADUser -SearchBase $ou -Filter *
$groups = Get-ADGroup -Filter * -SearchBase $DecomOUGROUP
foreach($group in $groups) {
Remove-ADGroupMember -Identity $group -Members $users -ErrorAction SilentlyContinue
}
}
Related
When somebody leaves my organization, we remove all AD group memberships apart from the PrimaryGroup which is Domain Users. We often process these in batches, so pull the affected usernames from a CSV file.
I have the following code, and while it does the job of deleting all group memberships, I get an error for each user:
The user cannot be removed from a group because the group is currently the user's primary group
Whilst it does the job, how can I "clean up" the process to avoid this message each time? Is there a way to exclude Domain Users from the groups it removes the user from, or should I do this another way?
$users = Import-Csv "c:\temp\leavers.csv"
foreach ($user in $users) {
Get-ADPrincipalGroupMembership -identity $user.username | foreach {Remove-ADGroupMember $_ -Members $user.username -Confirm:$false}
}
You can use Where-Object for filtering those groups that are not in an array of groups to exclude. In case you only want to filter for 1 specific group, you would use -NE instead of -NotIn in below example.
$groupToExclude = 'Domain Users', 'someOtherGroup'
$users = Import-Csv "c:\temp\leavers.csv"
foreach ($user in $users) {
try {
Get-ADPrincipalGroupMembership -Identity $user.username |
Where-Object Name -NotIn $groupToExclude |
Remove-ADGroupMember -Members $user.username -Confirm:$false
}
catch {
Write-Warning $_.Exception.Message
}
}
If you get the ADUser object before the ADGroup memberships, you can get the PrimaryGroup of the user and ensure that the list of groups to remove from are not its PrimaryGroup:
$users = Import-Csv "c:\temp\leavers.csv"
foreach ($user in $users) {
$primaryGroup = ( Get-ADUser $user.UserName -Properties PrimaryGroup ).PrimaryGroup
Get-ADPrincipalGroupMembership -Identity $user.UserName | Where-Object {
$_ -ne $primaryGroup
} | ForEach-Object {
Remove-ADGroupMember $_ -Members $user.username -Confirm:$False -WhatIf
}
}
Since this has the potential to be a very destructive command, I have included a safeguard in the example above. Remove the -WhatIf parameter from Remove-ADGroupMember to actually perform the removal.
I'd propose a slightly different approach - just drop Get-ADPrincipalGroupMembership altogether. For example:
$users = Import-Csv -Path c:\temp\leavers.csv
foreach ($user in $users) {
# Assuming DN is not in the csv...
$distinguishedName = (Get-ADUser -Identity $user.UserName).DistinguishedName
Get-ADGroup -LdapFilter "(member=$distinguishedName)"
# Alternatively, just pipe memberOf property to Get-ADGroup...
(Get-ADUser -Identity $user.UserName -Property MemberOf).MemberOf |
Get-ADGroup
}
That way you don't have to filter out something you insisted on getting (by using above mentioned cmdlet).
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.
I was wondering if this script could be changed into one for only active users?
import-module ActiveDirectory
Start-Transcript -Path "C:\test\teetest.txt"
$groups = Get-ADGroup -filter {(name -like "runners*") -or (name -like "helpers*")
foreach($group in $groups)
{
$countUser = (Get-ADGroupMember $group.DistinguishedName).count
Write-Host "The group $($group.Name) has $countUser user(s)."
}
Stop-Transcript
Any help would be appreciated.
If I understand your question correctly and by active users you mean groups with at least 1 member(i.e. greater than 0). You could just filter out results using Where-Object cmdlet. Like so:
$groups = Get-ADGroup -filter {(name -like "runners*") -or (name -like "helpers*") -Properties Members | Where-Object { $_.Members.Count –gt 0 }
Yes, you can add a filter to only get the number of active Members in the Group.
Since Get-ADGroupMember doesn't supply all properties for the Users you have to do another lookup for each of them:
$countUser = (Get-ADGroupMember $group.DistinguishedName | % { Get-ADuser -Identity $_ -Property Enabled | Where-Object {$_.Enabled -eq $true}}).count
Explanation:
% { Get-ADuser -Identity $_ -Property Enabled - Get the Informations for each User found in the Group with the Enabled Property added to it
Where-Object {$_.Enabled -eq $true} - Filters the users that are enabled
I think this may be because the Get-ADGroupMember not just returns user objects with a limited set of properties, but can also return groups and computers.
Since you are only looking for users that are direct descendents of the groups 'runners*' or 'helpers*', it is better to limit the objects returned by the Get-ADGroupMember cmdlet to be users only.
Below I do this by adding Where-Object { $_.objectClass -eq "user" }.
Next, to ensure the .Count property can be used I would suggest to enclose the thing in a #() so the returned value actually is an array and therefore has the Count property.
For a script like this, I also suggest NOT to try and put it all in one single line, because that makes spotting mistakes (like forgetting a closing bracket) more difficult.
Try this:
Start-Transcript -Path "C:\test\teetest.txt"
$groups = Get-ADGroup -Filter {(name -like "runners*") -or (name -like "helpers*")}
foreach($group in $groups) {
$countUser = 0
Get-ADGroupMember $group.DistinguishedName | Where-Object { $_.objectClass -eq "user" } |
ForEach-Object {
if ((Get-ADuser -Identity $_.DistinguishedName).Enabled) { $countUser++ }
}
Write-Host "The group $($group.Name) has $countUser user(s)."
}
Stop-Transcript
Replace the $countUser statement alone with below example.
For only Enabled User Accounts
$countUserEnabled = (get-aduser -filter *|where {$_.enabled -eq "True"}).count
For only Disabled User Accounts
$countUserDisabled = (get-aduser -filter *|where {$_.enabled -ne "False"}).count
In my company, I have a PowerShell script to copy group from one user to another. But it happens that I have two groups which one Denied access and the other Allow acces.
For example, after copying, I could have that in the user account :
FIC-LSM-APP-BO5-DND
FIC-LSM-APP-BO5-MAJ
I would like to delete the Denied access group with a script.
Here my code:
Import-Module ActiveDirectory
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
$nomuser = [Microsoft.VisualBasic.Interaction]::InputBox("Login de >l'utilisateur à traiter", "A traiter", "")
$nommodele = [Microsoft.VisualBasic.Interaction]::InputBox("Nom du >modèle", "Modèle", "")
try {
$user = Get-ADUser -Identity $nomuser -Properties memberOf
} catch {
[System.Windows.Forms.MessageBox]::Show("Utilisateur $nomuser non >trouvé","Erreur",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Error)
exit
}
try {
$usertemplate = Get-ADUser -Identity $nommodele -Properties memberOf
} catch {
[System.Windows.Forms.MessageBox]::Show("Utilisateur modèle $nomuser >non trouvé","Erreur",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Error)
exit
}
$Groups = $User.MemberOf | ForEach-Object {
Get-ADGroup $_
}
$groups = $Usertemplate.MemberOf | ForEach-Object {
Get-ADGroup $_
}
$Groups | ForEach-Object {
Add-ADGroupMember -Identity $_ -Members $user
}
$user = Get-ADUser -Identity $nomuser -Properties memberOf
$user.MemberOf | Get-ADGroup |
Group-Object {$_.Name -replace '^(.*)-(?:DND|MAJ)$', '$1'} |
Where-Object { $_.Count -eq 2 } | Select-Object -Expand Group |
Remove-ADGroupMember -Members $user -Confirm:$false
$user.MemberOf | ogv -Title "Nouvelle liste d'appartenance pour $nomuser"
I finally have time and a test data base to perform this code.
However I have a problem. For example :
Groups of $nomuser : FIC-LSM-APP-BO5-DND
Group of $nommodele : FIC-LSM-APP-BO5-MAJ
When I run the code, the target user ($nomuser) haven't no more group... I think there is an error in the code, but where I don't know.
You can use Group-Object to determine whether a user is member of both allow and deny group. The cmdlet allows you to specify custom matching criteria in a scriptblock, so you can remove a trailing -DND or -MAJ from the group name and group by the group "basename":
$user.MemberOf | Get-ADGroup |
Group-Object {$_.Name -replace '^(.*)-(?:dnd|maj)$', '$1'}
Use Where-Object to filter the result for groups with a count of 2 and expand the Group property to get the original AD groups:
... | Where-Object { $_.Count -eq 2 } | Select-Object -Expand Group | ...
Use another Where-Object filter to restrict the groups to just the ones you want to remove the user from (e.g. with a filter Name -like '*-dnd'), then remove the user from those groups:
... | Remove-ADGroupMember -Members $user -Confirm:$false
I got a list of 150+ users and I want to know which group they have membership for?
I just started using PS. I can query for 1 user, but not for a list of users. Would like
to know exact command??? I got :
(get-aduser -identity "username" -properties memberof |select-object memberof).memberof > c:\temp\ss.csv
Read your user list into an array and check if your AD users are contained in that array:
$userlist = Get-Content 'C:\your\userlist.txt'
Get-ADUser -Filter '*' -Properties memberof | Where-Object {
$userlist -contains $_.SamAccountName
} | ForEach-Object {
$username = $_
$groups = $_ | Select-Object -Expand memberof |
ForEach-Object { (Get-ADGroup $_).Name }
"{0}: {1}" -f $username, ($groups -join ', ')
} | Out-File 'c:\temp\ss.csv'
Replace SamAccountName as appropriate if the user list doesn't contain the account names of the users.