AWS Developer Tools Blog

Exploring ASP.NET Core Part 2: Continuous Delivery

The first post in this series discussed how to use an Amazon EC2 instance and AWS CodeDeploy to deploy ASP.NET Core applications from GitHub. The setup assumed all git pushes to GitHub were deployed to the running environment without validation. In this post, let’s examine how we can create an AWS environment for our ASP.NET Core application that gives us quality control and takes advantage of AWS cloud scale.

You’ll find the code and setup scripts for this post in the part2 branch of the aws-blog-net-exploring-aspnet-core repository.

Validating the Deployment

In the first post, the appspec.yml file called the InstallApp.ps1 script during the ApplicationStart hook to extract the application and set up IIS. To verify the application is up and running, let’s update the appspec.yml file to call ValidateInstall.ps1 during the ValidateService hook.

version: 0.0
os: windows
files:
  - source: 
    destination: C:ExploringAspNetCore
hooks:
  ApplicationStop:
    - location: .RemoveApp.ps1
      timeout: 30
  ApplicationStart:
    - location: .InstallApp.ps1
      timeout: 300
  ValidateService:
    - location: .ValidateInstall.ps1
      timeout: 300

This script allows us to call tests to make sure our application is running correctly. In the GitHub repository, I added some xunit tests under .SampleAppsrcSmokeTests. My sample application is no more than a simple “Hello World” web application so I just need to test that I can make a web call to the application and get a valid response. In a real-world application you would have a much more exhaustive suite of tests to run during this validation step.

Let’s take a look at ValidateInstall.ps1 to see how the tests are run.

sl C:ExploringAspNetCoreSampleAppsrcSmokeTests

# Restore the nuget references
& "C:Program Filesdotnetdotnet.exe" restore

# Run the smoke tests
& "C:Program Filesdotnetdotnet.exe" test

exit $LastExitCode

To run the tests, switch to the directory where the tests are stored. Restore the dependencies, and then run the dotnet test command. The failure of any test will cause dnx to return a non 0 exit code, which we return from the PowerShell script. AWS CodeDeploy will see the failed exit code and mark the deployment as a failure. We can get logs from the test run from the AWS CodeDeploy console to see what failed.

Using AWS CodePipeline

Now that deployments are running smoke tests, we can detect bad deployments. In a similar way, we want to make sure users aren’t affected by bad deployments. The best practice is to use a pipeline with a beta stage during which we run smoke tests. We promote to production only if beta was successful. This gives us continuous delivery with safety checks. Again, as we discussed in part 1, we benefit from the ability of ASP.NET Core to run from source. It means we do not have to bother configuring a build step in our pipeline. Pipelines can pull source from Amazon S3 or GitHub. To provide a complete sample that can be set up with just a PowerShell script, we’ll use S3 as the source for our pipeline. AWS CodePipeline will monitor S3 for new versions of an object to push them through the pipeline. For information about configuring a GitHub repository, see the AWS CodePipeline User Guide.

Setup Script

The PowerShell script .EnvironmentSetupEnvironmentSetup.ps1 in the repository will create the AWS resources required to deploy the application through a pipeline.

Note: To avoid charges for unused resources, be sure to run .EnvironmentSetupEnvironmentTearDown.ps1 when you are done testing.

The setup script sets up the following resources:

  • An S3 bucket with a zip of the archive as the initial deployment source.
  • A t2.small EC2 instance for beta.
  • An Auto Scaling group with a load balancer using t2.medium instances.
  • An AWS CodeDeploy application for beta using the t2.small EC2 instance.
  • An AWS CodeDeploy application for production using the Auto Scaling group.
  • AWS CodePipeline with the S3 bucket as the source and the beta and production stages configured to use the AWS CodeDeploy applications.

When the script is complete, it will print out the public DNS for the beta EC2 instance and production load balancer. We can monitor pipeline progress in the AWS CodePipeline console to see if the deployment was successful for both stages.

The application was deployed to both environments because the smoke tests were successful during the AWS CodeDeploy deployments.

Failed Deployments

Let’s see what happens when a deployment fails. We’ll force a test failure by opening the .SampleAppsrcSmokeTestsWebsiteTests.cs test file and making a change that will cause the test to fail.

[Fact]
public async Task PassingTest()
{
    using (var client = new HttpClient())
    {
        var response = await client.GetStringAsync("http://localhost-not-a-real-host/");

        Assert.Equal("Exploring ASP.NET Core with AWS.", response);
    }
}

In the repository, we can run the .DeployToPipeline.ps1 script, which will zip the archive and upload it to the S3 location used by the pipeline. This will kick off a deployment to beta. (The deployment will fail because of the bad test.)

A deployment will not be attempted during the production stage because of the failure at beta. This keeps production in a healthy state. To see what went wrong, we can view the deployment logs in the AWS CodeDeploy console.

Conclusion

With AWS CodeDeploy and AWS CodePipeline, we can build out a full continuous delivery system for deploying ASP.NET Core applications. Be sure to check out the GitHub repository for the sample and setup scripts. In the next post in this series, we’ll explore ASP.NET Core cross-platform support.