Removing certain email addresses by domain or prefix from all mail objects in Exchange on-premises

Over the summer my company moved our email ingress and egress from on premises Cisco IronPort Gateways to Exchange Online Protection. While this moved us to a modern and cloud-based mail hygiene solution, we did lose some of custom mail routing schemes we implemented when we had full control of our mail flow. One such custom feature was the ability to selectively block SMTP addresses on any given mail object in our on-premises Exchange environment from external mail without having to block all external mail to the object.

This was accomplished by adding a second proxy address for each address we wanted to block from receiving external email at the Cisco Iron port level with ‘HIDE’. Such that when our Cisco IronPort’s did an LDAP lookup for an internal proxy address, if the same address was returned twice, (an SMTP and HIDE entry) then drop the message since that address should be internal only. In this example of the proxyaddresses on a given mail object:

hide:John.Mello@contoso.com
smtp:mello@mountainview.usa.com
smtp:mello@contosocloud.mail.onmicrosoft.com
smtp:mello@tailspintoys.com
SMTP:John.Mello@contoso.com

john.mello@contso.com would be blocked at the Cisco IronPort level since an LDAP search would return the following two addresses

hide:John.Mello@contoso.com
SMTP:John.Mello@contoso.com

The remaining address would be allowed through

smtp:mello@mountainview.usa.com
smtp:mello@contosocloud.mail.onmicrosoft.com
smtp:mello@tailspintoys.com

Once we moved to Exchange Online Protection, and the HIDE proxy addresses no longer functioned, we had to clean-up all the entries we had in our environment. To do so we created an Exchange Management Shell PowerShell script to do the following

  1. Run the script from the Exchange management shell
    1. The script assume you are getting non-deserialized objects. If you are not using the Exchange Management shell, then you will need to update it to take into account that you are getting deserialized objects
  2. Ensure you are viewing the entire forest
    1. This was needed for us since we have Exchange on premises in a resource domain and thus our public folders are in this domain while everything else is in a separate domain
  3. Grab all recipients
  4. Check if there are any HIDE proxyaddresses on the mail object. If not, then skip if so then
  5. Determine the Recipient detail type so we know which Set-* command to use to remove the addresses
    1. We could have just used Set-ADObject and instead to update the proxyaddresses property, but I wanted to keep this strictly within the commands available to Exchange Administrator
    1. In our environment we had a lot of contacts that required updating before we could edit the email addresses. So while we tried to force an update whenever we came across one, it still required manual intervention at the console to approve the change. We only had a few dozen in this state so after coming across the first one we filtered them out in the big run and then addressed them and manually accepted the update. In this case Set-ADObject would have allowed us to automate it, but wouldn’t fix the underlying need to update them
  6. Save the results to an array for later referencing in case we need to roll back

Outside the need to update our mail contacts here are some issues we ran into while running the script

  1. We encountered a number of distribution groups that gave the following error when we tried to remove the addresses : ‘Members can’t add themselves to security groups. Please set the group to Closed or Owner Approval for requests to join.’
    • In some research this was due to the fact that the distribution group was flipped to a Security using ADUC (Active directory users and computers) or vice verse, as opposed to done in Exchange
    • This put the distribution group on an inconsistent state as far as Exchange was concerned and we needed to run the following parameters when running the set-distributiongroup with the following parameters: -MemberJoinRestriction closed -MemberDepartRestriction closed
  2. We had exactly 1 object with a plus sign (‘+’) in it’s name, which the various set-* cmdlets do not like when using a distinguished name with a plus sign. In this case switching the script to leverage the Alias instead for each set-* cmdlet fixed that issue

Script

#This will ensure mail objects in other forests are included
Set-ADServerSettings -ViewEntireForest:$TRUE
$AllObjects = Get-Recipient -ResultSize Unlimited
[System.Collections.ArrayList]$Changelist = @()

ForEach ($MailObject in $AllObjects) {
    $TableEntry = [PSCustomObject]@{
        Name        = $MailObject.Name
        DistinguishedName = $MailObject.DistinguishedName
        PreviousEmailAddresses = $MailObject.EmailAddresses
        RecipientTypeDetails = $MailObject.RecipientTypeDetails
        RemovedAddress      = ""
        Result = "UNPROCESSED"
    }#$TableEntry = [PSCustomObject]@
    #If you were looking for specific doamin you could use a regex like such
    #[String[]]$ProxyAddressesToRemove = ($MailObject.EmailAddresses | Where-object SMTPAddress -match '^.*@DOMAIN\.com$').ProxyAddressString
    [String[]]$ProxyAddressesToRemove = ($MailObject.EmailAddresses | Where-object prefix -eq 'hide').ProxyAddressString
    If ($ProxyAddressesToRemove.count -ne 0) {
        $TableEntry.RemovedAddress = $ProxyAddressesToRemove
        Try{
            switch -regex ($MailObject.RecipientTypeDetails) {
                '^(MailUniversalSecurityGroup|MailUniversalDistributionGroup|RoomList)$' { $SetCommand = "Set-DistributionGroup"; break }
                '^(UserMailbox|TeamMailbox|SharedMailbox|RoomMailbox|EquipmentMailbox|DiscoveryMailbox)$' { $SetCommand = "Set-Mailbox"; break }
                'MailUser' { $SetCommand = "Set-MailUser"; break }
                '^(RemoteUserMailbox|RemoteRoomMailbox|RemoteEquipmentMailbox|RemoteSharedMailbox)$' { $SetCommand = "Set-RemoteMailbox"; break }
                'MailContact' { $SetCommand = "Set-MailContact"; break }
                'PublicFolder' { $SetCommand = "Set-MailPublicFolder"; break }
                'DynamicDistributionGroup' { $SetCommand = "Set-DynamicDistributionGroup"; break }
                Default {Throw "Unhandled RecipientTypeDetails: $($MailObject.RecipientTypeDetails)"}
            }#switch -regex ($MailObject.RecipientTypeDetails) {
            #We noticed that a good majority of the mail contacts in our enviroment were of 'ExchangeVersion' '0.0 (6.5.6500.0)'
            #Any attempt to update them resulted in a prompt to update, so in all instances we force an upgrade to our current version, which is '0.20 (15.0.0.0)'
            if ($SetCommand -ne 'Set-MailContact"' ) {
                .$SetCommand $MailObject.DistinguishedName -EmailAddresses @{Remove=$ProxyAddressesToRemove} -erroraction stop
            }#if ($SetCommand -ne 'Set-MailContact"' ) {
            Else {
                .$SetCommand $MailObject.DistinguishedName -EmailAddresses @{Remove=$ProxyAddressesToRemove} -ForceUpgrade:$true -Confirm:$false -erroraction stop
            }#Else
            $TableEntry.Result = 'HIDES REMOVED'
        }#Try
        Catch {
            $TableEntry.Result = $_
        }#catch
        $Changelist.add($TableEntry) | Out-Null
    }#If ($ProxyAddressesToRemove.count -ne 0) {
    Else{
        $TableEntry.Result = 'NO ACTION NEEDED'
        $Changelist.add($TableEntry) | Out-Null
    }#Else
    [System.GC]::Collect()
}#ForEach ($MailObject in $AllObjects) {

About mell9185

IT proffesional. Tech, video game, anime, and punk aficionado.
This entry was posted in Uncategorized and tagged , . Bookmark the permalink.

Leave a Reply