Skip to main content

EWS Basics : Sending Messages using EWS

One of the most basic things you might want to do with EWS is Send a Message, while its may sound easy sending messages programmatically can be at times confusing.

But first lets take one step back for a second from EWS and look at the different ways you could send Email programmatically on Exchange.

SMTP : (Simple Mail Transfer Protocol) SMTP is the backbone protocol for Email and is how most Email is transferred between servers on the Internet. Its also the protocol POP and IMAP clients use to send email. Because SMTP is a protocol (meaning its a set of rules defined in a RFC) rather then an API to use it you need to use a library that will give you some objects that you can code against that will then generated the necessary communication that follows the protocol rules. Some example of these are CDOSys, and System.NET.Mail. From an Exchange technical point of view when you submit a message via SMTP your submitting it directly into the Transport Pipeline so it doesn't going through Mailbox Store.

Pickup and Replay Directories : These are special directories on the Transport server that you can place Messages into https://technet.microsoft.com/en-us/library/bb124230(v=exchg.150).aspx typically used by specialized applications like foreign connectors https://technet.microsoft.com/en-us/library/aa996779(v=exchg.150).aspx

Mailbox API's : These are the specific API's that Microsoft has made available to access a Mailbox which are MAPI, EWS Managed API and for Office365 the new REST api. (ActiveSync could also be used but is a more specialized protocol) . When you send a Message via one of these Mailbox API's you first have to talk to the Mailbox role server (via the CAS ) and the message is submitted to Exchange Store which will then send it through into the transport pipeline. As part of that process you may choose to save a copy of the message your sending into the sentItems folder of the user who is sending the message. As you are sending via the Store you can also assert the SendOnBehalf rights if you send a message on behalf of a delegate.

EWS : To Send a Message using EWS you use the SendItem operation https://msdn.microsoft.com/en-us/library/office/aa580238(v=exchg.150).aspx you can also use the CreateItem Operation and set the SendDisposition to Send or SendAndSaveCopy. Depending on if your sending a message with Attachments you may need to make multiple requests to the server to create a draft message and then add attachments. In the EWS Managed API this complexity is implemented in the API so you don't have to worry about it. eg the following is a basic function for sending a message using EWS in PowerShell.

function Send-EWSMessage  {
     param( 
             [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
  [Parameter(Mandatory=$true)] [System.Management.Automation.PSCredential]$Credentials,
  [Parameter(Position=2, Mandatory=$false)] [switch]$useImpersonation,
  [Parameter(Position=3, Mandatory=$false)] [string]$url,
  [Parameter(Position=6, Mandatory=$true)] [String]$To,
  [Parameter(Position=7, Mandatory=$true)] [String]$Subject,
  [Parameter(Position=8, Mandatory=$true)] [String]$Body,
  [Parameter(Position=9, Mandatory=$false)] [String]$Attachment
    )  
  Begin
 {
  if($url){
   $service = Connect-Exchange -MailboxName $MailboxName -Credentials $Credentials -url $url 
  }
  else{
   $service = Connect-Exchange -MailboxName $MailboxName -Credentials $Credentials
  }
  if($useImpersonation.IsPresent){
   $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName) 
  }
  $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems,$MailboxName)   
  $SentItems = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
  $EmailMessage = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service  
  $EmailMessage.Subject = $Subject
  #Add Recipients    
  $EmailMessage.ToRecipients.Add($To)  
  $EmailMessage.Body = New-Object Microsoft.Exchange.WebServices.Data.MessageBody  
  $EmailMessage.Body.BodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::HTML  
  $EmailMessage.Body.Text = "Body"  
  $EmailMessage.From = $MailboxName
  if($Attachment)
  {   
   $EmailMessage.Attachments.AddFileAttachment($Attachment)
  }
  $EmailMessage.SendAndSaveCopy($SentItems.Id) 
  
 }
}

One other way of sending a Message in EWS is you can use the MIMEContent that maybe generated by another library or maybe a message that was exported or saved eg

function Send-MimeMessage {
     param( 
     [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
  [Parameter(Mandatory=$true)] [System.Management.Automation.PSCredential]$Credentials,
  [Parameter(Position=2, Mandatory=$false)] [switch]$useImpersonation,
  [Parameter(Position=3, Mandatory=$false)] [String]$MimeMessage
    )  
  Begin
 {
  if($url){
   $service = Connect-Exchange -MailboxName $MailboxName -Credentials $Credentials -url $url 
  }
  else{
   $service = Connect-Exchange -MailboxName $MailboxName -Credentials $Credentials
  }
  if($useImpersonation.IsPresent){
   $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName) 
  }
  $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems,$MailboxName)   
  $EmailMessage = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service
  [byte[]]$bdBinaryData1 = [System.IO.File]::ReadAllBytes($MimeMessage)  
  $EmailMessage.MimeContent = new-object Microsoft.Exchange.WebServices.Data.MimeContent("UTF-8", $bdBinaryData1)
  $EmailMessage.SendAndSaveCopy($SentItems.Id) 
 }
 
}

Both of these functions come from the following github script https://github.com/gscales/Powershell-Scripts/blob/master/EWSSend.ps1

Sending Options : To Send a Message as another user (eg other then the current user being used to authenticate) you need to have been granted the SendAs permission on a Mailbox (which is a separate permission from the Mailbox rights granted via Add-MailboxPermission). The other option in EWS is that you can use EWS impersonation which would give the caller the same rights as the Mailbox owner,  the script I've posted on GitHub has an option to use EWS Impersonation.

Both of these example functions save a copy of the message being sent into the SentItemFolder of the Mailbox passed into the Function. This is controlled via the

$EmailMessage.SendAndSaveCopy($SentItems.Id)

line if you didn't want to save a copy to the SentItem folder you can use the Send Method instead eg

$EmailMessage.Send()

This will set the necessary MessageDispostion value in the SOAP request.

EWS only allows you to send using one body format eg you need to choose between HTML or Text if you do want to use the more advanced body types allowed in MIME look to use the MIME Content option to send a message. There is also an example of sending an encrypted message using the MIME content  https://blogs.msdn.microsoft.com/emeamsgdev/2015/08/10/ews-how-to-send-signed-email-using-the-ews-managed-api/

Using VBA or VB6 : If your still enjoying the retro Programing languages like VBA or VB6 its still possible to use EWS to send a message from your code by manually constructing the SOAP message involved. This maybe helpfully where your enviorment has been migrated to the cloud and your CDOsys code that was using  SMTP to send messages no longer works. As a work around you can use a simple function like this to do a Send using EWS in Office365 (or change the URL to you OnPrem EWS endpoint).

Sub SendMessage(Subject As String, Recipient As String, Body As String, User As String, Password As String)
   Dim sReq As String
   Dim xmlMethod As String
   Dim XMLreq As New MSXML2.XMLHTTP60
   Dim EWSEndPoint As String
   EWSEndPoint = "https://outlook.office365.com/EWS/Exchange.asmx"
   sReq = "<?xml version=""1.0"" encoding=""UTF-8""?>" & vbCrLf
   sReq = sReq & "<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:t=""http://schemas.microsoft.com/exchange/services/2006/types"">" & vbCrLf
   sReq = sReq & "<soap:Header>" & vbCrLf
   sReq = sReq & "<t:RequestServerVersion Version=""Exchange2010""/>" & vbCrLf
   sReq = sReq & "</soap:Header>" & vbCrLf
   sReq = sReq & "<soap:Body>" & vbCrLf
   sReq = sReq & "<CreateItem MessageDisposition=""SendAndSaveCopy"" xmlns=""http://schemas.microsoft.com/exchange/services/2006/messages"">" & vbCrLf
   sReq = sReq & "<SavedItemFolderId>" & vbCrLf
   sReq = sReq & "<t:DistinguishedFolderId Id=""sentitems"" />" & vbCrLf
   sReq = sReq & "</SavedItemFolderId>" & vbCrLf
   sReq = sReq & "<Items>" & vbCrLf
   sReq = sReq & "<t:Message>" & vbCrLf
   sReq = sReq & "<t:ItemClass>IPM.Note</t:ItemClass>" & vbCrLf
   sReq = sReq & "<t:Subject>" & Subject & "</t:Subject>" & vbCrLf
   sReq = sReq & "<t:Body BodyType=""Text"">" & Body & "</t:Body>" & vbCrLf
   sReq = sReq & "<t:ToRecipients>" & vbCrLf
   sReq = sReq & "  <t:Mailbox>" & vbCrLf
   sReq = sReq & "       <t:EmailAddress>" & Recipient & "</t:EmailAddress>" & vbCrLf
   sReq = sReq & "  </t:Mailbox>" & vbCrLf
   sReq = sReq & "</t:ToRecipients>" & vbCrLf
   sReq = sReq & "</t:Message>" & vbCrLf
   sReq = sReq & "</Items>" & vbCrLf
   sReq = sReq & "</CreateItem>" & vbCrLf
   sReq = sReq & "</soap:Body>" & vbCrLf
   sReq = sReq & "</soap:Envelope>" & vbCrLf
   xmlMethod = "POST"
   XMLreq.Open xmlMethod, EWSEndPoint, False, User, Password
   XMLreq.setRequestHeader "Content-Type", "text/xml; charset=""UTF-8"""
   XMLreq.setRequestHeader "Translate", "F"
   XMLreq.setRequestHeader "User-Agent", "VBAEWSSender"
   XMLreq.send sReq
   If XMLreq.Status = 200 Then
        ' Message Sent okay
    Else
        ' Something went Wrong
   End If
End Sub

Popular posts from this blog

Testing and Sending email via SMTP using Opportunistic TLS and oAuth in Office365 with PowerShell

As well as EWS and Remote PowerShell (RPS) other mail protocols POP3, IMAP and SMTP have had OAuth authentication enabled in Exchange Online (Official announcement here ). A while ago I created  this script that used Opportunistic TLS to perform a Telnet style test against a SMTP server using SMTP AUTH. Now that oAuth authentication has been enabled in office365 I've updated this script to be able to use oAuth instead of SMTP Auth to test against Office365. I've also included a function to actually send a Message. Token Acquisition  To Send a Mail using oAuth you first need to get an Access token from Azure AD there are plenty of ways of doing this in PowerShell. You could use a library like MSAL or ADAL (just google your favoured method) or use a library less approach which I've included with this script . Whatever way you do this you need to make sure that your application registration  https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-

The MailboxConcurrency limit and using Batching in the Microsoft Graph API

If your getting an error such as Application is over its MailboxConcurrency limit while using the Microsoft Graph API this post may help you understand why. Background   The Mailbox  concurrency limit when your using the Graph API is 4 as per https://docs.microsoft.com/en-us/graph/throttling#outlook-service-limits . This is evaluated for each app ID and mailbox combination so this means you can have different apps running under the same credentials and the poor behavior of one won't cause the other to be throttled. If you compared that to EWS you could have up to 27 concurrent connections but they are shared across all apps on a first come first served basis. Batching Batching in the Graph API is a way of combining multiple requests into a single HTTP request. Batching in the Exchange Mail API's EWS and MAPI has been around for a long time and its common, for email Apps to process large numbers of smaller items for a variety of reasons.  Batching in the Graph is limited to a m

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia  https://en.wikipedia.org/wiki/Opportunistic_TLS .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why I wrote th
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.