How we deploy this website

Mickaël Derriey
• 5 min read

You can always learn something from how other people do things, so we're giving you a peek into how we build and deploy the site you're currently looking at.

Azure DevOps for builds and deployments

We use Azure DevOps for work management and our git repository, so it makes sense to also use the built-in build and release capabilities. Also, having an all-in-one product gives us a good level of traceability; when promoting a release from an enironment to another, we can see which work items have been worked on and what we're about to release.

All in Azure

The application is based on Umbraco so it doesn't need that many moving parts; it's comprised of:

  • an Azure App Service for running the web application, along with Application Insights for monitoring;
  • an Azure SQL instance to persist the content; and
  • an Azure Blob Storage account to store the media files

In terms of environment, it's also lean: a Dev and a Prod instance on their own infrastructure.

The one difference is that we're making use of App Service slots. This means we have two instances of the web application running against the same Azure SQL instance. We'll see later what this means for deployments and content type definitions.

Development and build processes

We work on short-lived feature branches, created off the master branch. When we're ready for someone to review our work, we create a pull request, which kicks off a CI (Continuous Integration) build. One particularity of this build is that it precompiles our Razor views to make sure they're fine. This step is done only in the CI build which runs on pull requests because Umbraco doesn't play well with precompiled views.

When the pull request is merged in master, the CD (Continuous Deployment) build is kicked off automatically. This will build the application and package it so we can deploy it on to our different environments.

At this stage, we're making transformations in the configuration files by swapping development values with tokens — like #{config.umbracoTimeoutInMinutes}# — that will be replaced at release time. This encompasses the Build once, release many mantra, making sure we're deploying the same binaries across different environments, instead of having multiple packages with different configuration values.

While the MSBuild Web Publishing Pipeline (WPP for short) automatically applies transformation for the web.config, they're not supported by default on other config files. Most of Umbraco config files are in a config directory, so we used Slow Cheetah, a NuGet package that extends the MSBuild process to cater for those configuration files. It makes use of the same XDT syntax, so we just needed to add a couple of MSBuild attributes to both the transformation file as well as the target file:

<Content Include="config\uSyncBackOffice.Config">
<None Include="config\uSyncBackOffice.Release.Config">

The release pipeline

We have 3 environments defined in Azure Pipelines:

  • the Dev instance;
  • the Staging slot in Production; and
  • the Production slot in Production

The process for the first two is very similar:

  • provision the infrastructure through an ARM template, using an AAD service principal that targets a specific resource group;
  • unzip the WebDeploy package created via the CD build pipeline;
  • replace the tokens in the config files using the very useful Replace tokens task;
  • rezip the contents of the WebDeploy package in a new .zip file; and
  • deploy the package to Azure App Service

The process for the third and last environment is much simpler, as we only need to perform a slot swap so that the Staging version becomes the Production one.

How we synchronise the content type definitions

We don't want to have to manually create content types on each environment, so we used uSync for that.

At development time, uSync listens to changes and persists content types definitions in XML files. By default, when Umbraco starts, uSync will import the content types, making the appropriate changes.

While this works great at development time, we encountered an issue with this model. The Production environment has two slots, each running their own version of the application, which means there can potentially be two sets of content types competing with each other. Every time one slot restarts, the import process kicks in, and potentially makes undesirable changes causing data loss.

We decided to disable the automatic import process on application startup when the app is deployed in Azure, and instead start it manually at the end of each deployment. The easiest way to do this was to run that import process from the Azure DevOps agent, but we found it not suitable because of the latency between the agent and the Azure SQL instance, as the process took around 15 minutes. The alternative was to start it directly on the App Service by using some Kudu APIs to upload a batch file and start running it. Doing this is supported by the Azure App Service Deploy task, which abstracts interfacing with Kudu. This reduced the import process down to 25 seconds.

Another area we need to carefully consider is destructive operations, such as removing a field from a content type. Again, because we have two versions of the application running against the same database, we could find ourselves in a position where we remove a field, deploy that to the Staging slot, but the Production slot still references it, causing runtime errors. While we haven't yet been in this situation, our solution is to adopt a two-step approach:

  • stop using the field without removing it first. Generally by no longer showing a specific piece of information on a page; then
  • once the change has been deployed to all environments, release a new version where the field is now removed

Mickaël Derriey

I’m a French software developer living in Sydney, Australia. I left France in 2015 to join the amazing team at Readify. I’m currently working as a Senior Consultant with fantastic and very talented people. If you’re interested in knowing more about what we can do, feel free to contact us.