ECS: A Pay-As-You-Go Email Transmission Service
Microsoft is very clear that using basic authentication to connect to Exchange Online via SMTP AUTH, the protocol used by apps and devices to connect and send email via Exchange Online, is going away. It’s part of their longstanding campaign to remove basic authentication from Exchange Online and have every connection serviced by modern authentication. Other changes announced at the same time limit the ability to abuse email transmission via Exchange Online including reducing the number of messages individual mailboxes can send to external recipients daily.
The High-Volume Email (HVE) service (now in preview) gives tenants the ability to send up to 100,000 messages to internal recipients daily. The need to send large volumes of email, like marketing communications, statements, and so on, to external recipients is among the reasons why organizations keep on-premises Exchange servers, and Microsoft wants to remove those reasons and have customers use cloud services for all their email requirements. However, the preview of HVE limits external communications to 2,000 recipients daily, so HVE is a service primarily aimed at internal communications.
If you want a production-ready email service designed for external mailings, Microsoft’s answer is Azure Communication Services (ACS), or more specifically the Email Communication Service (ECS) component of ACS. Generally available since April 2023, ECS is a Pay As You Go metered service funded through an Azure subscription, much like other on-demand services like Microsoft 365 Backup and SharePoint Translation.
Like HVE, ECS is built on top of Exchange Online, but while Exchange Online is all about dealing with end-user messaging, ECS focuses on email generated by applications. Unlike HVE, ECS requires modern authentication for client connections. ACS does support sending SMTP messages using basic authentication. I don’t cover that topic here.
This article describes how to configure ECS to send email via PowerShell using a vanity domain linked to a Microsoft 365 tenant.
Outline of the Solution
The basic parts of the solution and the order in which they are configured or created are:
- An Azure subscription linked to a valid credit card. The account used during this exercise must have administrative control over the subscription.
- An ECS resource.
- A vanity domain. I used the office365itpros.com domain, which is registered to my Microsoft 365 tenant. I didn’t try using a Microsoft 365 service domain (like office365itpros.onmicrosoft.com), and I didn’t try using the free Azure email domain that can be configured for an ECS resource (these domains have lower sending limits). The vanity domain must be configured for SPF and DKIM. If the domain is used with Microsoft 365, it is probably set up for SPF and DKIM (for Exchange Online). You will have to add DNS records for the domain for the DKIM and DKIM2 selectors for Azure. Validating that the domain is configured correctly is part of the process of linking the domain to the ECS resource.
- An ACS resource linked to the ECS resource. Both resources must be located in the same Azure datacenter region. Currently, the Email communication service is only available in the United States, so that’s where both resources must be (Figure 1). [Update: Microsoft reassures me that ECS is available in other datacenter regions. I’m willing to accept their assurances with the caveat that I know that the U.S. region works!]
- An Entra ID registered app for authentication (using the application identifier, tenant identifier, and an application secret).
- An ACS role assignment for the Entra ID app to allow it to submit messages. The process to add an assignment is described here.
- A PowerShell script to create and send messages using the Email Send API.
I don’t intend to take up space here by repeating the steps described in the Microsoft documentation. However, I suggest that you take some time to read and absorb the information presented in the documentation, such as:
Create and manage Email Communication Service.
Connect a verified email domain.
Send an email (including the Try Email test feature).
In addition, decide on points like what Azure subscription to use before starting. It takes a little time to line everything up and you might need to go through the process a couple of times before everything works (I did), but persistence pays!
Preparing to Send Email
Once the Azure resources are configured and a domain is connected, we can send some email. In a Microsoft 365 environment, it’s likely that the code to send bulk email is written in PowerShell and might have been inherited from on-premises Exchange. It’s still possible to send some bulk email using Exchange Online but Microsoft doesn’t want tenants to use Exchange Online for that purpose, which is why they’ve introduced the new external recipient rate (ERR) limit to restrict mailboxes from sending email to more than 2,000 external recipients daily.
The good news is that the skills and experience used to send email via Exchange Online, especially with the Graph APIs, create a great foundation for working with ECS. The basic steps needed are:
- Define variables to hold the app id for the Entra ID registered app created to communicate with ECS, the app secret known to the app, and the tenant id. These variables are used to obtain an access token from the Azure communication services endpoint (very similar to obtaining an access token to work with Graph APIs). Like any other access token, it expires after an hour or so and must be renewed if necessary to send more messages after the time when the access token is first obtained.
- Define the sender address to use. ECS doesn’t have user mailboxes of the type found in Exchange Online. The address meets the SMTP requirements to submit messages. By default, an ECS resource is configured with an email address of DoNotReply@domain. It’s possible to add additional email addresses for senders. It’s also possible to set a real email address as the reply-to address to allow recipients to respond to messages. I used the address of a shared mailbox for this purpose.
- Define a variable to hold the ECS resource endpoint. The endpoint will be something like “office365itpros.unitedstates.communication.azure.com” (Figure 2 shows the endpoint displayed in the ACS resource properties). The endpoint is used to construct the URI to send messages, which looks like:
https://office365itpros.unitedstates.communication.azure.com/emails:send?api-version=2023-03-31
- Create the HTML content for the message body. I tried several ways to create HTML like saving Word documents as web pages, but ECS rejected anything except simple HTML (basic formatting like bold and underline, different headings, and hyperlinks). I eventually ended up handcrafting the HTML.
- Build an email message structure including elements like the TO, CC, and BCC recipients, message body, sender address, and reply to address. This process is like creating the structure used to submit email to Exchange Online. Some differences exist, so pay attention to the documentation for the Email Send API (or use the code in my example script as a template). The structure is defined in a set of arrays and hash tables that are converted to create a JSON-format payload.
- Submit a Post request to the ECS API to submit the message payload to ECS for processing. For a bulk email operation, it’s likely that the script will read in recipient addresses from some data source and loop through the set of addresses to send a message to each. I used a CSV file as the data source for recipient email addresses.
- Optionally, check if the message sends succeeded. A final status for a message should be retrievable between 5-10 seconds post submission.
The example script I used for testing can be downloaded from GitHub. Remember to update the variables to the correct values for your ECS resource and Entra ID app.
Throttling and Rate Limits
ECS requires the email traffic generated applications to remain within service limits. This means that an application cannot send more than 100 messages per hour (or 30 within a minute) when using a vanity domain. The limits are lower for Azure-managed domains. Staying within the limits often requires judicious use of the Start-Sleep cmdlet. I usually pause for two seconds between each message.
The limits apply per subscription and seem quite low when compared to Microsoft’s assertions that the ACS platform “enables high-volume notifications.” One way around the limitation is to add multiple BCC recipients to messages. This isn’t a good approach if you want to personalize messages, but it’s OK when sending out general-purpose communications. Another service limit restricts messages to 50 recipients. It’s possible to file a request with Azure support to seek an increase in the service limits if you have a good justification. I asked them to increase the limit to 2,000 messages per hour, which doesn’t seem unreasonable. Nothing happened as my request foundered in the byzantine web of Microsoft support.
Checking the Result of Email Submission
The monitoring section of the ACS resource includes an Email insights page to help administrators discover what happened to messages sent using ECS. Figure 3 shows a snapshot of ECS activity over a 7-day period.
An aspect of bulk email is the potential for a relatively high percentage of submissions to fail. People might have moved jobs, or stopped using a mailbox, or changed their email address, or their mailbox is full. My experiments with ECS covered several failure conditions, both expected and unexpected.
For instance, I used a bunch of contoso.com addresses to see what happened (they failed!). In addition, I tried to send to Exchange Online mailboxes with delivery restrictions imposed by transport rules because the mailboxes belong to administrator accounts that I don’t want receiving email from external sources. The email performance tab of ECS insights shows where errors occur in sending (Figure 4). However, there’s no way to download this information for analysis, nor does ECS give any indication of why a message failed (such as “email address not found”). Don’t expect to see the kind of detailed non-delivery reports provided by Exchange Online.
The Cost of Using ECS
Pricing examples and a calculator for ECS are available online. I don’t think costs will be much of a problem. I managed to accumulate a bill of $0.70 to send just over 4,000 messages in a week (Figure 5).
Low Sending Rate is the Achilles Heel for ECS
The rate limits imposed by Microsoft on ECS are the biggest issue I encountered. To me, the default limits prevent ECS being a compelling offering. Most organizations have mailing lists containing at least several thousand external addresses. Given that you can paste hundreds of external addresses as BCC recipients (to preserve recipient privacy) into an Outlook message and send it, the ECS limit of 100 messages per hour is inexplicable. It’s easy to get around Exchange Online’s external recipient limit by spreading mail traffic across multiple mailboxes.
Given what I experienced with ECS, I don’t think this is an attractive service for Exchange customers. HVE (when generally available) is a better option. But if you want an email service that isn’t connected to Microsoft 365 and is built to handle external communications, ECS might do a job for you. However, if Microsoft wants ECS to be more attractive, ECS needs to handle higher loads, have better management interfaces, and be easier to use.
Hi Tony,
You’re previous post on about HVE you said that this feature is really meant for internal emails, while this post states the opposite. Is this different now?
https://practical365.com/exchange-online-hve/
Hmmm… I wouldn’t say that. The H2 for the HVE article says: “HVE SMTP-based Messaging Designed for Internal Communications (and Some External Email)” and this article says “However, the preview of HVE limits external communications to 2,000 recipients daily, so HVE is a service primarily aimed at internal communications.” That seems a consistent position.
The conclusion also says “But if you want an email service that isn’t connected to Microsoft 365 and is built to handle external communications, ECS might do a job for you.”
ECS for external
HVE for internal
But both can handle the other traffic (within limits)
I did all steps and it did work