Feature deployments, the holy grail?

Folkert Jongbloed
7 min readMar 4, 2021

During sprint development, there are some moments in time when one or more features will be merged and deployed to a test environment. When this version is released to the test environment, QA can start testing.

In some cases, it might happen that the feature, the developer is working on, is not fully specified yet and needs additional detailing. It might require a review by the PO, or the developer would like to show some stuff which he is working on. In this case, merging in the main branch is not an option.

This is the point where you might consider Feature Releases. These are releases which are targeting a single feature, often a single functionality of design element which needs a review before being merged into the main branch.

Adding these feature releases to the project is not that hard and won’t cost you tons of money if set up correctly.

Our Project

Let’s start with a sample project, which is just a very simple page. The app just serves an Html page using Node and Express. It will be shipped as a Docker image. It will result in a structure like this:

Folder structure

When I run the app, the page will look like this:

My awesome app!

It doesn’t make sense to deploy every change to an environment. So, the best moment to capture is the Pull Request event. This event is created when a pull request is created in Azure Devops.
When a developer is working on a feature branch and would like to merge it in the main branch, the PR is the only way to accomplish this (assuming direct commits are prevented by setting up correct policies).

Instead of just building and testing the app, we need to create additional tasks to provision and deploy this app so QA or PO can test this feature before it’s merged into the main branch. Let’s check the pipeline which is triggered upon a pull request:

Important lines:
#5: pull request trigger on the main branch
#24, #33, #44: different stages: Build, Create, Remove

As you can see, the feature release pipeline will:

  1. Builds a Docker image from the code
  2. Pushes the Docker image to the registry.
  3. Creates infra, in this case: a resource group, service plan, and web app.
  4. Bind web app to the Docker image
  5. Remove all resources

The final stage, which is responsible for the removal of the infra is fired when the step is approved in Azure Devops. Until this step is not approved, the pipeline will not proceed to this stage hence, the web app will be available.

I will list the different stages here:

Build stage

This is of course the first stage, it must be built. The only challenge is the name of the process which will be used for additional resource naming. Obviously, the branch name would be the best choice (assuming feature branches are linked to user story IDs). However, when a PR is triggered, the branch name being used is not really the branch name itself. This is the source branch while the main branch is the target branch. The branch itself is a temporary branch that is created with the main branch and feature branch merged (virtually).

Azure devops has numerous system variables which can be used in the pipeline. The one we would like to use is the ‘Build.SourceBranchName’. But when used in a Pull Request, the value of this variable will be: ‘merge’
Not really the name, I’m looking for.

The source branch is not really defined as a variable but some values can be used, for example, the: ‘System.PullRequest.SourceBranch’. The value for this variable is: refs/heads/feature/RA-1234. I only need to split this value into the correct value. Because this needs to be re-used, let’s make a template for this step:

Important lines:
#8 this line is getting the variable that returns the full path of the branch
#9 splits the path and filters out the last value
#10 sets the branch name as a variable that can be used in additional steps.

Now we have the branch name, let’s start with the build template:

Important lines:
#12 this is the template that is responsible for getting the correct branch name.
#23 the Docker image is tagged with the branch name.

As you can see, this is not really an impressive step. The only important part of this task is the tag that is used for the Docker image. The tag is based upon the output of the previous task, which is the ID of the Feature/Story.

When this task is finished, a Docker image is pushed to the Azure Container Registry, tagged with the ID of the Feature:

Infra stage

Now the build stage is finished, it’s time for provisioning the resources to run the Docker image.

To accomplish this we need to:

  1. Create a resource group
  2. Create a service plan
  3. Create a web app
  4. Bind web app to the Docker image

Because all the tasks are in the same pipeline we can include the template task to filter the branch name and use this name for the resources.

As you can see, all resources inherit the same name set in the pipeline. This is important because we need to reference all resources by this name.

Nothing really fancy, just an ordinary infra template. The task at #19 is something I’m using often. Setting all variables in a single step. After this step, it’s just a matter of using these variables by name instead of concatenating these different parts every time.

Write variable:
Write-Output "##vso[task.setvariable variable=resourceGroup]${{ parameters.companyPrefix }}-$(sourceBranch)"
Read variable:
$(resourceGroup)

The final result in Azure:

And in the browser:

Awesome!

Time for testing

Infra has been created and the app is running, so QA or PO can start testing or approving the feature. Because of the setup of the pipeline and the definition of the Feature ID, it is possible to add changes (commits) to the branch. Let’s say, the used colors for the background are too flashy, let’s change it in the feature branch:

CHange in GitKraken

Commit and push the change to the feature branch. Because the branch is already in a PR state, the pipeline is triggered:

Triggered pipeline run

This run will just rerun all steps. Because the infra already has been created (using the same feature branch ID) it will run faster:

Logs in Azure Devops
Pipeline overview

And the result within a couple of minutes:

Approval

The trick in the pipeline is the ‘approval set on the last stage. The last stage is responsible for finalizing the pipeline and merge the feature in the main branch. But before the final step, all resources must be cleaned first for these are not required anymore. To accomplish this, the final step is defined as a ‘deployment’ job. Deployment jobs can be linked to an ‘Environment’ that has additional options in Azure Devops, like approval:

Because of this approval, the pipeline stops before the stage and will wait for approval to continue:

Waiting for approval

When this step is approved, the pipeline will continue with the last stage:

Again, I’m using the template to get the Feature branchID for correct reference. The actual task is quite simple: just remove the resource group which will remove all including resources, so we only need to get the name of the resource group. Don’t forget the ‘-y’ flag because this command will not continue for it requires confirmation.

When I approve this step, the pipeline will continue:

Approval

And will proceed to the last stage which will remove the resource group completely. When the pipeline has finished, the branch will be merged to the main branch and will possibly trigger the build and release pipeline to the actual Test environment.

Keep in mind that these az CLI commands, like the removal of the resource group, are ‘fire and forget’ commands. The pipeline will show a ‘success’ status while Azure will start working on the actual removal itself which will take some minutes.

Of course, this is the most simple way of working with Review Apps/Feature Deployments. In more complex scenarios you might consider duplicating databases, add additional services, etc. This is just created to get you started!

--

--

Folkert Jongbloed

I am working as a Software Architect/DevOps specialist for Valtech Netherlands.