Monday, July 31, 2017

PowerShell and Exchange Web Services (EWS) script to parse bounced emails

In my previous post, I had shared how to run PowerShell scripts on a locally configured Outlook profile, with Windows 10 powershell.

In this post, we will be making use of EWS to connect to an exchange server, impersonate a system maibox, download the emails and parsing the email body to extract the bounced email recipient.

References:
https://goodworkaround.com/2015/01/29/powershell-and-ews-managed-api/
https://msdn.microsoft.com/en-us/library/office/jj900168(v=exchg.150).aspx
https://www.linkedin.com/pulse/how-use-date-range-search-query-ews-managed-api-using-sunil-chauhan

What you need:
Microsoft Exchange Web Services API (download from nuget)

If you do not know which version of Exchange server your outlook profile is hosted on, refer to this article:

The script will attempt to detect a pre-embedded header. I find that this is the easiest way of detection since I have control over the email sending mechanism as well.
Example: in my email sending script (vbs sample here), I embed a header field
With Flds
' Set custom header so that the email can be easily read from bounce back addresses
.Item("urn:schemas:mailheader:x-CustomHdr") = strRecipientEmail
.Update
End With

Full source code:

Friday, July 28, 2017

PowerShell script (client machine) to parse bounced emails from Outlook

PowerShell script tested on Windows 10, with Outlook 2016 installed.
This is only meant for machines with Outlook connected to at least one email.

The script will attempt to parse bounced emails based on certain text patterns and attempt to extract the recipient email from a custom header. Assuming the custom header is set during send time.
Emails are extracted and saved to a SQL server database by invoking a stored procedure.

 Clear-host  
 Add-Type -Assembly "Microsoft.Office.Interop.Outlook"  
 $Outlook = New-Object -ComObject Outlook.Application  
 $Namespace = $Outlook.GetNameSpace("MAPI")  
 $mb = $Namespace.Folders | ?{$_.name -match "testabc@test.com"}    
 $Inbox = $mb.Folders | ?{$_.Name -match "Inbox"}    
 
# Clear deleted items > 30 days  
 $cutOff = (get-date).AddDays(-30)  
 $DeletedItems = $mb.Folders | ? { $_.Name -match 'Deleted Items' };  
 Foreach($e in $DeletedItems.Items){  
   if ($item.SentOn -gt $cutOff) {  
     $e.delete()  
   }  
 }  
 
# A bug in Outlook 2013 and 2016 will not display bounced emails properly  
 $uEnc = New-Object System.Text.UnicodeEncoding  
 $enc = [system.Text.Encoding]::UTF8  
 $dataSource = "dbservername"  
 $database = "dbname"  
 $dbUser = "user"  
 $pwd = "pwd"  
 $connectionString = "Data Source=$dataSource; User Id=$dbUser; Password=$pwd; Initial Catalog=$database"  
 $connection = new-object system.data.SqlClient.SQLConnection($connectionString)  
 $connection.Open()  
 #---------------------------------------------------------------------------------  
 # Function to pass in SQL query and return results as a table  
 #---------------------------------------------------------------------------------  
 function Invoke-SQL {  
   param(  
     [string] $sqlCommand = $(throw "Please specify a query.")  
    )  
   $command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)  
      if ($command.ExecuteNonQuery() -ne 1)  
      {  
           Write-Host "Failed to execute SQL: $sqlCommand";  
      }  
 }  
 function Update-InvalidEmail  
 {  
   param(  
     [string] $email,  
     [string] $subject  
   )  
   If ($email.length -gt 100)  
   {  
     $email = $email.Substring(0,100)  
   }   
   If ($subject.length -gt 500)  
   {  
     $subject = $subject.Substring(0,500)  
   }   
      $email = $email.Replace("'", "''")  
   $subject = $subject.Replace("'", "''")  
      $strSQL = "sp_UpdateInvalidEmails '$email', '$subject'"  
   $dt = Invoke-SQL -sqlCommand $strSQL -returnResults $false  
   $dt  
 }  
 #checks newest messages  
 $Inbox.items | select -first 100 | foreach {  
   $subject = $_.Subject  
   $body = $enc.GetString($uEnc.GetBytes($_.Body))  
   if ($body -like "*Delivery has failed to these recipients*") {  
     write-host "failed delivery detected" -ForegroundColor Cyan  
     # look for lines like this: x-CustomHdr: email@test.com  
     # x-CustomHdr is a custom header 
     $regex = '\sx-CustomHdr:\s*([\w_.+-]+@[\w-]+\.[\w-.]+)\s'  
     $found = $body -match $regex  
     if ($found) {  
       $emailAddr = $matches[1]  
       write-host $emailAddr  
       Update-InvalidEmail -email $emailAddr -subject $subject  
     }  
   }  
   $_.Delete()  
 }  
 $connection.Close()  
 $connection.Dispose()  
Related Posts Plugin for WordPress, Blogger...