Sunday, January 4, 2026

Sending Emails using Azure Communication Services in .NET

In this post, let's see how to send emails using Azure Communication Services (ACS) in a .NET application. With SendGrid's free tier being discontinued, ACS has become an attractive alternative for sending transactional emails from Azure-hosted applications.

First, you need to create two resources in Azure:
  1. Email Communication Services - This handles email domain configuration
  2. Communication Services - This is the main resource your application connects to
Creating Email Communication Services

In the Azure Portal, search for Email Communication Services and create a new resource. Once created, you need to add a domain. You have two options:
  1. Azure Managed Domain: Quick setup, gives you a subdomain like DoNotReply@xxxxxxxx.azurecomm.net 
  2. Custom Domain: Use your own domain like noreply@yourdomain.com

For a custom domain, you'll need to add following DNS records for verification, Azure will provide the values.

  • TXT: Domain ownership verification
  • TXT: SPF (Sender Policy Framework)
  • CNAME: DKIM key 1
  • CNAME: DKIM key 2

You can do this by clicking on Provision domains -> Custom domain and following the steps.

Email Communication Services: Add Custom Domain

Note: If you have an existing SPF record (e.g. from email forwarding), you need to merge them. You can only have one SPF record per domain:
v=spf1 include:spf.protection.outlook.com include:other.service.com -all
After domain verification, add a MailFrom address under your domain. 
Email Communication Services Domain: MainFrom addresses

Linking Domain to Communication Services

Create a
Communication Services resource, then navigate to Email -> Domains -> Connect domains and link your verified domain.
Communication Services: Link Email Domain
Implementing in .NET

First we need install the following NuGet Package.
dotnet add package Azure.Communication.Email

Add the following to your appsettings.json:

{
  "Email": {
    "Endpoint""https://<your-acs-resource>.communication.azure.com",
    "SenderAddress""<configured_mailfrom_address>"   }
}

We can define an Options Class to map Email settings.

namespace YourApp.Options;

public record Email
{
    public required string Endpoint { getinit; }

    public required string SenderAddress { getinit; }
}

Now let's register the Email options, EmailClient and an EmailService.

// In Program.cs

builder.Services.AddOptions<Email>()
    .Bind(builder.Configuration.GetSection("Email"));

builder.Services.AddSingleton<EmailClient>(sp =>
{
    var emailOptions = sp.GetRequiredService<IOptions<Email>>();
    var credential = new DefaultAzureCredential();

    return new EmailClient(new Uri(emailOptions.Value.Endpoint), credential);
});

builder.Services.AddScoped<EmailService>();

Now let's create a simple EmailService.

using Azure.Communication.Email;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace YourApp.Services;

public class EmailService
{
    private readonly EmailClient _emailClient;
    private readonly Email _emailOptions;
    private readonly ILogger<EmailService> _logger;

    public EmailService(
        EmailClient emailClient,
        IOptions<EmailemailOptions,
        ILogger<EmailServicelogger)
    {
        _emailClient = emailClient;
        _emailOptions = emailOptions.Value;
        _logger = logger;
    }

    public async Task SendEmailAsync(
        string recipientEmail,
        string subject,
        string htmlContent,
        string plainTextContent,
        CancellationToken cancellationToken = default)
    {
        var emailMessage = new EmailMessage(
            senderAddress: _emailOptions.SenderAddress,
            recipientAddressrecipientEmail,
            contentnew EmailContent(subject)
            {
                Html = htmlContent,
                PlainText = plainTextContent
            });

        try
        {
            EmailSendOperation operation = await _emailClient.SendAsync(
                Azure.WaitUntil.Completed,
                emailMessage,
                cancellationToken);

            _logger.LogInformation(
                "Email sent successfully. MessageId: {MessageId}",
                operation.Id);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex"Failed to send email");
            throw;
        }
    }
}

Managed Identity RBAC Role

When using DefaultAzureCredential with an Managed Identity in Production, you must assign the Communication and Email Service Owner RBAC role to your identity on the Communication Services resource.

az role assignment create `
  --assignee <managed-identity-principal-id> `
  --role "Communication and Email Service Owner" `
  --scope /subscriptions/<subscriptionId>/resourceGroups/<resourceGroup>/providers/Microsoft.Communication/CommunicationServices/<acsName>

Once email is sent:

Received Email

Hope this helps.

More read:
   Azure Communication Services Email Overview
   Quickstart: Send Email
   Email Pricing

Happy Coding.

Regards,
Jaliya

No comments:

Post a Comment