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
Related
I am trying to run a command where I get all active directory users in the parent OU (Users) and filter out the child OU's (Admin accounts, service accounts, disabled accounts) as well as filter out any user account that does not have a surname in the surname field.
At the moment I have
Get-ADUser -Filter{enabled -eq $true} -SearchBase 'OU=Users,OU=Company,DC=CompanyName,DC=local' | Where-Object { $_.DistinguishedName -notlike "*,$Disabled" } | Where {$_.Surname -notlike "$Null"} | select samAccountName
When I add another child OU after 'Disabled' there is an error
Where-Object : A positional parameter cannot be found that accepts argument 'Where'.
Please may someone advise on how to filter out additional child OU's?
Good day Smoore
The problem is you are using multiple Where-object cmdlets but you only need one and separate them using () and adding the -and option, also to refer to $null value you don't need to use the "" marks
Get-ADUser -Filter {Enabled -eq $true} -SearchBase "OU=Users,OU=Company,DC=CompanyName,DC=local" | Where-Object {($_.DistinguishedName -notlike "*,$Disabled*") -and ($_.Surname -notlike $Null)} | select samAccountName
With this options you should be able to get all the users you want
Have a nice day!
I would use a regex -notmatch so it would be possible to combine all OU Distinguished names in just one variable.
Something like this:
$Admins = 'OU=Administrators,OU=Company,DC=CompanyName,DC=local'
$Service = 'OU=ServiceAccounts,OU=Company,DC=CompanyName,DC=local'
$Disabled = 'OU=DisabledUsers,OU=Company,DC=CompanyName,DC=local'
# build a regex string from the above OU DistinguishedNames
$Exclude = '({0}|{1}|{2})$' -f [regex]::Escape($Admins), [regex]::Escape($Service), [regex]::Escape($Disabled)
Get-ADUser -Filter 'Enabled -eq $true' -SearchBase 'OU=Users,OU=Company,DC=CompanyName,DC=local' |
Where-Object { ![string]::IsNullOrWhiteSpace($_.Surname) -and $_.DistinguishedName -notmatch $Exclude } |
Select-Object SamAccountName
As per your comment:
$Admins = 'OU=Administrators,OU=Company,DC=CompanyName,DC=local'
$Service = 'OU=ServiceAccounts,OU=Company,DC=CompanyName,DC=local'
$Disabled = 'OU=DisabledUsers,OU=Company,DC=CompanyName,DC=local'
# the group you want to add the users to
$TargetGroup = 'Company Team'
# build a regex string from the above OU DistinguishedNames
$Exclude = '({0}|{1}|{2})$' -f [regex]::Escape($Admins), [regex]::Escape($Service), [regex]::Escape($Disabled)
$users = Get-ADUser -Filter 'Enabled -eq $true' -SearchBase 'OU=Users,OU=Company,DC=CompanyName,DC=local' |
Where-Object { ![string]::IsNullOrWhiteSpace($_.Surname) -and $_.DistinguishedName -notmatch $Exclude }
# get the AD group as object
$GroupObject = Get-ADGroup -Filter "Name -eq '$TargetGroup'"
# now add these users that have Surnames to the security group all in one go
try {
Write-Host "Adding $(#($users).Count) users to group $TargetGroup"
$GroupObject | Add-ADGroupMember -Members $users -ErrorAction Stop -Verbose
}
catch {
Write-Warning "Error: $($_.Exception.Message)"
}
# or if you prefer loop through the users and add each one individually then use this instead
# foreach ($user in $users) {
# try {
# Write-Host "Adding user $($users.Name) to group $TargetGroup"
# $GroupObject | Add-ADGroupMember -Members $user -ErrorAction Stop -Verbose
# }
# catch {
# Write-Warning "Error adding user $($users.Name) to group $($TargetGroup): $($_.Exception.Message)"
# }
# }
My code:
$searchOU = "OU=a,OU=b,OU=c,OU=d,OU=e,DC=f,DC=g,DC=com"
Get-ADGroup -Filter 'GroupCategory -eq "Security"' -SearchBase $searchOU | sort name | ForEach- Object{
$group = $_
Get-ADGroupMember -Identity $group | Get-ADUser | Where-Object { $_.Enabled -eq $false} | ForEach-Object{
$user = $_
$uname = $user.Name
$gname = $group.Name
Write-Host "Removing $uname from $gname" -Foreground Yellow
Remove-ADGroupMember -Identity $group -Member $user -Confirm:$false #-whatif
}
}
It runs, but it's dog slow. Any suggestions on ways to make it run faster?
You need to remember that Get-ADGroupMember can return users, groups, and computers, not just user objects..
If you want to search for user objects only, you need to add a Where-Object clause there.
Unfortunately, while Get-ADUser has a -Filter parameter that enables you to find disabled users much more quickly than filtering afterwards on the collection of users, using a filter while piping user DN's to it will totally ignore the pipeline and collect all users that are disabled..
So, in this case, we're stuck with appending a Where-Object clause.
You could change your code to rule out all objects from Get-ADGroupMember that are not uders:
$searchOU = "OU=a,OU=b,OU=c,OU=d,OU=e,DC=f,DC=g,DC=com"
Get-ADGroup -Filter "GroupCategory -eq 'Security'" -SearchBase $searchOU | Sort-Object Name | ForEach-Object {
$group = $_
Get-ADGroupMember -Identity $group | Where-Object { $_.objectClass -eq 'user' } |
Get-ADUser | Where-Object { $_.Enabled -eq $false} | ForEach-Object {
Write-Host "Removing $($_.Name) from $($group.Name)" -Foreground Yellow
Remove-ADGroupMember -Identity $group -Member $_ -Confirm:$false #-whatif
}
}
The above removes one disabled user at a time and for each of them writes a line on the console.
You could make it work faster if you can cope with getting a different output on screen like this:
$searchOU = "OU=a,OU=b,OU=c,OU=d,OU=e,DC=f,DC=g,DC=com"
$result = foreach ($group in (Get-ADGroup -Filter "GroupCategory -eq 'Security'" -SearchBase $searchOU)) {
$users = Get-ADGroupMember -Identity $group | Where-Object { $_.objectClass -eq 'user' } |
Get-ADUser | Where-Object { $_.Enabled -eq $false}
if ($users) {
# the Remove-ADGroupMember cmdlet can take an array of users to remove at once
Remove-ADGroupMember -Identity $group -Member $users -Confirm:$false #-whatif
# output an object that gets collected in variable $result
[PsCustomObject]#{Group = $group.Name; RemovedUsers = ($users.Name -join '; ')}
}
}
# if you like, output to console as table
$result | Sort-Object Group | Format-Table -AutoSize -Wrap
# or write to CSV file
$result | Export-Csv -Path 'D:\Test\RemovedUsers.csv' -NoTypeInformation
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
}
}
I'd like to get an AD user account via powershell within a specific group.
I will know the GivenName and Surname of the user I will be looking for, so Get-ADUser seems like a good function to use.
The issue is that we have a large number of users in the OU and I want to limit the scope of the search to one AD group and the groups under that one AD group. As far as I can tell, the SearchBase parameter of Get-ADUser will only work for OUs and not for groups.
I'd like to do this as efficiently as possible (i.e. not get all the users in the group and search within those users).
You could use Get-ADGroupMember for enumerating the members of a group, and use that as input for Get-ADUser:
Get-ADGroupMember 'groupname' |
Get-ADUser -Properties EmailAddress |
Where-Object { $_.Surname -eq 'foo' -and $_.GivenName -eq 'bar' } |
Select-Object -Expand EmailAddress
If the group contains not only user objects you need to filter the members by class first:
Get-ADGroupMember 'groupname' |
Where-Object { $_.objectClass -eq 'user' } |
Get-ADUser -Properties EmailAddress |
Where-Object { $_.Surname -eq 'foo' -and $_.GivenName -eq 'bar' } |
Select-Object -Expand EmailAddress
For unrolling nested groups you need a recursive function:
function Unroll-Group($group) {
Get-ADGroupMember $group | ForEach-Object {
$userOrGroup = $_
switch ($_.objectClass) {
'group' { Unroll-Group $userOrGroup }
'user' { Get-ADUser $userOrGroup -Property EmailAddress }
}
}
}
Unroll-Group 'groupname' |
Where-Object { $_.Surname -eq 'foo' -and $_.GivenName -eq 'bar' } |
Select-Object -Expand EmailAddress
Note that this approach won't work for a user's primary group.
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.