Testing for bad SMTP Addresses

While working on my latest project,  I encountered a significant number of objects with malformed SMTP addresses.  These appeared to have been objects that had been somehow manually modified over the years by directly writing to the proxyAddresses attribute in Active Directory, bypassing an API that would check for correctly-formatted addresses.

As part of a migration effort, I needed to identify these bad addresses that would prevent my customer from successfully migrating to Office 365.  In an AD environment with a few hundred thousand objects (each of which could have anywhere from 1 to 50 or 60 proxy addresses), where would I even start?

Here’s what I did (and maybe it can be helpful to you).

  1. Get a list of objects.  The core attributes that we’re looking for are Alias (because these are all Exchange mail-enabled objects, they had an alias), PrimarySmtpAddress (for UserMailboxes and distribution groups), and EmailAddresses (the Exchange name for the proxyAddresses array).When you export a multivalued attribute (such as EmailAddresses/proxyAddresses), you need to iterate through all of the values and capture them as a single string that can be written to a column.  On top of that, you need to tell it how to delimit the fields in the column (since X500 addresses can have commas, using a comma as a delimiter in this column will cause you much grief, should you decide to do other things with this CSV).  To do this:
    Get-Recipient -ResultSize Unlimited | Select Alias,PrimarySmtpAddress,@{N="EmailAddresses";E={$_.EmailAddresses -join "|"}} | Export-Csv -NoType AllObjects.csv

    While you can certainly just do a Get-Recipient into a variable, I find that I like to have an tangible file to work with in case I lose network connectivity, power, or something else interrupts my processing.

    Also, when specifying the character to use for the join separator, don’t use a comma (since it’s part of an x500 address) or a semi-colon (since it’s part of an x400 address). I find that the pipe (|) is the best separator to use when dealing with the proxyAddresses array.

  2. Import the CSV into a variable.
    $AllObjs = Import-Csv AllObjects.csv
  3. Loop through the objects, writing the bad ones out to a file for review.
    $smtptested = 0
    $proxies = 0
    $bad = 0
    $objects = 0
    $head = """" + "Alias" + """" + "," + """" + "BadSMTPAddress" + """"
    $head | out-file badsmtp.csv
    $regex = ((?i)x500:|x400:|gwise:|notes:|ms:|sip:|\=)
    foreach ($obj in $AllObjs) 
       [array]$EmailAddresses = $obj.EmailAddresses.Split("|");
       $alias = $obj.Alias; 
       foreach ($addr in $EmailAddresses) 
         if ($addr -notmatch $regex)
             write-host "trying $($addr)"; 
             $to = New-Object Net.Mail.MailAddress($addr.SubString(5))
           catch [system.exception] 
             write-host -ForegroundColor Red "$($addr) is bad"; 
             $data = """" + $Alias + """" + "," + $addr + """"; 
             $data | out-file badsmtp.csv -append 
           } # End If ($addr)
         } # End Inner ForEach
       } # End Outer Foreach
    Write-Host "$($objects) objects containing addresses tested."
    Write-Host "$($proxies) proxy addresses tested."
    Write-Host "$($smtptested) smtp addresses tested."
    Write-Host "$($bad) bad smtp addresses found."

That’s it!

The output file, badsmtp.csv, will have have two columns: the Exchange alias of the affected object, and the offending SMTP entry in that object’s EmailAddresses/proxyAddresses array.

Happy hunting!

Comments (10)

  1. Thanks
    much appreciated

  2. Aaron,could you explain what this line is doing:

    $to = New-Object Net.Mail.MailAddress($addr.SubString(5))

    and specifically why the number 5?
    Thanks in advance

    1. That line is taking the address value in $addr (which is the current value in the pipeline) and trying to use the Net.Mail.MailAddress .net library against it.

      You can actually open up a PowerShell window and type:

      $to=New-Object Net.Mail.MailAddress(“john@contoso.com”) and then try

      $to=New-Object Net.Mail.MailAddress(“.john@contoso.com”)

      You should get an error with the second one because it is not a well-formed SMTP address.

      The substring(5) is stripping off the first 5 characters in the proxyAddress pipeline object. If you look in the AD attribute editor, you’ll see that values look like:


      The first part of the script makes evaluates if the string starts with SMTP (if ($addr -match “smtp”)) and then tests matching strings against the Net.Mail.MailAddress library.

      1. Thanks a lot
        i forgot that there is that SMTP: which make sense of that 5 characters strip
        great work.
        btw, its always interesting to read this stuff and helpful, even if in part, in other issues even if they are not exact 1:1 the same as your issues:)

  3. Hi Aaron,
    i was wondering(i will test that too:) when i have few minutes)but wouldnt idfix identify these?

    1. In this particular environment, I don’t have the ability to run IDFix against these objects. They are being synced into a BPOS-D tenant and written directly without any API checking. In my instance, I also have a lot of objects with an unprintable characters that this catches.

      1. Thanks for getting back to me:)
        Now i get it
        I’m wondering if there is a character “flaw” that idfix wont catch and this will

        1. I’m guessing Bill probably uses Net.Mail.MailAddress or something that performs a similar function in IDFix; I didn’t have the ability to run IDFix in the source environment, since it’s in Office 365 Dedicated, so I had to deal with whatever results were given to me. 🙂

  4. Naytaris says:

    Great work – thanks

Skip to main content