Removing Disabled Exchange Delegates with PowerShell
The PowerShell script in this article will retrieve all mailboxes in an Exchange environment and remove Full Access, Send-As, and Send-On-Behalf permissions from them. The final report is adequate for reporting mailbox delegates as well.
Anyone in an organization using Microsoft Exchange knows there are many reasons to grant individual access to another mailbox. So what happens when a user leaves an organization? If your IT Department deletes the user’s Active Directory account, this might not apply to you. However, depending on the IT department, it’s possible that accounts are disabled and then kept in Active Directory.
Scenario:
As employees leave your organization, their Active Directory accounts are disabled. Without manually removing delegates from Exchange mailboxes, the number of disabled delegates assigned to mailboxes increases until going back to “clean them up” is unmanageable; leaving these accounts in place could leave a potential security risk.
Solution:
Automating Exchange delegate cleanup steps with PowerShell.
Deleting delegates from shared mailboxes with PowerShell is done by looping through all Exchange mailboxes and reading the delegates on them. The script then does a one-time lookup in Active Directory for users and saves their status (Enabled/Disabled) for future reference; this saves from doing many queries against AD.
When running the script you will only need to provide your domain’s name, as it appears in Active Directory (samAccountName), but just a heads up: This script can take hours to complete.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
####################################################################################### # # This script gets all mailboxes and looks up each listed READ, Send-On-Behalf, of Full Access delegate. # If any delegate accounts are disabled in Active Directory, the account is removed from the mailbox. # ####################################################################################### write-host "" write-host -foregroundcolor Green "Run this script in ExchangeShell (Tested Exchange 2013). Requires PS ActiveDirectory module." ### Prompt for variables ### # Domain name that appears in same account name. Example: <domain>\<username> $DomainName = Read-host 'Enter domain name as it appears in a AD SamAccountName (Example for "MyDomain\username": Enter "MyDomain")' # Import Active Directory Module Import-Module ActiveDirectory # Build AD User Hash Table - For lookups on whether an account is enabled or disabled. $ADUserHash = @{} $ADUsers = Get-ADUser -filter * | select SamAccountName,Enabled foreach($user in $ADUsers){ $ADUserHash.Add($user.SamAccountName,$user.Enabled) } # Create function to lookup account status from hash table. This is to avoid constant LDAP queries. function ADUserCheck{ PARAM( [Parameter(Mandatory=$true)][string]$identity ) $HashResult = ($ADUserHash.GetEnumerator() | where {$_.name -eq $identity}) return $HashResult } # Create function to export item to result report. function Export-Results{ PARAM( [Parameter(Mandatory=$False)][string]$MailboxN, [Parameter(Mandatory=$False)][string]$UserN, [Parameter(Mandatory=$False)][string]$ADStatus, [Parameter(Mandatory=$False)][string]$AccessType, [Parameter(Mandatory=$False)][string]$RemoveAction, [Parameter(Mandatory=$False)][string]$Removed ) $list = New-Object psobject $list | add-member NoteProperty Mailbox $MailboxN $list | add-member NoteProperty User $UserN $list | add-member NoteProperty Delegate_Enabled $ADStatus $list | add-member NoteProperty AccessRights $AccessType #Add Report Entry $list | add-member NoteProperty RemoveAction $RemoveAction $list | add-member NoteProperty Removed $Removed $list | export-csv -path $ReportLogName -append IF($Error -ne $Null){ $error | out-file $ErrorLogName -append $error.clear() } return $list } # Create report file name to be created, along with error log. $currentDate = Get-Date -format yyyy-MM-dd $ErrorLogName = $PSScriptRoot+"\CleanUp_ErrorLog-"+$currentDate+".csv" $ReportLogName = $PSScriptRoot+"\DelegateCleanUp-"+$currentDate+".csv" # Clear any errors that might be stored. $error.clear() ####################################################################################### # Get mailboxes and then mailbox permissions. Do not include system accounts or inherited and denied accounts. $gmailbox = Get-Mailbox # Loop through each mailbox that is returned. ##################################### foreach($n in $gmailbox){ write-host -BackgroundColor white -ForegroundColor DarkRed "Mailbox is" $n.identity # Get mailbox delegates $box = get-mailboxpermission -identity $n.identity | where {($_.IsInherited -eq $False -AND $_.Deny -eq $False) -AND ($_.user.rawIdentity -like ($DomainName+"*"))} # Be sure that variables are clear $MailN = $Null $UserN = $Null $ADStatus = $Null # Remove Full Access Delegates ##################################### foreach($i in $box){ write-host -BackgroundColor white -ForegroundColor DarkYellow "Full access Delegate is" $i.identity # Set report action $RemoveAction = "" ##### Determine if FullAccess or Read Permission If ($i.AccessRights -match "FullAccess"){ $AccessType = "Full_Access" } if (($i.AccessRights -match "ReadPermission") -AND ($i.AccessRights -notmatch "FullAccess")){ $AccessType = "Read" } ##### Lookup User in hash table, see if account is disabled. # Use UserName (remove DomainName), which is the SamAccountName in AD. $UserString = $(($i.user).ToString()) # Convert to String $ADSamAccount = $UserString.Replace(($DomainName+"\"),"") # Remove "Domain\" $ADUser = ADUserCheck $ADSamAccount ##### IF account is disabled, remove delegate from mailbox. # IF FullAccess run this IF ($ADUser.value -eq $False) { IF($AccessType -eq "Full_Access"){ $DelegateIdentity = ($i.User.RawIdentity).ToString() Remove-MailboxPermission -Identity $i.identity -User $DelegateIdentity -AccessRights FullAccess -InheritanceType All -confirm:$false $RemoveAction = "Remove_Full-Access" $Removed = $true write-host -ForegroundColor Green "Found FullAccess - issued commmand" } # IF has READPermission run this IF($AccessType -eq "Read"){ $UserSID = ($i.User.SecurityIdentifier).ToString() #Use this because using "domain\username" can fail. Remove-MailboxPermission -Identity $i.identity -User $UserSID -AccessRights ReadPermission -InheritanceType All -confirm:$false $RemoveAction = "Remove_ReadPermission" $Removed = $true write-host -ForegroundColor Green "Found ReadPermission - issued commmand" } } ##### Export Results to file. Export-Results -MailboxN $i.Identity -UserN $i.User -ADStatus $ADUser.Value -AccessType $AccessType -RemoveAction $RemoveAction -Removed $Removed $Removed = $NULL # Reset Action Flag } # Remove Send-As Delegates ##################################### $RemoveSendAs = Get-ADPermission -identity $n.identity | Where-Object {($_.ExtendedRights -like "*send-as*") -and ($_.User.RawIdentity -like ($DomainName+"*"))} ######## Loop through each permission result foreach($i in $RemoveSendAs){ write-host -BackgroundColor white -ForegroundColor Darkgreen "Send access Delegate is" $i.identity #### Lookup user in hash table, see if account is disabled # Use UserName, remove "Domain\", which is the SamAccountName in AD. $UserString = $(($i.user.RawIdentity).ToString()) # Convert to String $ADSamAccount = $UserString.Replace(($DomainName+"\"),"") # Remove "Domain\" $ADUser = ADUserCheck $ADSamAccount # Get AD User # Clear Report Action $RemoveAction = "" # Set access type for this section $AccessType = "Send-As" #### IF account if disabled, remove delegate from mailbox # Determine if User Has Send-As Permission IF ($ADUser.Value -eq $False) { # IF ReadAccess Run This IF(($UserString -match $ADUser.Name) -AND ($i.ExtendedRights.RawIdentity -like "*send-as*")){ write-host -ForegroundColor Green "Found Send-AS- issued command" Remove-ADPermission -identity $n.identity -User $i.User.RawIdentity -ExtendedRights "Send As" -confirm:$false $RemoveAction = "Remove_Send-As" $Removed = $true } } ##### Export Results to file. Export-Results -MailboxN $i.Identity -UserN $i.User -ADStatus $ADUser.Value -AccessType $AccessType -RemoveAction $RemoveAction -Removed $Removed $Removed = $NULL # Reset Action Flag } # Remove Send On Behalf permissions ############################## #$onBehalf = get-mailbox -identity $n.identity | select -ExpandProperty GrantSendOnBehalfTo foreach ($i in $onBehalf){ # Start Report Line, Add Entries $RemoveAction = "" # Set access type for this section $AccessType = "Send-On-Behalf" #### Query AD user by distinguished name. $ADUser = Get-ADUser -Identity $i.DistinguishedName # Get AD User IF($ADUser.Enabled -eq $False){ write-host -ForegroundColor Green "Send On Behalf Permission Found. Issued Command" # Remove permission from mailbox. Set-Mailbox $n.identity -GrantSendOnBehalfTo @{remove=$i.DistinguishedName} # Set Report Action $RemoveAction = "Remove_Send-On-Behalf" $Removed = $true } ##### Export Results to file. $MailboxN = $n.Identity $UserN = $i.Name $ADStatus = $ADUser.Enabled Export-Results $Removed = $NULL #Reset Action Flag } } |
If you do not wish to copy this script, click this to save the .ps1: Download
When this script runs, you’ll get decent console output and the delegate report will include all of those delegates found, as well as those that have been removed: