AppVeyor – Continuous Delivery to Azure for your .NET Core Applications

AppVeyor - Continuous Delivery for your .NET Core Applications Header Image

Today’s header image was created by Ollie Tigwell, the original source for the image is available here

This week we’re going to continue where we left off with AppVeyor.

if you haven’t read my previous post on AppVeyor, I’d recommend giving it the once over.

As with last time, this week’s topic isn’t necessarily .NET Core specific, but it seems right to finish what we started. Plus, how cool would it be for the latest version of a repository to be built, tested, and deployed for us as soon as we commit to it?

Before we do that though, I want to point out that I’ll be using AppVeyor to publish to Azure. If you’re not using Azure to host your .NET Core applications, then parts of this blog article wont apply to your used case. I’ll point out where things don’t apply, as we go along.

As with last time, we’ll cover a little software Engineering theory first.

I know it’s boring, but some people don’t know this stuff. If you already know about this stuff, then you can skip to the gooey goodness by clicking here

Continuous Delivery

Continuous delivery (CD) is a software engineering approach in which teams produce software in short cycles, ensuring that the software can be reliably released at any time. It aims at building, testing, and releasing software faster and more frequently. The approach helps reduce the cost, time, and risk of delivering changes by allowing for more incremental updates to applications in production. A straightforward and repeatable deployment process is important for continuous delivery

Source: https://en.wikipedia.org/wiki/Continuous_delivery

A lot of people confuse Continuous Delivery with Continuous Deployment. The latter (Continuous Deployment) is all about getting the very latest changes out to the customer as soon as they are committed to the code base.

It’s not a perfect analogy, but you can think of Visual Studio Code Insiders as software which follows Continuous Deployment, as it’s an almost nightly build of the VS Code GitHub repo with the latest features and bug fixes.

it’s not a perfect analogy, because it doesn’t really work like that. But it’s close enough for now.

Whereas Continuous Delivery is all about having a specific delivery pipeline which facilitates testing and approval of all changes before a build can be used in production.

A basic pipeline might be as follows:

Continuous Delivery Diagram
Image source: https://en.wikipedia.org/wiki/Continuous_delivery#/media/File:Continuous_Delivery_process_diagram.svg By Grégoire Détrez, original by Jez Humble [CC BY-SA 4.0 (http://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia Commons

A single code change (or a collection of code changes) will follow the path from Version Control through to Release, and can be stopped along the way at any point. A failing unit test (the first red box in the above diagram) feeds back to the delivery team, who implement a fix for the failing test. This loop of triggers and feeding back continues until the code reaches the Release stage (this, typically, is when the code is handed over to the customer).

CD for .NET Core

If you remember from last time,

you did read the previous article in this series, right?

we got AppVeyor to clone and build a repository whenever a push to the master branch happened.

This time, we’ll build a separate script to do the same thing (build, test, and bundle our application) but with the included step of pushing it to our production environment. For dwCheckApi this means pushing to Azure.

quick refresher: the latest build of dwCheckApi is available at dwcheckapi.azurewebsites.net and if you want to fork that repo and play along, you can do so here

Why the Separate AppVeyor script?

Think of it this way: the AppVeyor script that we built last week uses the master branch. But what if we want that to use a development (or feature) branch for CI? If we had one script which built, tested, packaged and deployed our application, then we’d need to either stop it from doing the final step or create a separate script just for CI.

oh wait we don’t need to, because we did that last time.

Having a separate script for deploying to production makes sense; if only to separate our development builds from the master build.

I know that (at the time of writing) dwCheckApi only has one branch, but it might have more in the future. Plus, whatever you’re using might have multiple branches

Building a Deployment Script

The first thing we need to do is get the latest version of the code from our repository. I’ll leave you to figure out how to do that, as your repo might be different from mine.

unless you’ve forked dwCheckApi

Once you’ve done that, you’ll need to create a new script for building, testing and deploying.

Create AppVeoyor Deploy Script
highlighted on the left-hand side, here

I’ve called mine appveyor-deploy.yml, but you can call yours whatever you wish.

We’ll build the script content to match most of the steps in in the script we built last time. It would be pretty strange to have a deploy script which differed completely from the CI build script, unless there was a specific reason to have it differ. I can’t think of one, so let’s base it on that.

Here’s the deploy script that we’ll create:

version: 1.1.{build}
image: Visual Studio 2017
branches:
only:
- master
init:
- git config --global core.autocrlf true
before_build:
# Display minimal restore text
- cmd: dotnet restore ./src/src.csproj --verbosity m
build_script:
# output will be in ./src/bin/debug/netcoreapp1.1/publish
- cmd: dotnet publish ./src/src.csproj
artifacts:
- path: '\src\bin\Debug\netcoreapp1.1\publish'
name: WebSite
type: WebDeployPackage
test_script:
# tests in here
- cmd: dotnet restore ./tests/tests.csproj --verbosity m
- cmd: cd tests
- cmd: dotnet xunit
deploy:
- provider: Environment
name: dwCheckApi-Production

Go ahead and paste that code in. We’ll give it a read through in a moment.

The Deploy Script Proper

Taking a look at the deploy script and comparing it to the build script, they look quite similar.

Here’s the build script from last time, just as a refresher:

version: '1.0.{build}'
image: Visual Studio 2017
branches:
only:
- master
init:
# Good practise, because Windows line endings are different from Unix/Linux ones
- cmd: git config --global core.autocrlf true
install:
# Install repo specific stuff here
before_build:
# Display .NET Core version
- cmd: dotnet --version
# Display minimal restore text
- cmd: dotnet restore ./src/src.csproj --verbosity m
build_script:
# output will be in ./src/bin/debug/netcoreapp1.1/publish
- cmd: dotnet publish ./src/src.csproj
after_build:
# For once the build has completed
artifacts:
- path: '\src\bin\Debug\netcoreapp1.1\publish'
name: WebSite
type: WebDeployPackage
clone_depth: 1
test_script:
# restore packages for our unit tests
- cmd: dotnet restore ./tests/tests.csproj --verbosity m
# run the unit tests (requires changing into the test directory)
- cmd: cd tests
- cmd: dotnet xunit
on_finish :
# any cleanup in here
deploy: off

Aside from where we’ve removed comments and empty sections, we’ve added the following section:

deploy:
- provider: Environment
name: dwCheckApi-Production

Environments

Before we commit our changes, we need to create an environment. I guess we should figure out what an environment is, too.

Environments are an AppVeyor feature which allow us to deploy our build artifacts to an external hosting environment.

Ok, I used the word “environment” way too many times, there

We’ll use an (AppVeyor) environment to store all of the settings that we need to asynchronously deploy a built, tested and packaged application. So let’s go create one.

Head over to the Environments tab in the AppVeyor UI

click here to go directly to the environments tab

then click “New Environment” and you should see something similar to this:

AppVeyor New EnvironmentWe need to pick a provider which will line up with our chosen hosting environment. As dwCheckApi is hosted on Azure, we can use the “Web Deploy” option.

this is specific to Azure, obviously

As soon as you choose “Web Deploy”, the screen will fill up with all sorts of options:

AppVeyor New Environment Web Deploy
This image is pretty big, so you’ll have to click to get the full experience

I know what you’re thinking,

was that five shots or six?

I was a little overwhelmed when I saw all of those options, too.

Caveat

The first thing we need to do is make sure (and this is a pitfall I fell down) that the new Environment uses the name that we provided in the deploy script.

deploy:
- provider: Environment
name: dwCheckApi-Production

AppVeyor will use the value we supply in the name field to pick the correct Environment to use when deploying. So make sure that whatever you entered in the name field in the deploy section

I chose “dwCheckApi-Production” for mine, but you can call yours whatever you like

matches the name of the Environment we’re about to create. Otherwise you’ll end up with deploys that don’t run to completion.

Web Deploy Settings

So, what do we need to enter into the Web Deploy settings? dwCheckApi is published on Azure, so we need to get some settings from there.

I say “we”

I’m going to assume that you’re hosting is being provided by Azure; if not, then you’ll need to find similar settings from your chosen hosting provider.

they probably already provide them, to be honest. Otherwise, how have you published to that environment already?

In any case, head over to Azure and load the App Service blade for your… well, app service:

Azure App Service Blade

Once the App Service blade has loaded, click on “Get Publish Profile”

I intentionally left the mouse pointer there in the image

and this will download a file called “<your app service name here>.PublishSettings”, which is an XML-like file.

psst, mine was called “dwCheckApi.PublishSettings”. Not that you needed to know that, mind

In here, you’ll find the values for the Environment settings. Here is an edited version of a publish profile, and we’ll use it to fill in the Environment settings in a moment:

<publishData>
<publishProfile
profileName="dwCheckApi - Web Deploy"
publishMethod="MSDeploy"
publishUrl="dwcheckapi.someUrl.someService.tld:443"
msdeploySite="dwCheckApi"
userName="$dwCheckApi"
userPWD="CorrectHorseBatteryStaple"
destinationAppUrl="http://dwcheckapi.azurewebsites.net"
SQLServerDBConnectionString=""
mySQLDBConnectionString=""
hostingProviderForumLink=""
controlPanelLink="http://panel.someService.tld"
webSystem="WebSites">
<databases />
</publishProfile>
</publishData>

that’s not my real publish profile, obviously. I’ve changed the values of all of the sensitive fields.

Firstly, we need the server name. We’ll use the publish URL as a base, as we need to add some things to it:

publishUrl="dwcheckapi.someUrl.someService.tld:443"

We need to add “/msDeploy.axd?site=” then whatever the value of “msdeploySite” is. Using the values from publish profile above, we’ll end up with:

dwcheckapi.someUrl.someService.tld:443/msdeploy.axd?site=dwCheckApi

We added “msDeploy.axd” because of the value of the publish method:

publishMethod="MSDeploy"

If your publish method field has a different value, then I would recommend checking this page in the AppVeyor documentation.

The value we just created needs to go into the Server field of the Environment settings.

The value for the Website Name setting is taken from the following field in the publish profile:

msdeploySite="dwCheckApi"

The value for the User Name setting comes from the aptly named “userName” field of the publish profile:

userName="$dwCheckApi"

The value for the Password setting comes from the “userPWD” field:

userPWD="CorrectHorseBatteryStaple"

no, this isn’t the password for my publish profile

and will be encrypted by AppVeyor when we hit save.

The value for the “Artifact to deploy” field comes from the atrifacts section of our deploy script:

artifacts:
- path: '\src\bin\Debug\netcoreapp1.1\publish'
name: WebSite
type: WebDeployPackage

We instructed AppVeyor to build an artifact called “WebSite”, so that’s what we’ll use for our publish Environment.

Make sure the check the following checkboxes:

  • ASP.NET Core application
  • Force restarting ASP.NET Core application on deploy

which will appear after you check “ASP.NET Core application”

  • Take ASP.NET application offline during deployment

Then click “Add Environment”

New Project

Now that we’ve added an environment, we need to add a new project which will use that new environment in conjunction with our appveyor-deploy.yml file to deploy everything for us.

Head back to the projects page in AppVeyor

or click this link

and click on “New Project”

just like we did when setting up our original project, last time

Select the repository we’re going to deploy from the list.

AppVeyor Add Project Select Repo Add Button
I’m working on dwCheckApi, so I’ll choose that

don’t worry that we’re adding a duplicate project just yet, we’re about to change the name of it

Once the project has been added, we’ll be taken to the Settings page for that project. We really only need to change a few settings. First, give it a suitable name as it will have defaulted to the name of the repository

I chose “dwCheckApi-Deploy” for mine, but you can choose whatever you like

then scroll down to “Custom configuration .yml file name” and enter appveyor-deploy.yml (which is the name of our deployment yml). then click “Save”.

Here is what my deploy project looks like:

appVeyor deploy project
Click to enlarge

Once that has been saved, all that remains it to commit your appveyor-deploy.yml file and watch the build and deploy happen.

Caveat

At the time of writing, dwCheckApi only has one branch (i.e. master). When I committed my appveyor-deploy.yml to that branch, both projects in AppVeyor fired, which meant that I had a separate build and deploy of my application (one get’s queued whilst the other is processed).

appveyor multiple builds at once

This is fine for me, but you might want to revisit your appveyor.yml file (i.e. the non-deploy file) ensuring that it builds the correct branch for your circumstances.

Deploy

Once the deploy project has started, we can head to the project screen and see the build, test, and deploy steps in progress; it should look a little like this:

appveyor build and reploy output
Just like last time, this image is pretty big. Click for the full version

And heading over to your hosting environment will show the new version of the code.

in my case, heading to dwcheckapi.azurewebsites.net

et voilà

.NET Core And Azure Specific Caveat

In our environment settings, we instructed AppVeyor to take our application offline while deploying and restart it once it has finished deploying. While AppVeyor is doing it’s magic

and by that, I mean deploying the latest build

and for a short while after

depending on your application’s startup tasks

you (or your users/customers) might not be able to access the application – any requests for the application will receive the app_offline.html page whilst the application is restarting.

this can be customised by adding an “app_offline.html” file into the “www_root” directory and adding the static files service in your startup class.

Just something to bear in mind.

Conclusion

We’ve taken a pre-existing AppVeyor project and added a deployment step to it.

Ok, technically we’ve added a separate project for deployment, but I prefer to do that. That way, you’re only deploying changes to a specific branch.

even though the demo repository that I chose only has one branch, but you should be able to see where this is useful

Have you built, tested, and deployed anything with AppVeyor yet? If not, why not? It’s really simple to get going, and a nice alternative to things like Octopus Deploy.

 

A .NET developer specialising in ASP.NET MVC websites and services, with a background in WinForms and Games Development. When not programming using .NET, he is either learning about .NET Core (and usually building something cross platform with it), speaking Japanese to anyone who'll listen, learning about languages, writing for his non-dev blog, or writing for a blog about Retro Gaming (which he runs with his brother)