Posted in:

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("mark@soundcode.org");

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.

Want to learn more about how easy it is to get up and running with Azure Functions? Be sure to check out my Pluralsight courses Azure Functions Fundamentals and Microsoft Azure Developer: Create Serverless Functions

Comments

Comment by Chandrashekar Reddy D

Hi Mark,
In 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.

Chandrashekar Reddy D
Comment by Parag

Hey Mark,
Can 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.

Parag
Comment by Mark Heath

Yes use the IAsyncCollector<t> binding instead of the out parameter, and that allows you to send zero or more messages.

Mark Heath
Comment by Parag

Thanks Mark,
Also 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.

Parag
Comment by marcus mendoza

Hi Mark ,
Can 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

marcus mendoza
Comment by Mark Heath

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