Powershell script to remove users from distribution groups - powershell

I'm trying to find a solution to remove a user from all the distribution groups they are in. I found this script but am running into issues:
$email = Read-Host "Please provide a user's email address to remove from all distribution groups"
$mailbox = Get-Mailbox -Identity $email
$DN=$mailbox.DistinguishedName
$Filter = "Members -like ""$DN"""
$DistributionGroupsList = Get-DistributionGroup -ResultSize Unlimited -Filter $Filter
Write-host `n
Write-host "Listing all Distribution Groups:"
Write-host `n
$DistributionGroupsList | ft
$answer = Read-Host "Would you like to proceed and remove $email from all distribution groups ( y / n )?"
While ("y","n" -notcontains $answer) {
$answer = Read-Host "Would you like to proceed and remove $email from all distribution groups ( y / n )?"
}
If ($answer -eq 'y') {
ForEach ($item in $DistributionGroupsList) {
Remove-DistributionGroupMember -Identity $item.DisplayName –Member $email –BypassSecurityGroupManagerCheck -Confirm:$false
}
Write-host `n
Write-host "Successfully removed"
Remove-Variable * -ErrorAction SilentlyContinue
}
Else
{
Remove-Variable * -ErrorAction SilentlyContinue
}
It will get to the stage where it lists all the groups a user is in and asks whether or not to remove them, however it seems to get stuck on the –BypassSecurityGroupManagerCheck advising that there is an issue with this parameter.

I found this article on Microsoft TechCenter forum... obviously you'll need to expand it to meet your needs. - https://social.technet.microsoft.com/Forums/exchange/en-US/99c1f07b-12fa-4e06-95bd-246a757bb00f/powershellscript-to-remove-all-group-memberships-for-one-user
$DGs= Get-DistributionGroup | where { (Get-DistributionGroupMember $_ | foreach {$_.PrimarySmtpAddress}) -contains "user#domain.com"}
foreach( $dg in $DGs){
Remove-DistributionGroupMember $dg -Member user#domain.com
}

Based on the comments on the fact the - are not the same you could just open the script in PowerShell ISE or VisualStudio Code and then next to each command as you write it out you can press Tab on your keyboard and it would correct would if the parameters work with that command. But yes I would say based on your last comment
The operation couldn't be performed because object 'rebecca.edge#greensqaureaccord.co.uk' couldn't be found on 'DCWESTBROM01.accord.local'. + CategoryInfo : NotSpecified: (:) [Get-Mailbox], ManagementObjectNotFoundException + FullyQualifiedErrorId : [Server=EXCH2016-01,RequestId=ae3c7f93-e204-4245-aa5f-8678ff68aa63,[FailureCategory=Cmdlet-ManagementObjectNotFoundException] 4EA0476A,Microsoft.Exchange.Management.RecipientTasks.GetMailbox
That the email doesn't exist. I tested the script replacing all the dashes with correct ones (best done in Notepad to avoid formatting) and seems to work without error for me.

Related

Import-Csv with a dialog box

I am trying to add a dialog box into my Import-Csv script in Powershell v7. Basically, I do not want to remember different names of .csv files that have been exported from another script, so this will make it a lot easier. I have looked around and could not find anything that relates to what I am getting.
The code I have is this:
Write-Host "This will bulk add users to an AD group." -ForegroundColor Yellow
" "
# Import the data from CSV file and assign it to variable
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$OpenFIleDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.InitialDirectory = $InitialDirectory
$OpenFileDialog.Filter = "CSV (*.csv) | *.csv"
$OpenFileDialog.ShowDialog() | Out-Null
$Path = $OpenFileDialog.Filename
$Users = Import-Csv -Path $Path
# Specify target group where the users will be added to
# You can add the distinguishedName of the group. For example: CN=Pilot,OU=Groups,OU=Company,DC=exoip,DC=local
$Group = Read-Host "Enter in the target group name"
# Start transcript
Start-Transcript -Path "C:\Output Log\Users Logs\Add-ADUsers.log" -Append -UseMinimalHeader
foreach ($User in $Users) {
# Retrieve UPN
$UPN = $User.UserPrincipalName
# Retrieve UPN related SamAccountName
$ADUser = Get-ADUser -Filter "UserPrincipalName -eq '$UPN'" | Select-Object SamAccountName
# User from CSV not in AD
if ($null -eq $ADUser) {
Write-Host "$UPN does not exist in AD" -ForegroundColor Red
}
else {
# Retrieve AD user group membership
$ExistingGroups = Get-ADPrincipalGroupMembership $ADUser.SamAccountName | Select-Object Name
# User already member of group
if ($ExistingGroups.Name -eq $Group) {
Write-Host "$UPN already exists in $Group" -ForeGroundColor Yellow
}
else {
# Add user to group
Add-ADGroupMember -Identity $Group -Members $ADUser.SamAccountName
Write-Host "Added $UPN to $Group" -ForeGroundColor Green
}
}
}
Stop-Transcript
" "
Remove-Variable -Name Users
Write-Host "Users have finished adding to $group" -ForegroundColor Yellow
" "
}
The results of the output is this:
The search filter cannot be recognized
+ CategoryInfo : NotSpecified: (:) [Get-ADUser], ADException
+ FullyQualifiedErrorId : ActiveDirectoryServer:8254,Microsoft.ActiveDirectory.Management.Commands.GetADUser
+ PSComputerName : localhost
does not exist in AD
Note: "does not exist in AD" has a blank in front of it. I suspect it is because the import is not reading the file, but I cannot make sense of how to fix it.
Any help will be much appreciated!
Thank you in advanced.

My Powershell Script Does Not Write Output to Logfile

My Powershell script works well enough for what I want to achieve. It polls the entered in name and places that user's groups in an array. That array loops until it deletes all listed groups. Domain Users is the only group left, but that is polled by "Get-ADPrincipalGroupMembership," not "Get-ADUser." "Domain Users" is the group the user's email ties into. Once all groups are removed from their account, they are permanently disabled, but they can still access their email for paystub information until we delete their account entirely.
That said, I'm unable to write the script's group removal output to a logfile. Ideally, this will be a .log file, but a .csv file fails as well. What am I missing? The script successfully runs without error, but nothing writes to the log file of my choice.
Here is my script:
#Requires -Module ActiveDirectory
Import-Module ActiveDirectory
function Disable-ADUser{
$msg = 'Do you want to remove a user from all Security groups? [Y/N]'
do {
$response = Read-Host -Prompt $msg
if ($response -eq 'y') { # Beginning of if statment
#Asks user via a text prompt to ender the firstname and lastname of the end user to remove
$firstName = Read-Host "Please provide the First name of the User"
$lastName = Read-Host "Please provide the Last name of the User"
#The uesr's samaccoutname is found by searching exactly for the user's first name and lastname given in the above prompts
$samName = Get-ADUser -Filter "GivenName -eq '$firstName' -and Surname -eq '$lastName'"| Select-Object -ExpandProperty 'SamAccountName'
#All of the user's groups are queried based on their sam name
$listGroups = Get-ADUser -Identity $samName -Properties MemberOf | Select-Object -ExpandProperty MemberOf
#All of the user's groups are placed in an array
[System.Collections.ArrayList]$groupsArray = #($listGroups)
#Every group in the groupsArray is cycled through
foreach ($group in $groupsArray) {
#A text output is displayed before the user is removed from each group listed in the above array
#Once all groups have been cycled through, the for loop stops looping
Start-Transcript -Path Y:\Scripts\remove_user_groups.log
Write-Host "Removing $samName " -f green -NoNewline; Write-Host "from $group" -f red
Remove-ADGroupMember -Identity $group -Members $samName
Stop-Transcript
}
} # End of if statement
} until ($response -eq 'n')
}
Disable-ADUser
Here is the solution that worked.
Write-Host "Removing $samName " -f green -NoNewline
Write-Host "from $group" -f red
$OutputLine="Removing $samName from $group"
Out-File -FilePath Y:\Scripts\remove_user_groups.log -InputObject $OutputLine -Append
Remove-ADGroupMember -Identity $group -Members $samName
Windows Server 2012 is most probably using Powershell 4. Start-Transcript ignores Write-Host in powershell Versions before 5. Use Write-Output instead.
You might aswell use the -Verbose parameter for Remove-ADGroupMember.
If you are trying to write the output of the cmdlet to a file, you can do this...
#Every group in the groupsArray is cycled through
foreach ($group in $groupsArray) {
#A text output is displayed before the user is removed from each group listed in the above array
#Once all groups have been cycled through, the for loop stops looping
Write-Host "Removing $samName " -f green -NoNewline; Write-Host "from $group" -f red
Remove-ADGroupMember -Identity $group -Members $samName |
Out-File -FilePath 'Y:\Scripts\remove_user_groups.log' -Append
}
Also, there is no real reason to have this on the same line:
Write-Host "Removing $samName " -f green -NoNewline; Write-Host "from $group" -f red
... as this is not a one-liner. It is just all code on one line.
This is more prudent:
Write-Host 'Removing $samName ' -f green -NoNewline
Write-Host 'from $group' -f red
... and that screen output would still be on the same line. As a best practice. Use single quotes for simple strings, double for expansion, and some formatting use cases.

Powershell - Order of script

I am trying to input a user using a variable and check active directory to confirm the full name of the user and pause the script before running the next command.
The script is running the pause command before the get-aduser command - see below script
#Enter Username
$username = read-host "Username"
Get-ADUser -Filter "Name -eq '$username'" | Select-Object name, samaccountname
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
#Removes user from groups
Get-ADPrincipalGroupMembership -Identity $username | where {$_.Name -notlike "Domain Users"} |% {Remove-ADPrincipalGroupMembership -Identity $uSername -MemberOf $_ -Confirm:$false}
write-output End
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
In my experience, Get-ADUser and similar commands can take a long time to run, possible up to 20 seconds or so. Rarely, I have found that it makes the code unusable due to some commands running before or after it. If you want to test to see if this is really the case for you, add this line in between every other line in your code:
Read-Host -Prompt "Press Enter to continue"
That way, you can test whether there is a real difference between when you put that line there, and if you don't. If there is actually a difference, you may have to look into start-sleep or wait.
I would do something like this to have the user validate, cause i think that is what you are after, before continuing to revoke the users group membership
Write-Host "`nEnter the UserName: " -NoNewline -ForegroundColor Yellow
$UserName = Read-Host
$UserName = Get-ADUser -Filter "Name -eq '$UserName'" | Select-Object Name, SamAccountName
Write-Host "`nRevoke membership of all groups for user" $UserName.Name "("$UserName.SamAccountName")?`n [Y]es, [N]o : " -ForegroundColor Yellow -NoNewline
$Confirmation = Read-Host
While ("y","yes","n","no" -notcontains $Confirmation) {
Write-Host "`nNot a valid input! Please try again ..." -ForegroundColor Red
Write-Host "`nRevoke membership of all groups for user" $UserName.Name "("$UserName.SamAccountName")?`n [Y]es, [N]o : " -ForegroundColor Yellow -NoNewline
$Confirmation = Read-Host
}
If ($Confirmation -eq "n" -or $Confirmation -eq "no") {
Write-Host "Aborted!" -ForegroundColor Red
Break
}
# Next step here!
# Get-ADPrincipalGroupMembership -Identity $UserName | where {$_.Name -notlike "Domain Users"} |% {Remove-ADPrincipalGroupMembership -Identity $UserName -MemberOf $_ -Confirm:$false}
Just another piece of code, these kind of changes needs some proper logging and error handling, while my code only logs to the console it can still be useful.
It uses confirm in place of 'pause' so the user can choose to continue or stop.
### CmdletBinding
# Alows the use of -Whatif(not used), -Confirm, -Verbose and -Debug.
# Reference: https://technet.microsoft.com/en-us/library/ff677563.aspx
# https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_functions_cmdletbindingattribute
# https://blogs.technet.microsoft.com/poshchap/2014/10/24/scripting-tips-and-tricks-cmdletbinding/
[CmdletBinding(
SupportsShouldProcess = $true,
ConfirmImpact=’High’
)]
# Script parameters.
Param(
[parameter(HelpMessage = "Command parram, not used.")]$Command = "nothing"
#Run with PowerShell Fix, reference: https://social.technet.microsoft.com/Forums/office/en-US/fe7fb473-7ed6-4397-9c95-120201c34847/problems-with-powershell-30?forum=winserverpowershell
)
#Console clean-up.
Clear-Host
# Set error action to Stop, if something happens and it isnt inside a trap (try/catch) then stop.
$ErrorActionPreference = "Stop"
# Controls the Verbose Output
$VerbosePreference = "Continue" #Optional
#Intial message for User execution, whitespace is for the progressbars.
"
Script: Remove-ADUserGroupMembership.ps1
"
Write-Verbose "Starting main loop."
While ($true){
#White space for in between questions.
Write-Host "
"
#Retrieve username from user input.
Write-Host "Provide the ADUser for ADGroup removal here:"
$Username = read-host "Username"
#Retrieve ADUser object from AD.
Write-Verbose "Querying Active Directory for user $Username"
Try {
$ADUser = Get-ADUser $Username
Write-Verbose "User Found, $($ADUser.Name) "
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
Write-Warning "Could not find user $Username in Active Directory, check spelling and try again."
Continue #this wil reset the while loop
}
Catch {
Write-Warning "Unknown Errror, Could not retrieve user $Username from Active Directory, please try again."
Continue #this wil reset the while loop
}
#Retrieve GroupMembership for user.
Write-Verbose "Querying Active Directory for GroupMembership of User $($ADUser.name), exluding Domain Users"
Try {
$GroupMembership = $ADUser | Get-ADPrincipalGroupMembership | where {$_.Name -notlike "Domain Users"}
Write-Verbose "Found $($GroupMembership.count) GroupMemberships for User $($ADUser.name) (Not inluding Domain Users)"
}
Catch {
Write-Warning "Unknown Errror, Could not retrieve GroupMembership for user $($ADUser.Name) from Active Directory, please try again."
Continue #this wil reset the while loop
}
#Remove GroupMembership for user.
if ($pscmdlet.ShouldProcess("$($ADUser.name)", "Remove-ADPrincipalGroupMembership {$($GroupMembership.count) Groups}")) {
Write-Verbose "Entering GroupMembership removal loop for user $($ADUser.name)"
Foreach ($Group in $GroupMembership) {
Try {
$ADUser | Remove-ADPrincipalGroupMembership -MemberOf $Group -WhatIf -Confirm:$true
Write-Verbose "$Group removed from from user $($ADUser.name)"
}
catch {
Write-Warning "An Error occured, could not remove group $Group from user $($ADUser.Name)"
Continue #this will skip this group.
}
}
}
else {
Write-Warning "Action Remove-ADPrincipalGroupMembership {$($GroupMembers.count) Groups} canceled for $($ADUser.name)"
}
Read-Host "Press Enter to exit."
break #exit from while loop
}

Simplify powershell query

I am learning powershell, and a looking to query couple of AD groups and determine whether a user is member of AD group.
Part 1 : Query a AD group which has 10 nested AD groups
Part 2: Query user's AD group and pull a list
Part 3: Not posted, but to compare output of Part 1 and Part 2
Searched online and found some tits and bits. I am aware of Active Directory module, but avoid using it, since this script would be executed by user who is non-technical and avoid installing RSAT just for that.
I have Powershell version 2 and Windows 7
Part 1
group1 is the AD group which has 10 nested AD groups.
Write-Host "Fetching information from groups.Please wait.."
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$group=[System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity ($ct,'group1')
$group1 = $group.GetMembers | Where {$_.StructuralObjectClass -eq "group"} | Select SamAccountName
$group1 = $group1 -replace("=", " ") -replace("{", " ") -replace("#", " ") -replace ("}", " ") -replace("SamAccountname", " ") -replace '\s+', ' '
$ADGroups = foreach ($l in group1) {$l.trim()}
To ensure the AD group information is fetched by program or exit the script
if (($ADgroups | out-string) -like $null) {
Write-Host "Unable to fetch AD groups information" -foreground "red"
Start-sleep 10
break
}
Part 2
Temp location where files can be written
$location = "C:\AD"
$ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($ct, $username)
If ($user -like $null) {
Write-host "User does not exist in AD" -foreground "magenta"
start-sleep 10
break
}
Write-Host "Please wait...Looking user group membership..."
$usergroups = $user.GetGroups()
Removing file if exists.
Remove-Item $location\useradgroups.txt
$usergroups | select SamAccountName | Out-File $location\useradgroups.txt -append
$testr = gc $location\useradgroups.txt
if (($testr | out-string) -like $null) {
Write-Host "Unable to fetch user AD groups information" -foreground "red"
Start-sleep 10
Remove-Item $location\useradgroups.txt
break
}
$useradgroups = foreach ($l in $testr ) {$l.trim()}
$useradgroups | Select-String -Pattern "\w" | out-file $location\useradgroups.txt
$useradgroups = gc $location\useradgroups.txt
Question:
Unless I trim the output, cannot compare it. so had to write the script as shown above:
Avoid writting the output to text file
Avoid -replace("=", " ") -replace("{", " ") in Part 1
Simplify the code.
Any suggestion from Powershell gurus would be welcomed. It would help me in learning process
So what you're trying to do is recursively get ADGroupMembers? Sort of like this or this? There is always more than one way to do things. You can use the [ASDI] type accelerator.
$groups = [adsi]'LDAP://CN=GroupName,OU=Groups,OU=Place,DC=Tomorrow,DC=Today'
$group | Get-Member
foreach($group in $groups){
$members = $group.member
foreach($member in $members){
#Is this a group or a user?
#If group Get the Group members
#If a user say the user is part of the group
}
}
Are you trying to format the distinguishedName of a user?
$user = "CN=Huck Finn,OU=Users,OU=Today,OU=Tomorrow,DC=Yesterday,DC=com"
$splits = ($user -split ',')
[PSCustomObject]#{Username=$splits[0].substring(3);OUPath=($splits[1..($splits.Count-1)] | % {$_.substring(3)}) -join '\'}
Hope I helped.
Untested, but can't you use something like this for part 1?
Import-Module ActiveDirectory
Get-ADGroupMember "group1" | select sAMAccountName

Error handling and minimize script output to end-user

I have a script which loops through a list of users (samaccountname):
# Read usersfile to variable
$users = get-content ("users.txt")
# Get current time
$now = $(get-date -uformat "%H:%M %d/%m/%Y")
# Loop through list of users
foreach($user in $users) {
# Disable user
Disable-QADUser $user
# Set informative description
Set-QADuser $user -Description "Disabled $now"
# Delete all groupmemberships except "domain users"
Get-QADGroup -Containsmember $user | where-object { $_.name -ne 'domain users'} | Remove-QADGroupmember
# Move to "disabled users" group
move-QADObject $user -NewParentContainer 'contosoc.com/Disabled users'
# Hide from addresslist
Set-Mailbox -identity $user -HiddenFromAddressListsEnabled $true
# Moving mailbox to disabled users database
Move-Mailbox -Identity $user -TargetDatabase "myserver\mydb" -BadItemLimit 50 -Confirm:$False
}
I would like to:
Suppress output from the different cmdlets and only show "$user is OK!" if all is ok and log success to a logfile.txt
Display "Error!" and the command that failed if not ok. And output the complete error msgs to a separate logfile.
I've been thinking about doing a if(!cmdlettorun) { write-host "Error!" } But I'm thinking that there must be a better way.
How should I do error handling in a proper fashion so I minimize the displayed output but still let me see it if desirable?
For suppressing cmlet output you can pipe to out-null or precede the command with:
[void](.. your cmdlets..)
a good way for your goal is using a tray-catch-finally code like in this minimized code:
$a = $ErrorActionPreference
$ErrorActionPreference = "SilentlyContinue"
foreach($user in $users) {
try
{
... yours code ...
$JobStatus = "OK"
}
catch [exception]
{
$("Error catched: " + $_.Exception.GetType().FullName) | out-file c:\file.log
$("Error catched: " + $_.Exception.Message) | out-file c:\file.log -append
$JobStatus = "not OK"
continue;
}
finally
{
write-host "$user is $JobStatus!"
}
$ErrorActionPreference = $a
}
For some hint to use try-catch-finally read here