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)
           { 
           try 
             { 
             write-host "trying $($addr)"; 
             $to = New-Object Net.Mail.MailAddress($addr.SubString(5))
             $smtptested++
             } 
           catch [system.exception] 
             { 
             write-host -ForegroundColor Red "$($addr) is bad"; 
             $data = """" + $Alias + """" + "," + $addr + """"; 
             $data | out-file badsmtp.csv -append 
             $bad++
             } 
           finally 
             {
             }
           } # End If ($addr)
           $proxies++
         } # End Inner ForEach
         $objects++
       } # 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!