remove terminated users from DISTRIBUTION groups in AD - powershell

I am working on a solution that will help keep our Active Directory clean, so I want to use a Powershell script that will remove the disabled accounts from all groups.
I got the following script:
foreach ($username in (Get-ADUser -SearchBase "OU=Terminated Users,DC=corp,DC=company,DC=com" -filter *)) {
# Get all group memberships
$groups = get-adprincipalgroupmembership $username;
# Loop through each group
foreach ($group in $groups) {
# Exclude Domain Users group
if ($group.name -ne "domain users") {
# Remove user from group
remove-adgroupmember -Identity $group.name -Member $username.SamAccountName -Confirm:$false;
# Write progress to screen
write-host "removed" $username "from" $group.name;
# Define and save group names into filename in c:\temp
$grouplogfile = "c:\temp\" + $username.SamAccountName + ".txt";
$group.name >> $grouplogfile
}
}
}
It's working fine but only for security groups. Users are not deleted from distribution groups. I searched the Internet and people mostly suggest to use "Remove DistributionGroup Member" cmdlet. However, this is the Exchange cmdlet and we use Google Workspace for our email, so this cmdlet is not recognized when I run it on the DC.
Any idea how to solve it? Thanks!

The cmdlet Remove-ADPrincipalGroupMembership will help:
#get all disabled users in specified OU
$disabledUsers = get-aduser -SearchBase "OU=test_piotr,DC=corp,DC=company,DC=com" -LDAPFilter '(userAccountControl:1.2.840.113556.1.4.803:=2)' -Properties memberof,samaccountname
#Loop through array and remove groupmembership and store operation result in $result
$result = #(
foreach ($user in $disabledusers){
try {
#Only process user account if memberships are present
If ($user.memberof){
#Remove all memberships the user has currently, no need to exclude domain users as $user.memberof does not return it
$null = Remove-ADPrincipalGroupMembership -Identity $user.samaccountname -MemberOf $user.memberof -Confirm:$false -ErrorAction:stop
write-host "Removed user: $($user.samaccountname) from groups: $($user.memberof -join ',')"
#Build object for logfile, you could also loop through $user.memberof to create one object per removed group
$attrsHt = #{
smaccountname=$user.samaccountname
group=($user.memberof -join ',')
status='removed'
exception=$null
}
New-Object -typename psobject -Property $attrht
}
}
Catch {
write-error "Failed to remove user: $($user.samaccountname) from groups: $($user.memberof -join ',') - Exception: $_"
$attrsHt = #{
smaccountname=$user.samaccountname
group=($user.memberof -join ',')
status='error'
exception=$_
}
New-Object -typename psobject -Property $attrht
}
}
)

Related

How to fix the remove from group function

I am trying to make a script that will add and remove computers from a group if they are in a specific ou. What I have so far adds the computers and then when run again it removes all computers from the group. Then run it again and it adds them all back.
It should add the computers that are in the OUs and remove the computers that have been moved out of the OU
$OUSTUCOMPUTERS = "OU=STUDENT,OU=Computers,DC=domain,DC=local"
$OUConstruction = 'BCT','Carpentry','Electrical Occupations','HVAC'
$List = #{}
$Group=[ADSI]"LDAP://CN=CompStuConstruction,OU=Computer Groups,OU=Computers,DC=domain,DC=local"
foreach ($Shop in $OUConstruction)}
{$Distinguishedname = "OU="+"$Shop"+","+"$OUSTUCOMPUTERS"
$OU = [ADSI]"LDAP://$distinguishedname"
# Enumerate all objects in the OU.
$arrChildren = $OU.Children
ForEach ($Child In $arrChildren)
{
# Only consider computer objects.
If ($Child.Class -eq "computer")
{
# Add all computers in the OU to the hash table.
$List.Add($Child.distinguishedName, $True)
# Check if user a member of the group.
If ($Group.IsMember($Child.ADsPath) -eq $False)
{
# Add the computers to the group.
$Group.Add($Child.ADsPath)
"Added " + $Child.distinguishedName
}
}
}
}
# Enumerate all members of the group.
ForEach ($Member in $Group.member)
{
# Check if this member object is a computer object in the OU.
If ($List.ContainsKey($Member)-eq $False)
{
# Remove this member from the group.
$Group.Remove("LDAP://$Member")
"Removed " + $Member
}
}
I would suggest to use the ActiveDirectory module instead of [ADSI]. Then do
# get your group
$group = Get-ADGroup -Identity "your group"
# clear group
Remove-ADGroupMember -Identity $group -Members #(Get-ADGroupMember -Identity $group)
# add back only correct computer objects
Add-ADGroupMember -Identity $group -Members #(Get-AdComputer -Filter * -SearchBase "your ou" -SearchScope Subtree)

Powershell scripting: How to add users to AD Group as user moves from OU,

I am trying to get a script to run that checks if User within an OU and added to the same named Security Group,
If the user moves to another OU it needs to be removed from the group and added to the new group
I understand the concept of what I need to do but I cannot get it into PowerShell.
The User will move from OU to OU, and will need to be removed from the current group and added to the New group
OU's and Security Groups are named the Same:
OU Structure is
You need to use the Compare-object cmdlet.
Try this on some Test OUs and Test user accounts to be safe:
(Update the variables in the beginning of the script to match your environment...)
$VerbosePreference = "continue"
$UserOULocation = "OU=Test unit,OU=OU1,DC=Domain,DC=local" # please update
$DCServerName = "<servername>" # please update
$ADUsers = Get-ADUser -SearchBase $UserOULocation -filter * -Properties * -Server $DCServerName
$OUNames = Get-ADOrganizationalUnit -SearchBase $UserOULocation -Filter *
Foreach ($ADUser in $ADUsers)
{
$Groups = $ADUser.MemberOf | % {Get-ADGroup $_}
$CurrentOU = $ADUser.distinguishedname.Split(",")[1].replace("OU=","")
If ($Groups)
{
# You need to comapre the list of OUs to the groups that the account is a member of
$Comparing = Compare-Object $CurrentOU $Groups.name
foreach ($compare in $Comparing | Where-Object {$OUNames.name -contains $_.inputobject})
{
If ($Compare.SideIndicator -eq "<=")
{
$GroupName = $Compare.InputObject
Write-Verbose "Adding user $($aduser.name) to group $GroupName"
Add-ADGroupMember -Identity $Compare.InputObject -Members $ADUser.SamAccountName -Verbose
}
else
{
$GroupName = $Compare.InputObject
Write-Verbose "Removing user $($aduser.name) from group $GroupName "
Remove-ADGroupMember -Identity $GroupName -Members $ADUser.SamAccountName -Verbose
}
}
}
else
{
Write-Verbose "No - No groups found for user $($aduser.name)"
Write-Verbose "ACTION - Adding user to groups"
Add-ADGroupMember $CurrentOU -Members $ADUser.samaccountname
}
}
If this question is about you wanting to move user 'X' to another OU and by doing so:
remove him from the group with the same name as the OU he is in currently
add him to the group with the same name as the OU he is moved to
Then you could do something like this:
# change these to match your configuration
$userToMove = 'jdoe'
$destinationOU = 'OU=Accounting,DC=Europe,DC=Fabrikam,DC=com'
$user = Get-ADUser -Filter "SamAccountName -eq '$userToMove'" -ErrorAction -SilentlyContinue
if (!$user) { Write-Warning "User '$userToMove' does not exist" }
else {
# parse the OU from the users DistinguishedName property
$currentOU = [regex]::Match($user.DistinguishedName, '(?i)(?=OU=).*$').Value
# use the OU names to get the names for the groups
$currentGroup = (Get-ADOrganizationalUnit -Identity $currentOU).Name
$newGroup = (Get-ADOrganizationalUnit -Identity $destinationOU).Name
# if you need the DisplayNames instead,add parameter -Properties DisplayName
# and get the DisplayName property value. ie:
# $currentGroup = (Get-ADOrganizationalUnit -Identity $currentOU -Properties DisplayName).DisplayName
# $newGroup = (Get-ADOrganizationalUnit -Identity $destinationOU -Properties DisplayName).DisplayName
# remove the user from the group with the same name as the OU he's currently in
Remove-ADGroupMember -Identity $currentGroup -Members $user
# add the user to the new group
Add-ADGroupMember -Identity $newGroup -Members $user
# finally move the user to the new OU
$user | Move-ADObject -TargetPath $destinationOU
}
Regex details used in parsing the OU from the users DistinguishedName:
(?= Assert that the regex below can be matched, starting at this position (positive lookahead)
OU= Match the characters “OU=” literally
)
. Match any single character that is not a line break character
* Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
$ Assert position at the end of the string (or before the line break at the end of the string, if any)

Remove list of users from list of groups, BUT users from a different domain (foreign security principals)

I have a list of accounts (SAM account names) and a list of groups all the accounts need to be removed from. My problem is that I have to specify -server DC01 since the users are in a different domain. I can do that for removing 1 user at a time with:
$Groups = Get-Content C:\temp\groups.txt
$user = get-aduser <username> -Server "DC01.domain.com"
foreach ($Group in $Groups) {
Write-Host "Removing $user from $group" -Foreground Yellow
Remove-ADGroupMember -Identity $group -Members $user -Confirm:$false
}
But I have several long lists of users, and there's got to be a way to do this for all of them.
I tried adding the ** entries below, but no dice:
$Groups = Get-Content C:\temp\groups.txt
$user = Get-Content C:\temp\users0.txt **-Server "DC01.domain.com"**
foreach ($Group in $Groups) {
Write-Host "Removing $user from $group" -Foreground Yellow
Remove-ADGroupMember -Identity $group -Members $user **-searchbase = "DC=domain,DC=com"** -Confirm:$false
}
Thanks in advance for any suggestions!
I cannot test this right now, but I believe you can set the -Members parameter to an array of users.
Apparently, the Remove-ADGroupMember does not work if you supply an array of DistinguishedNames when the users are from another domain, so we need to use the full default (Microsoft.ActiveDirectory.Management.ADUser) objects as returned by the Get-ADUser cmdlet.
$DCGroups = (Get-ADDomain your.domainA.com).PDCEmulator # get a PDC emulator in domain A where the groups are
$DCUsers = (Get-ADDomain your.domainB.com).PDCEmulator # get a PDC emulator in domain B where the users are
$Groups = Get-Content 'C:\temp\groups.txt' # the groups are in domainA
# get an array users ADUser Objects from Domain B
$users = Get-Content 'C:\temp\users0.txt' | ForEach-Object {
$user = Get-ADUser -Filter "SamAccountName -eq '$_'" -Server $DCUsers -ErrorAction SilentlyContinue
if ($user) { $user }
}
# remove these users from the groups in Domain A
foreach ($Group in $Groups) {
Write-Host "Removing $($users.Count) users from $Group" -Foreground Yellow
Remove-ADGroupMember -Identity $Group -Members $users -Confirm:$false -Server $DCGroups
}
Edit
To overcome the exception being thrown when you try to remove a user that is not a member of the group, you need to add some extra code to make sure the -Members parameter of Remove-ADGroupMember contains only ADUser objects that currently are member of this group.
$DCGroups = (Get-ADDomain your.domainA.com).PDCEmulator # get a PDC emulator in domain A where the groups are
$DCUsers = (Get-ADDomain your.domainB.com).PDCEmulator # get a PDC emulator in domain B where the users are
$Groups = Get-Content 'C:\temp\groups.txt' # the groups are in domainA
# get an array users ADUser Objects from Domain B
$users = Get-Content 'C:\temp\users0.txt' | ForEach-Object {
$user = Get-ADUser -Filter "SamAccountName -eq '$_'" -Server $DCUsers -ErrorAction SilentlyContinue
if ($user) { $user }
}
# remove these users from the groups in Domain A
foreach ($Group in $Groups) {
# get a list of SamAccountNames of users that are currently a member of this group
$members = Get-ADGroupMember -Identity $Group | Where-Object {$_.objectClass -eq 'user'} | Select-Object -ExpandProperty SamAccountName
# create a subset of users that are indeed in the members list
$removeThese = #($users | Where-Object { $members -contains $_.SamAccountName })
if ($removeThese.Count) {
Write-Host "Removing $($removeThese.Count) users from $Group" -Foreground Yellow
Remove-ADGroupMember -Identity $Group -Members $removeThese -Confirm:$false -Server $DCGroups
}
else {
Write-Host "No users need to be removed from group '$Group' " -Foreground Green
}
}

Add users into AD group with correct CSV output

I am trying to add an AD group into user profiles based on an OU
I had a similar script working, so tried to modify it and failed. I am guessing it's the " -Identity $_" it maybe, but I am not good enough to debug.
#Create a new class to hold the info for our CSV entry
Class CSVEntry{
[String]$UserName
[String]$GroupName
[String]$TimeStamp
}
#Creating a list to hold the CSV entries
$Results = New-Object 'System.Collections.Generic.List[PSObject]'
#Defined the name of the group here
$GroupName = 'GROUPS NAME'
$ou = 'ou=XX,ou=XX,ou=XX,dc=XX,dc=local'
Get-ADUser -Filter * -SearchBase $ou | ForEach-Object{
#Add the user to the group here
Add-ADPrincipalGroupMembership -MemberOf $GroupName Identity $_
#Write-Host $_.Name - $groupName
#Build a custom CSVEntry object and add it to the list
$newRecord = [CSVEntry]::new()
$newRecord.UserName = $_.Name
$newRecord.GroupName = $groupName
$newRecord.TimeStamp = Get-Date
#Add the new record to the list
$Results.Add($newRecord)
}
#Export the list of CSV entries
$Results | Export-Csv C:\PS\AddADGroupToUsers.csv
errors:
Add-ADPrincipalGroupMembership : A positional parameter cannot be found that accepts argument 'CN=NAME,OU=XX,OU=XX,OU=XX,OU=XX,DC=XX,DC=LOCAL'.
At line:18 char:5
+ Add-ADPrincipalGroupMembership -MemberOf $GroupName Identity $_
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Add-ADPrincipalGroupMembership], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.ActiveDirectory.Management.Commands.AddADPrincipal
GroupMembership
EDIT:
So, the script doesn't actually do any changes, the group doesn't get added to the users. the output on screen is:
WARNING: User is already a member of group XYZ
WARNING: User is already a member of group XYZ
WARNING: User is already a member of group XYZ
UserName GroupName TimeStamp
-------- --------- ---------
shows ok XYZ 14/10/2019 14:50:23
shows ok XYZ 14/10/2019 14:50:23
shows ok XYZ 14/10/2019 14:50:23
All I have changed is the group name to XYZ and username shows ok in the second half. But, shows blank in the top, and I assure you that a) the user isn't already in the group and b) the script isn't adding them
Current tweaked code, warts and all but sanitised:
$groupName = 'GROUP'
$ou = 'setcorrectly'
$cred = Get-Credential -credential dom\usr
$results = Get-ADUser -Filter * -SearchBase $ou -Credential $cred | ForEach-Object {
#Add the user to the group here
try {
Add-ADGroupMember -Identity $groupName -Members $_.DistinguishedName -Credential $cred -ErrorAction Stop
}
catch {
Write-Warning "User $($_.Name) is already a member of group $groupName"
}
# output a PsCustomObject that gets collected in the $results variable
[PsCustomObject]#{
'UserName' = $_.Name
'GroupName' = $groupName
'TimeStamp' = Get-Date
}
}
# output on console
$results | Format-Table -AutoSize
# Export to CSV file
$results | Export-Csv C:\PS\AddADGroupToUsers.csv -NoTypeInformation
Read-Host -Prompt "Press Enter to exit"
CSV output shows the second half of the screen output only, and doesn't say anything is already a member
Below uses Add-ADGroupMember to add user(s) to 1 group instead of Add-ADPrincipalGroupMembership which is used to add 1 user to multiple groups.
It also uses [PsCustomObject]s to output the results, so you don't need to use the Class CSVEntry.
# Define the name of the group here.
# can be either:
# A distinguished name
# A GUID (objectGUID)
# A security identifier (objectSid)
# A Security Account Manager account name (sAMAccountName)
$groupName = '<NAME OF THE GROUP>'
$ou = 'ou=XX,ou=XX,ou=XX,dc=XX,dc=local'
$results = Get-ADUser -Filter * -SearchBase $ou | ForEach-Object {
#Add the user to the group here
$userName = $_.Name
try {
Add-ADGroupMember -Identity $groupName -Members $_.DistinghuishedName -ErrorAction Stop
# output a PsCustomObject that gets collected in the $results variable
[PsCustomObject]#{
'UserName' = $_.Name
'GroupName' = $groupName
'TimeStamp' = Get-Date
}
}
catch {
Write-Warning "User $userName is already a member of group $groupName"
}
}
# output on console
$results | Format-Table -AutoSize
# Export to CSV file
$results | Export-Csv C:\PS\AddADGroupToUsers.csv -NoTypeInformation
Edit
If you want the $results variable to ALSO contain users that are already a member of the group, you could simply move the creation of the [PsCustomObject] below the catch{..} block:
$results = Get-ADUser -Filter * -SearchBase $ou | ForEach-Object {
#Add the user to the group here
$userName = $_.Name
try {
Add-ADGroupMember -Identity $groupName -Members $_.DistinghuishedName -ErrorAction Stop
$status = "User added successfully"
}
catch {
Write-Warning "User $userName is already a member of group $groupName"
$status = "User is already a member"
}
# output a PsCustomObject that gets collected in the $results variable
[PsCustomObject]#{
'UserName' = $userName
'GroupName' = $groupName
'TimeStamp' = Get-Date
'Status' = $status
}
}
Hope that helps

Powershell Import-Csv then Get-Aduser results in all users in ad being displayed when a Blank Line appears

I am writing a powershell script to disable users due to the fact that we get a list of them everyday and it is monotonous. I paste the list from the ticket into a csv formatted as Lastname, Firstname then run my script with imports the list, serches ad and ask if you want to disable if it finds them. Here is the code...
# Set variables
$Import = "C:\Scripts\Support Files\Users_To_Disable.csv"
$Export = "C:\Scripts\Support Files\Disabled_Users_Output.txt"
# Import user list
$Users = Import-CSV $Import
foreach ($User in $Users)
{
# Set user variables
$LastName = $User.("Surname")
$FirstName = $User.("GivenName")
# Use user variables from list to search ad
$UserName = (Get-ADUser -Filter "GivenName -like '$FirstName*' -and Surname -like '$LastName*'").SamAccountName
# What to do if it finds nothing
If ($UserName -eq $Null)
{
Write-Host $LastName, $FirstName NA -ForegroundColor Yellow
Write-Output "$LastName, $FirstName NA" | Out-File $Export -Append
}
# What to do if it finds a user
Else
{
# Ask for user input
Write-Host $LastName, $FirstName Found -ForegroundColor Green
Write-Host UserName = $UserName -ForegroundColor Green
DO {
$Disable = Read-Host "Do you want to disable user? (Y/N)"
If($Disable -eq "Y")
{
# Disable the user
Disable-ADAccount -Identity $UserName
# Move the user
Get-ADUser $UserName | Move-ADObject -TargetPath "OU=Disabled - Retention,DC=intranet,DC=sw"
# Add Disabled Users group
Add-ADGroupMember "Disabled Users" -Members "$UserName"
# Set Disable Users as primary group
$Group = Get-ADGroup "Disabled Users" -Properties #("PrimaryGroupToken")
Get-ADUser "$UserName" | Set-ADUser -Replace #{PrimaryGroupID=$Group.PrimaryGroupToken}
# Remove all other groups
$User = Get-ADUser "$UserName" -Properties MemberOf
$Groups = $User.MemberOf |ForEach-Object { Get-ADGroup $_ }
$Groups | ForEach-Object { Remove-ADGroupMember -Identity $_ -Members $User -Confirm:$false }
# Output
Write-Host $LastName, $FirstName Disabled -ForegroundColor Red
Write-Output "$LastName, $FirstName Disabled" | Out-File $Export -Append
Break
}
}
Until ($Disable -eq "N")
}
}
Invoke-Item $Export
All of that works, what is scary is that if there are blank cells above a user then it returns all of the users in ad and asks if you want to disable all of them. In other words if the csv looks like this...
Surname GivenName
User Test
Everything works fine, but if it looks like this...
Surname GivenName
User Test
Pandemonium, well not really but it does ask if you want to initiate a resume generating event, which I don't so how can I build in some safety that would stop it from returning all of ad when there are blanks in the csv before users?
You can eliminate the blank lines by filtering out Null values on your import, which should resolve the problem.
$Users = Import-CSV $Import | Where-Object {$_.Surname}