Sending Emails with SendGrid and Azure Functions
A while go I blogged about how you could use Azure Functions to handle payment webhooks from a third party payment provider. The example I showed how I could receive a webhook from Paddle for sales for my Skype Voice Changer app, and post a message to a queue.
But that was just the first step in an order processing pipeline. The next step was for that message to cause a license file to get created and stored in blob storage. And then the step after that was to email that license out to the customer. And that can be done quite easily with the Azure Functions SendGrid binding.
Here’s how I set up my license file emailing Azure function.
First of all, every Azure Function needs a trigger. My function was going to be triggered by a license file appearing in blob storage.
So in my function.json
file, the following blobTrigger
binding was set up – looking at the licenses
container in my blob storage connection (whose connection string can be found in the App Setting with the name “MyStorageAccount”) for files with the .lic
extension.
{
"name": "myBlob",
"type": "blobTrigger",
"direction": "in",
"path": "licenses/{filename}.lic",
"connection": "MyStorageAccount"
},
My function also needed to be able to send emails. So I added a SendGrid output binding. This binding can have lots of things configured about it, but I chose only to specify the SendGrid API Key. Note that the key itself doesn’t go in the function.json
file. Instead you put the name of an App Setting that contains the key, just like you do with connection strings. This helps keep secrets from getting stored in source control. To get your own SendGrid API key you’ll need to sign up for a SendGrid account and then you can create a key from your account settings page on their site.
{
"type": "sendGrid",
"name": "message",
"apiKey": "SendGridApiKey",
"direction": "out"
},
My function also needed to know who to send the email to. That information was available in Table Storage, as I’d updated my webhook processing function to also write details of the incoming order into an Azure Table Storage table called orders. So I used a Table Storage input binding to automatically look up the matching row in the table for the license file that had triggered the function. I could do that because the license file name was simply the order number (e.g. 1247518.lic), so I could use {filename}
as the rowKey
for the table storage binding:
{
"type": "table",
"name": "ordersRow",
"tableName": "orders",
"partitionKey": "Orders",
"rowKey": "{filename}",
"take": 50,
"connection": "MyStorageAccount",
"direction": "in"
}
With these bindings in place, I could define my function with a string
parameter to take the contents of the license file in blob storage (which was a text file), an Order
parameter to contain the matching row in Table Storage, and an out Mail
parameter which can be used to send the SendGrid mail. You can also see that we need to reference the SendGrid assembly with a #r
statement, and open the SendGrid.Helpers.Mail
namespace.
#r "SendGrid"
using SendGrid.Helpers.Mail;
public class Order
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string OrderId { get; set;}
public string ProductId { get; set;}
public string Email { get; set;}
public decimal Price { get; set; }
}
public static void Run(string myBlob, string filename,
Order ordersRow, TraceWriter log, out Mail message)
{
...
}
Now we’re ready to send the email. We create a Mail
object, and use the Personalization
class to specify who it is sent to. Adding an attachment requires creating an Attachment
object and using Base 64 encoding to add the contents of the license file. Then we use the Content
class to set the content of the message, which can be HTML if we want. And finally we need to specify a subject and sender. Many of these settings can also be set up in the binding if you don’t want to specify them in code every time.
var email = ordersRow.Email;
log.Info($"Got order from {email}\n License file Name:{filename}");
message = new Mail();
var personalization = new Personalization();
personalization.AddTo(new Email(email));
message.AddPersonalization(personalization);
Attachment attachment = new Attachment();
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(myBlob);
attachment.Content = System.Convert.ToBase64String(plainTextBytes);
attachment.Type = "text/plain";
attachment.Filename = "license.lic";
attachment.Disposition = "attachment";
attachment.ContentId = "License File";
message.AddAttachment(attachment);
var messageContent = new Content("text/html", "Your license file is attached");
message.AddContent(messageContent);
message.Subject = "Thanks for your order";
message.From = new Email("[email protected]");
And that’s all there is to it. A very easy way to send emails, and the table storage binding makes it trivial to lookup the related order information for the license we want to send.
Once again Azure Functions makes it trivially easy to set up backend processing tasks like this for your applications.
Comments
Hi Mark,
Chandrashekar Reddy DIn my scenario I am creating a local .xls excel in the same server in which the function is hosted.
in the path @"D:\Home\site\wwwroot\myFoleder ";
Now I am trying to attach this file and send an email using SendGrid smtp send mail.
below is the error we facing:
"transport failed to connect to the server"
However we able to send the email with an attachment without any issues.
Hey Mark,
ParagCan I send an email based on a condition?
Looks like I always have to send out an email if, I have an out parameter.
Thanks for your help.
-Parag.
Yes use the IAsyncCollector<t> binding instead of the out parameter, and that allows you to send zero or more messages.
Mark HeathThanks Mark,
ParagAlso found another solution, where I can initialize the out paramenter message as null inside the function.
And only assign values for the subject and body, if I need to send the email.
Thanks.
Hi Mark ,
marcus mendozaCan I send email with following scenario.
Scenario – I have a storage account in Azure. What I want is that whenever a text file is added to this storage account in blob storage, I should receive an email with the content of the file.
and how to do it
Thanks and regards
Taha
That's quite easy - just use a BlobTrigger matching *.txt and bind to a string (assuming they are relatively small files), and a SendGrid output binding.
Mark Heath