Monday, July 27, 2020

Send emails from ASP.NET Core websites using SendGrid and Azure

Today we have multiple free options to send email from our apps. Let's review how to configure and use SendGrid and Azure to send emails from our ASP.NET Core apps and benefit from their extraordinary free plan.
Photo by Carol Jeng on Unsplash

Long are the days that we had to use Gmail App Passwords to send and test emails from our apps. Today we have a plethora of alternatives that cost nothing or close to nothing. On that category, SendGrid offers Azure subscribers 25,000 free emails per month! So let's review how to setup a free SendGrid account and build a simple ASP.NET website to send emails from it.

On this post we will:
  • create a SendGrid account directly in Azure
  • build a simple ASP.NET Core web app and review how to properly configure it
  • access and configure our SendGrid settings in SendGrid
  • send emails using SMTP (not the RESTful API) from our console application
For a quick start, download the code from GitHub at: github.com/hd9/aspnet-sendgrid

Creating a SendGrid account in Azure

The good news is that in case you don't have one already, you can create a SendGrid account directly from Azure. Let's get straight to it. Open your Azure portal and type sendgrid on the search tool bar and click on SendGrid Accounts:
Click Add to create our account from Azure:
Enter your information on the next screen:
Review and confirm your package:
Wait until your deployment completes (it should take no more than 10 seconds). Now go back to SendGrid Accounts and you should see your new account there:
Clicking on it would take you to the SendGrid pane showing you essential information about your new resource:
Did you notice that Manage button? Clicking that button will take us directly to SendGrid where we'll be able to configure out account, create api keys, monitor our usage and a lot more.

I won't expand much in what SendGrid offers (tldr; a lot!). For more of that, feel free to visit their website.

Configuring SendGrid

The first time you login to SendGrid, you'll be requested to confirm your email address. After confirmation, this is the what you should see a screen similar to the below, showing you a general overview of your account:

Creating our SMTP API Key

To be able to send emails from SendGrid, we'll have first to generate a password. First click on Settings -> API Keys:
Choose Restricted Access:

Select Mail Send (for this demo we only need that one):

And click create. You'll be presented with your password (api key). Copy it safely:

SendGrid Configuration

With the password in hand, here's a summary about the configuration we'll need:
  • Host: smtp.sendgrid.net
  • Port: 587
  • Username: apikey
  • Password: SG.zGNcZ-**********************

Building our App

I guess that at this point, creating an ASP.NET web app is no surprise to anyone. But if you're new to .NET Core, please check this documentation on how to build and run ASP.NET Core on Linux. It's a different perspective from the Visual Studio-centric approach you'll see elsewhere. To quickly create with VS, File -> Create a new project and select Web Application (Model-View-Controller).

Configuring our App

With the configuration in hand, let's now review how to use it. To simplify things, I built already a simple web app that captures 2 fields: name and email of a potential newsletter subscriber. It looks like this and is available on GitHub:
Apart from the visual, there are a couple of things on this app that are worth looking into. Let's start with the configuration. If you open appsettings.json on the root of the project you will see:
  "SmtpOptions": {
    "Host": "<smtp>",
    "Port": "587",
    "Username": "<account>",
    "Password": "<password>",
    "FromName": "<from-name>",
    "FromEmail": "<from-email>",
    "EmailOverride": "<email>"
  },
  "EmailTemplate": {
    "Subject": "[HildenCo WebStore] Welcome to our newsletter!",
    "Body": "Hello {0},\nThanks for signing up for our newsletter!\n\nBest Regards,\nHildenCo."
  }

Since I already explained how to bind that config to a class of our own, I'll not extend too much on the topic. Essentially we will:
  • map the SmtpOptions configuration into a SmtpOptions class
  • map the EmailTemplate config into the EmailConfig class
That mapping is done elegantly by the framework as this line from Startup.cs shows:
cfg = configuration.Get<AppConfig>();
Inspecting cfg during debug confirms the successful binding:

Dependency Injection

Next, it's time to setup dependency injection. For our objective here, ASP.NET's default DependencyInjection utility is good enough. Put the below in your ConfigureServices method to wire everything up:
services.AddSingleton(cfg.SmtpOptions);
services.AddSingleton(cfg.EmailTemplate);
services.AddTransient<IMailSender, MailSender>();
Next, inject the dependencies needed by our Controller and our MailSender classes:
readonly IMailSender _mailSender;
readonly ILogger<HomeController> _logger;

public HomeController(
    IMailSender mailSender,
    ILogger<HomeController> logger)
{
    _logger = logger;
    _mailSender = mailSender;
}

Invoking SendMail from our controller

To call MailSender from our controller, simply inject a SendMail command into and invoke it:
await _mailSender.Send(new SendMail
{
    Name = signup.Name,
    Email = signup.Email
});

Our MailSender class

To finish, here's an excerpt of our MailSender class (see the full source on GitHub):
// init our smtp client
var smtpClient = new SmtpClient
{
    Host = _smtpOptions.Host,
    Port = _smtpOptions.Port,
    EnableSsl = true,
    DeliveryMethod = SmtpDeliveryMethod.Network,
    UseDefaultCredentials = false,
    Credentials = new NetworkCredential(_smtpOptions.Username, _smtpOptions.Password)
};
// init our mail message
var mail = new MailMessage
{
    From = new MailAddress(_smtpOptions.FromEmail, _smtpOptions.FromName),
    Subject = _tpl.Subject,
    Body = string.Format(_tpl.Body, msg)
};
// send the message
await smtpClient.SendMailAsync(mail);

Testing the App

Run the app with Visual Studio 2019, enter a name and an email address. If all's configured correctly, you should soon get an email in your inbox:
As well as SendGrid reporting a successful delivery:

Final Thoughts

The only remaining question is why SMTP? The advantages of using SMTP instead of the API is that SMTP is a pretty standard protocol, works with .NET's primitives, works with any programming language or framework and, contrary to the restful API, does not require any specific packages. SMTP also works well with containers, but I'll leave that fore a future post. 😉

Conclusion

On this tutorial we reviewed how to create a SendGrid account directly from Azure, we demoed how to configure it so we can send emails from our applications. SendGrid is a great email service with a very powerful API that I recommend exploring and learning other topics such as creating your own templates, using webhooks, etc. On the future we'll revisit this example to send email from our own ASP.NET containers in a microservice application. Keep tuned!

Source Code

As always, the source is available on GitHub.

References

See Also

Wednesday, July 15, 2020

Hosting NuGet packages on GitHub

On this post let's review how to build, host and consume our own NuGet packages using GitHub Packages
Photo by Leone Venter on Unsplash

Long gone are the days we had to pay to host our NuGet packages. Today, things have changed. We have many options to host our own NuGet packages for free (including privately if we wish) including in our own GitHub repositories. On this tutorial let's review how to build our own packages using .NET Core's CLI, push them to GitHub and finally, how to consume from our own projects.

About NuGet

NuGet is a free and open-source package manager designed by Microsoft and used extensively in the .NET /.NET Core ecosystem. NuGet is the name of the tool and of the package itself. The most common repository for NuGet packages is NuGet.org hosting more than 200k packages! But we can host our own packages on different repos (including private ones) such as GitHub Packages. NuGet is bundled with Visual Studio and with the .NET Core SDK so you probably have it already available on your machine.

About GitHub Packages

GitHub Packages is GitHub's free offering for those wanting to host their own packages. GitHub Packages allows hosting public and private packages. The benefits of using GitHub Packages is that it's free, you can share your packages privately or with the rest of the world, integrate with GitHub APIs, GitHub Actions, webhooks and even create complex end-to-end DevOps workflows. For more information about GitHub Packages, click here.

Why build our own packages

But why build our own packages? Mainly because packages simplify using and distributing self-contained and reusable software (tools, libraries, etc) in a clean and organized way of doing so. Beyond that, other common reasons are:
  1. sharing packages with someone else (and possibly the world)
  2. sharing that package privately with your coworkers so they can be used in different projects.
  3. packaging software so it can be installed or deployed elsewhere.

Building NuGet Packages

So let's get started and build our first NuGet package. The project we'll build is a simple library consisting of POCOs I frequently use as standard onfiguration bindings when developing microservices: Smtp, Redis, RabbitMQ, MassTransit and MongoDB. I chose this example because this is the type of code we frequently duplicate, so why not isolate them in a shareable package and keep our codebase DRY?

Creating our project

To quickly create my project let's use the .NET Core CLI (feel free to use Visual Studio if you will):
dotnet new classlib -o HildenCo.Core
Then I'll add those config classes. For example the SmtpOptions looks like:
public class SmtpOptions
{
    public string Host { get; set; }
    public int  Port { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public string FromName { get; set; }
    public string FromEmail { get; set; }
}

Creating our first NuGet package

Let's then create our first package. The simplest way to do so is by configuring it via Visual Studio. For that, select the Project and Alt-Enter it (or right-click it with the mouse) to view Project Properties and check Generate NuGet package on build on the Package tab:
Don't forget to add relevant information about your package such as Id, Name, Version, Authors, Description, Copyright, License and RepositoryUrl. All that information is required by GitHub:
If you prefer, you can edit the above metadata directly in the csproj file.
Now, build again to confirm our package was built by inspecting the Build Output in VS (Ctrl-W, O):
1>------ Build started: Project: HildenCo.Core, Configuration: Debug Any CPU ------
1>HildenCo.Core -> C:\src\nuget-pkg-demo\src\HildenCo.Core\bin\Debug\netstandard2.0\HildenCo.Core.dll
1>Successfully created package 'C:\src\nuget-pkg-demo\src\HildenCo.Core\bin\Debug\HildenCo.Core.0.0.1.nupkg'.
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
Congrats! You now have built your first package!
Don't forget to add RepositoryUrl with your correct username/repo name. We'll need it to push to GitHub later.

Creating our package using the CLI

As always, the CLI may be a better alternative. Why? In summary because it allows automating package creation on continuous integration, integrating with APIs, webhooks and even creating end-to-end DevOps workflows. So, go ahead and uncheck that box and build it again with:
dotnet pack --configuration Release
This time, we should see this as output:
Microsoft (R) Build Engine version 16.6.0+5ff7b0c9e for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  All projects are up-to-date for restore.
  HildenCo.Core -> C:\src\nuget-pkg-demo\src\HildenCo.Core\bin\Release\netstandard2.0\HildenCo.Core.dll
  Successfully created package 'C:\src\nuget-pkg-demo\src\HildenCo.Core\bin\Release\HildenCo.Core.0.0.1.nupkg'.
TIP: You may have realized that we now built our package as release. This is another immediate benefit from decoupling our builds from VS. On rare occasions should we push packages built as Debug.

Pushing packages to GitHub

With the basics behind, let's review how to push your own packages to GitHub.

Generating an API Key

In order to authenticate to GitHub Packages the first thing we'll need is an access token. Open your GitHub account, go to Settings -> Developer Settings -> Personal access tokens, click Generate new Token, give it a name, select write:packages and save:

Creating a nuget.config file

With the API key created, let's create our nuget.config file. This file should contain the authentication for the package to be pushed to the remote repo. A base config is listed below with the fields to be replaced in bold:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <packageSources>
        <clear />
        <add key="github" value="https://nuget.pkg.github.com/<your-github-username>/index.json" />
    </packageSources>
    <packageSourceCredentials>
        <github>
            <add key="Username" value="<your-github-username>" />
            <add key="ClearTextPassword" value="<your-api-key>" />
        </github>
    </packageSourceCredentials>
</configuration>

Pushing a package to GitHub

With the correct configuration in place, we can push our package to GitHub with:
dotnet nuget push ./bin/Release/HildenCo.Core.0.0.1.nupkg --source "github"
This is what happened when I pushed mine:
dotnet nuget push ./bin/Release/HildenCo.Core.0.0.1.nupkg --source "github"
Pushing HildenCo.Core.0.0.1.nupkg to 'https://nuget.pkg.github.com/hd9'...
  PUT https://nuget.pkg.github.com/hd9/
  OK https://nuget.pkg.github.com/hd9/ 1927ms
Your package was pushed.
Didn't work? Check if you added RepositoryUrl to your project's metadata as nuget uses it  need it to push to GitHub.

Reviewing our Package on GitHub

If you managed to push your first package (yay!), go ahead and review it in GitHub on the Package tab of your repository. For example, mine's available at: github.com/hd9/nuget-pkg-demo/packages and looks like this:

Using our Package

To complete the demo let's create an ASP.NET project to use our own package:
dotnet new mvc -o TestNugetPkg
To add a reference to your package, we'll use our own nuget.config since it contains pointers to our own repo. If your project has a solution, copy the nuget.config to the solution folder. Else, leave it in the project's folder. Open your project with Visual Studio and open the Manage NuGet Packages. You should see your newly created package there:
Select it and install:
Review the logs to make sure no errors happened:
Restoring packages for C:\src\TestNugetPkg\TestNugetPkg.csproj...
  GET https://nuget.pkg.github.com/hd9/download/hildenco.core/index.json
  OK https://nuget.pkg.github.com/hd9/download/hildenco.core/index.json 864ms
  GET https://nuget.pkg.github.com/hd9/download/hildenco.core/0.0.1/hildenco.core.0.0.1.nupkg
  OK https://nuget.pkg.github.com/hd9/download/hildenco.core/0.0.1/hildenco.core.0.0.1.nupkg 517ms
Installing HildenCo.Core 0.0.1.
Installing NuGet package HildenCo.Core 0.0.1.
Committing restore...
Writing assets file to disk. Path: C:\src\TestNugetPkg\obj\project.assets.json
Successfully installed 'HildenCo.Core 0.0.1' to TestNugetPkg
Executing nuget actions took 960 ms
Time Elapsed: 00:00:02.6332352
========== Finished ==========

Time Elapsed: 00:00:00.0141177
========== Finished ==========
And finally we can use it from our second project and harvest the benefits of clean code and code reuse:

Final Thoughts

On this post we reviewed how to build our own NuGet packages using .NET Core's CLI, pushed them to GitHub and finally described how to consume them from our own .NET projects. Creating and hosting our own NuGet packages is important for multiple reasons including sharing code between projects and creating deployable artifacts.

Source Code

As always, the source code for this post is available on GitHub.

See Also

Monday, July 6, 2020

20 tips to manage Linux VMs on Azure

Azure offers many tools to manage our Linux VMs. On this post, let's review the most interesting ones.
Photo by Szabo Viktor on Unsplash

On previous posts we learned how to create a CentOS VM on Hyper-V and how to deploy and run it on Azure. Today we will review some of the tools available on Azure to manage our virtual machines. Azure offers many tools to manage our Linux VMs including alerts, automatic backups, security, making managing our VMs an easy task.
On this post I'll be using my a CentOS VM but these tips should apply to any distro of your choice. If you want to know what's necessary to run a custom CentOS VM on Azure, please check the previous posts where where I detailed how to create a CentOS VM locally and how to get it running on to Azure.

Getting ready

So let's get started. Let's access our VM in Azure by clicking on Virtual Machines and selecting it:
With the VM opened, we're provided with the following overview. From here, we can Start/Stop/Delete and Connect to it and also view important information such as our internal and external IPs:

Tip 1 - Deallocating to cut costs

In Azure, stopping and deallocating my VMs are different. If all you want is cutting costs then you should be deallocating your VMs. Just mind that by deallocating the VM, some of your resources will be released including your public IP. Want to keep it? You can as show below, but you will be charged for it.

Tip 2 - Accessing logs with Serial Log

Accessing the logs for your VMs is also simple by going to Boot Diagnostics -> Serial Log. For example, this is the what I got from my boot:

Tip 3 - VM Screenshot

Can't access your VM? The screenshot tool may be useful as it can show you the stat of your virtual machine. To access the screenshot tool, go to Boot Diagnostics -> Secreenshot:

Tip 4 - SSHing into the VM

Most of us know already how to SSH into our VMs: we get our public IP address from the overview page and SSH into directly to the terminal. However, Azure also provides us a nice Connect tab where it shows different ways we could to connect to our servers:
Assuming you have external access to your VM, connecting to it should be as simple as:
ssh user@ip

Tip 5 - Connection Troubleshoot

In case the above doesn't work, we also have access to a Connection Troubleshoot pane where we can test the connection to the VM. This screen is useful as it runs from inside Azure bypassing the external firewall rules isolating potential routing or network problems between our machine and our server:

Tip 6 - Configure the Virtual Network

The Networking tab allows us configuring the virtual network. From here we can allow/restrict access by source, protocol, port, inbound, outbound and load balancing tools. The basic configuration is:

Tip 7 - Allow your own IPs using NSG Firewall Rules

We can leverage custom firewall rules to block malicious IPs directly in Azure directly in the Network Security Group (NSG) associated to your VM. The recommendation is to block everything and only whitelist (allow access) to the IPs you trust. Whitelisting is better than blacklisting because people use cloud servers and rotate their IPs pretty frequently. To do that, choose Source = IP Addresses and specify your own IPs:

Tip 8 - Block specific ports using NSG Firewall Rules

You could also restrict access to whatever you need. For example, to deny requests on port 22 we could add the following rule:
It's recommended to not leave your machine exposed. During my tests my server logged more than 1000 attempts to brute force a password from more than 40 different IPs. Make sure to keep your box secured!

In case you need, here's how to monitor who's trying to break into your box since the last boot:
journalctl -b | grep "Failed password for" | sed 's/.* from //' failed.txt | sed 's/ port.*$//' | sort -u | wc -l
1136

Tip 9 - Accessing the Serial Console

Azure Serial Console is one of my favourites. It allows us to run commands on the VM from Azure. It's an essential tool to debug, fix and inspect our VM. From it you can login to your machine (even if you cannot from your own PC) and manage it remotely via Azure:

Tip 10 - Configure the Public IP

You can also configure how Azure assigns your VM a public IP. Dynamic means it will rotate (change) after each deallocation. Static (more expensive) will reserve that IP for you but will incur costs even if the VM is deallocated:

Tip 11 - Create a custom DNS Name

We could also create a custom DNS name for our VM so that we can access it using the DNS name instead of its IP address. That can be one on Overview -> DNS Name -> Configure and if your domain is managed by Azure (like mine), you should also be able to configure a subdomain to point to that VM directly from here. For example, to create a subdomain like ssh.mydomain.com pointing to your CentOS server you should do:

Tip 12 - Reset Password

Forgot your password? You can reset it directly from Azure. Plus,configuration and SSH public key can also be reset:

Tip 13 - Configure Backup Policies

Auto-backing up of our VM is another nice feature. We can specify the location, interval and time and have Azure do the job:

Tip 14 - View VM Telemetry

Azure also provides interesting telemetry about our VMs. And you can even configure your own if you wish:

Tip 15 - Create Custom Alerts

Azure utilizes insights captured for your VM to generate alerts based on your custom criteria and allows us to create custom alerts that can notify us by Email/text. Simply go to Create Rule:
Then create one by using one of the available metrics:

Tip 16 - Specify Auto-shutdown

Auto-shutdown is also very useful. It allows us to schedule shutdowns helping saving on costs:

Tip 17 - Specify a Disaster Recovery Strategy

We can also leverage Azure's Site Recovery functionality to replicate our VMs. That's very useful for keeping the SLAs and recovering from disaster recovery situations:

Tip 18 - Automated Security Recommendations

If you're willing to pay a little more Azure also offers security recommendations via its Security Center where you can increase your protection:

Tip 19 - Export Template Scripts

This is also nice as we can export a reproducible script to automate the creation of an equivalent VM. Save this on your company's repository so you can quickly span new clones of this VM in the future.

Tip 20 - Run adhoc commands

This is also a very useful feature where you can, from the portal, run a command on your machine:

Conclusion

On this post we presented various tools to manage your Linux VM on Azure. While we can always manage our VMs using SSH, it's also important to remember that when running on the cloud, there are other awesome tools we can leverage to monitor, inspect, alert, manage, secure and troubleshoot your server.

And remember that it's very, very important to secure your VMs in the cloud.

See Also

About the Author

Bruno Hildenbrand      
Principal Architect, HildenCo Solutions.