Bulk replacing proxy addresses based on a pattern

This one comes from the TechNet forums, where a user posted a query about being able to update the email aliases (proxy addresses) of a mailbox by removing a predefined string. As usual with such queries, the answer is to use PowerShell, so let’s dig in.

Assume you have a mailbox (appropriately named “left” in my case), which email addresses look something like this:

SMTP:firstname.lastname@domain.com
smtp:left@domain.com
smtp:leftSAM.something@domain.com
smtp:SAM.blabla@domain.com
smtp:notleft@domain.com
smtp:notleft2@domain.com

You want to remove any and all aliases that start with the prefix “left”, regardless of whether we are talking about the Primary SMTP address or secondary aliases. On the other hand, any proxy addresses that contain the “left” as part of the name might still be considered valid, so it’s desirable to only strip those that have it at the beginning of the address string.

Now, there are different approaches to this task, but essentially it all boils down to basic string manipulations. The method I chose was to do a quick filter using the Where-Object clause and the -notlike statement:

$aliases = $mailbox.EmailAddresses | ? {$_.AddressString -notlike 'left*'}

Applying this to the above example will strip the smtp:left@domain.com and smtp:leftSAM.something@domain.com entries, with those four remaining:

SMTP:firstname.lastname@domain.com
smtp:SAM.blabla@domain.com
smtp:notleft@domain.com
smtp:notleft2@domain.com

Now that the “left” aliases are stripped, we can just feed the remaining ones to the Set-Mailbox cmdlets and be done with it. Easy enough, right? Well, if you want a more robust solution, you have to cover some corner cases. What if the above filter removed the Primary SMTP address? Or what if it removed ALL aliases? You will of course need at least one valid proxy address in order to reconfigure the mailbox, so what kind of solution can we code to account for this?

For the first corner case, we can select one of the remaining proxy addresses and promote it to Primary SMTP Address. My regex skills are still mediocre so there is probably a better way to do this, but the following code seems to work just fine:

$emailaddresses = ($aliases.ProxyAddressString -join "," -replace "smtp:(?!.*smtp:)","SMTP:") -split ","

In a nutshell, the above converts the array of aliases to string of comma-separated proxy addresses, applies a regex to find the last instance of (lowercase) “smtp:” and replaces it with uppercase “SMTP:”, then converts back to array.

The other corner case to handle is when all aliases were removed, so we craft a new one using the below code sample:

$emailaddresses = $("SMTP:" + $mailbox.Alias + "@" + (Get-AcceptedDomain | ? {$_.Default -eq $true}).Name)

The new Primary SMTP address will consist of the Alias/MailNickname attribute of the user, the at sign and the default accepted domain for the company.

Combining all of the above into a single piece of code:

$mailbox = Get-Mailbox 'left' | Select-Object Alias, Emailaddresses

$aliases = $mailbox | select -ExpandProperty EmailAddresses | ? {$_.AddressString -notlike 'left*'}
if ($aliases | ? {$_ -cmatch "SMTP:"}) {
    $emailaddresses = $aliases.ProxyAddressString
    }
elseif (!$aliases) { $emailaddresses = $("SMTP:" + $mailbox.Alias + "@" + (Get-AcceptedDomain | ? {$_.Default -eq $true}).Name) }
else {
    $emailaddresses = ($aliases.ProxyAddressString -join "," -replace "smtp:(?!.*smtp:)","SMTP:") -split ","
    }

Set-Mailbox 'left' -EmailAddresses $emailaddresses

Now, you might as well want to execute this against in bulk against a list of mailboxes. It is indeed possible, with just some minor modifications:

$mailboxes = Get-Mailbox | Select-Object Alias,ExchangeGuid,Emailaddresses
foreach ($mailbox in $mailboxes) {

    $aliases = $mailbox | select -ExpandProperty EmailAddresses | ? {$_.AddressString -notlike 'left*'}
    if ($aliases.AddressString.Count -eq $mailbox.EmailAddresses.Count) {continue}
     
    if ($aliases | ? {$_ -cmatch "SMTP:"}) { 
        $emailaddresses = $aliases.ProxyAddressString
        }
    elseif (!$aliases) { $emailaddresses = $("SMTP:" + $mailbox.Alias + "@" + (Get-AcceptedDomain | ? {$_.Default -eq $true}).Name) }
    else {
        $emailaddresses = ($aliases.ProxyAddressString -join "," -replace "smtp:(?!.*smtp:)","SMTP:") -split ","
    }
    Set-Mailbox $mailbox.ExchangeGuid.ToString() -EmailAddresses $emailaddresses
}

The code above expects that the full Deserialized.Microsoft.Exchange.Data.SmtpProxyAddress object is exposed, as in the cmdlets are executed against the EMS if using on-premises Exchange or Implicit session if using Exchange Online. If you are using PowerShell remoting instead, some minor modifications will be needed:

$mailboxes = Get-Mailbox | Select-Object Alias,ExchangeGuid,Emailaddresses
foreach ($mailbox in $mailboxes) {

    $aliases = $mailbox | select -ExpandProperty EmailAddresses | ? {$_.Split(":")[1] -notlike 'left*'}
    if ($aliases.Count -eq $mailbox.EmailAddresses.Count) {continue}

    if ($aliases | ? {$_ -cmatch "SMTP:"}) {
        $emailaddresses = $aliases
    }
    elseif (!$aliases) { $emailaddresses = $("SMTP:" + $mailbox.Alias + "@" + (Get-AcceptedDomain | ? {$_.Default -eq $true}).Name)  }
    else {
        $emailaddresses = ($aliases -join "," -replace "smtp:(?!.*smtp:)","SMTP:") -split ","
    }
    Set-Mailbox $mailbox.ExchangeGuid.ToString() -EmailAddresses $emailaddresses
}

Additional details are available in the TechNet forums thread: Update Email addresses in Exchange for a user

2 thoughts on “Bulk replacing proxy addresses based on a pattern

  1. Joe Sutherland says:

    Instead of using $_.AddressString for EMS sessions and $_ for PowerShell remoting sessions (and, thus, having to maintain two different versions of the code), you can just use $_.toString() for both cases. The toString method returns the same value for a member of the EmailAddresses collection in both types of sessions, so you can write code that’s reliable in either use case.

    Reply
    1. Vasil Michev says:

      Thanks Joe, noted. I usually prefer to use the EMS version, as it keeps the full object, but I often run into validation errors when using it against remoting session, and only then remember to put a ToString() on it 🙂

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.