Setup a Continous Delivery (CD) Pipeline in AWS for a .NET Core 3.x Web API

Rui Pedrosa
The Startup
Published in
5 min readJul 8, 2020

--

One of my clients whose application runs on AWS had no Continuous Integration (CI). When we decide to use Continous Delivery (CD) for a new NET Core 3.x Web API, it was important that we do it using AWS tools/services as much as possible as this will make their life easier: no new accounts like Azure DevOps need to be managed, they can control access in a single place (AWS IAM), and so on.

Looking at AWS offering, we opted for:

Step 1: Automatically build the code aka Continous Integration (CI)

AWS CodeBuild requires your build specification to be defined in a buildspec file.

A buildspec is a collection of build commands and related settings, in YAML format, that CodeBuild uses to run a build.

So let's start by adding a buildspec.yml file in the root of your project like this:

AWS CodeBuild buildspec.yml example file for a .Net core 3.x Web API

As you can see, we are:

  1. restoring and build our API project using .Net Core CLI;
  2. publishing a runtime-dependent executable for a specific platform. Yes, we will deploy this in Linux through AWS Elastic Beanstalk later on;
  3. you may want to publish if tests succeed by run dotnet test first;

Time to test :)

  1. Create an AWS CodePipeline:
AWS CodePipelines: Choose pipeline settings

2. Add a source provider and select which branch should trigger a build:

AWS CodePipelines: Add source stage

By the time I’m writing, there isn’t a GitHub action supported by AWS to deploy to AWS Elastic Beanstalk but I bet that won’t take long to come so you may completely rely upon GitHub Actions if that is an option to you :). By the time I made this, GitHub actions were about to be announced ;)

3. And finally, add a build stage based on the AWS CodeBuild project. I strongly recommend that you create a new AWS CodeBuild project by AWS CodePipeline as this will configure some things automatically for you like the Source Provider as AWS CodePipeline

AWS CodePipelines: Add build stage

As you can see, we are using builspec file that we previously placed in the root of our project :)

Done! You can skip deploying stage by now just to check that builds works ;) As soon as you save the pipeline, a new build will be automatically triggered. The same will happen for future commits in the source branch that you choose at step 2. — “Add source stage” ;)

Step 2: Automatically deploy your code aka Continous Delivery (CD)

How you set the deploy stage, it depends on how you host your API. As we are using AWS, we decide to with AWS Elastic Beanstalk as it is an “easy-to-use service for deploying and scaling web applications” on several platforms. If you are using something else than AWS Elastic Beanstalk to manage your hosting environment in AWS, it is very likely that you end up using AWS CodeDeploy instead of AWS Elastic Beanstalk at “Deploy stage”.

So, let's start by creating an AWS Elastic Beanstalk application (& environment) by following the AWS tutorial:

  1. although I’m pointing to .Net core on Linux as a platform, you can also choose Windows Server (with IIS as a proxy) or docker if that is your preference;
  2. if you are creating a non-production environment and you want to run Entity Framework Core migrations as part of this pipeline, I suggest that you create a database as part of your AWS Elastic Beanstalk environment; Please note that having an RDS instance being created on AWS Elastic Beanstalk “it isn’t ideal for a production environment because it ties the lifecycle of the database instance to the lifecycle of your application’s environment”;

Now that you have your AWS Elastic Beanstalk application (& environment) up and running with the sample code, let’s edit our AWS CodePipeline project to deploy our code by adding a stage after build step called “Deploy”:

AWS CodePipeline: Add a deploy stage

and voilà :) If you trigger a new build, your AWS Elastic Beanstalk will be automatically updated :)

Step 3: Automatically run Entity Framework Core Migrations Continous Delivery (CD)

To run EF Core migrations, we can add another Stage in our AWS CodePipeline called Run-DBMigrations that rely on AWS CodeBuild to execute dotnet ef database update. So, let's create another buildspec file in the root of our project:

db-migrations-spec.yml

In order to this to work, you need to:

  1. setup the database connection string as an environment variable in AWS CodeBuild;
  2. allow AWS CodeBuild to reach the RDS instance

and voilà :) If you trigger another build, it just works otherwise just ask :)

AWS CodePipeline succcessful run

About AWS Elastic Beanstalk setup:

As you may notice, I’m not setting any environment variable as I’m doing that in AWS Elastic Beanstalk directly.

If like me you are using Nginx as a reverse proxy server, you might want to:

  1. Use app.UseForwardedHeaders in your Statup.Configure method;
  2. Have your app listening in PORT 5000 by using an .ebextension:
.ebextension — env.config
Api.csproj

Looking for professional excellence:

  1. AWS Elastic Beanstalk supports Blue/Green deployments so you may want to update the pipeline to first deploy to a pre-production environment and have, for example, a manual action before swap CNAMES using Elastic Beanstalk command line interface (EB CLI);

Because AWS Elastic Beanstalk performs an in-place update when you update your application versions, your application can become unavailable to users for a short period of time. You can avoid this downtime by performing a blue/green deployment, where you deploy the new version to a separate environment, and then swap CNAMEs of the two environments to redirect traffic to the new version instantly.

2. and if something goes wrong with EF Core migrations and some data got corrupted? You may still use an automatic RDS snapshot to restore but still, there is a data loss and a higher downtime time. So, why not to extend the pipeline to create a database backup named with a version number?
You can create a database snapshot in AWS RDS or a native backup like this for SQL Server:

RDS Native backup for SQL Server

--

--