TIP

💡 Learn more : Azure Functions Documentation.

Where are we?

Full Source Code The source code for the app can be found on GitHub

This blog post is part of a series on how to generate a weekly digest email for a blog using Azure Functions, SendGrid and Azure Storage.

We're trying to build a Email Subscription similar to the following. If you want to catch up, then read the previous posts.

Generating and Sending Emails

In our last post, we left off by creating a frontend that used HTML5, jQuery and some light CSS work. When the user filled out the form and clicked Submit, then it would check to ensure the email is valid and then use an AJAX call to POST the data to our Azure Function that we wrote in part 2. Today, we'll wrap things up by using SendGrid, C# and Azure Functions to send emails every Sunday at 9:30AM.

Use the Azure Functions Template inside of Visual Studio

Return to the project we created earlier and right-click the project and select Add Item and select Azure Functions. Now give it a name such as SendEmail and select Timer Trigger and provide the following schedule 0 30 9 * * SUN.

We'll begin by declaring the feedurl and looping through the feed to collect the last 7 days worth of blog posts and append them to a string. Here we are using SyndicationClient to make easier work of everything. We could also use StringBuilder but for now this will do.

string feedurl = "https://www.michaelcrump.net/feed.xml";
string last7days = "";

XmlReader reader = XmlReader.Create(feedurl);
SyndicationFeed feed = SyndicationFeed.Load(reader);
reader.Close();

last7days = last7days + "<b>New updates in the last 7 days:</b><br><br>";
foreach (SyndicationItem item in feed.Items)
{
    if ((DateTime.Now - item.PublishDate).TotalDays < 7)
    {
        last7days = last7days + "<a href=\"" + item.Links[0].Uri + "\')">" + item.Title.Text + "</a><br>";
    }       
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

We'll now grab a list of our EmailSubscribers that is in our Azure Storage Table.

CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["TableStorageConnString"]);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("MCBlogSubscribers");
table.CreateIfNotExists();
1
2
3
4

We'll need to add this helper method outside of the Run method we are currently in. It will search for the Partition Key that matches what we sent in our POST request in post #2.

public static List<string> GetAllEmailAddresses(CloudTable table)
{
    var retList = new List<string>();

    TableQuery<EmailEntity> query = new TableQuery<EmailEntity>()
            .Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "SendEmailToReaders"));

    foreach (EmailEntity emailname in table.ExecuteQuery(query))
    {
        retList.Add(emailname.EmailAddress);
    }

    return retList;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Now we need to send our emails. This is where it gets slightly tricky as we need to use the X-SMTPAPI header to hide the email address of all our users. Search NuGet and add Sendgrid.SmtpApi to your references. We'll need to get our SendGrid Username and Password and pass them in the credentials.

var header = new Header();

SmtpClient client = new SmtpClient();
client.Port = 587;
client.Host = "smtp.sendgrid.net";
client.Timeout = 10000;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential(ConfigurationManager.AppSettings["SendGridUserName"], ConfigurationManager.AppSettings["SendGridSecret"]);
1
2
3
4
5
6
7
8
9

Now we need to form our email message. We'll pull the list of email addresses from our Azure Storage Table and force the HTML view to ensure our links are clickable and tell those that don't have HTML enabled, to enable it. 😃

Finally, we'll send the email asynchronously and dispose of our client.

MailMessage mail = new MailMessage();
List<string> recipientlist = GetAllEmailAddresses(table);
header.SetTo(recipientlist);
mail.From = new MailAddress("michael@michaelcrump.net", "Azure Tips and Tricks");
mail.To.Add("no-reply@michaelcrump.net");
mail.Subject = "Weekly Digest for MichaelCrump.net Blog";
mail.BodyEncoding = Encoding.UTF8;
mail.SubjectEncoding = Encoding.UTF8;

AlternateView htmlView = AlternateView.CreateAlternateViewFromString(last7days);
htmlView.ContentType = new System.Net.Mime.ContentType("text/html");
mail.AlternateViews.Add(htmlView);
mail.Body = "Please enable HTML in order to view the message";

mail.Headers.Add("X-SMTPAPI", header.JsonString());

await client.SendMailAsync(mail);

mail.Dispose();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Before we publish the updates to our Azure Function, we'll need to ensure that users can unsubscribe easily. I was originally going to handle this myself, but it is super simple with SendGrid.

Log into your SendGrid account and go to Settings and then Tracking and you'll see Subscription Tracking. If you turn this on, then it will add the unsubscribe link for you and manage those users. NEAT!

Now is a great time to go ahead and publish our Azure Function. Simply right click the project name and select Publish, then Publish again as shown below.

Once it deploys, if you click on the SendEmail function, then you can run it (note you can also run it inside of Visual Studio).

It should say that it compeleted successfully, and now go check your email and it should be working.

And we're done! If something isn't working then check the source code for the app on GitHub and if you have any questions then ping me on twitter. You should follow me btw, I might have another tip and trick to share!