Over the past ten years, Azure has become one of the most prominent cloud computing platforms available, rivaled only by AWS. Part of Microsoft’s suite of Azure services, Azure web apps provides a packaged environment for hosting web applications built in many languages. Because this environment is fully managed by Azure, developers have limited options for control. One of the limitations involves the process of deploying applications to the hosting environment, which allows only for the connection of a remote repository while Azure takes over the whole deployment process.

In this tutorial, you will learn how to build a custom pipeline into the deployment workflow of an Azure web application, regaining full control of the process so you can run tests before deployment.

Prerequisites

To follow this post, a few things are required:

  1. Node.js installed on your system (version >= 10.3)
  2. An Azure account
  3. A CircleCI account
  4. A GitHub account

With all these installed and set up, it is time to begin the tutorial.

First, here is the strategy we will use to create the custom deployment:

  • Push our project to a remote repository
  • Create a specific deployment branch on the repository for Azure to use for triggering deployments
  • Build a pipeline to the main branch for running tests
  • If the tests pass, automatically push to the deployment branch for Azure to pick up the changes and deploy the application

Simple, right?

Creating the Azure web app

Go to your Azure Portal dashboard and click Create a resource to create a new service instance. From the Popular list, click Web App. You can also use the search box.

Create Web App - Azure

On the Create Web App page, select an Azure subscription and a resource group. Resource groups are a way of labeling Azure services that are related. You can create a new group or choose one from the list on the page.

Next, go to the Instance Details section and enter a name for your web app. The default .azurewebsites.net address must be unique across Azure. Also, make sure the name follows standard URL naming rules. For this tutorial, use the name node-api.

Fill in the next set of options:

  • Publish: Code
  • Runtime stack: Node 12 LTS (as we would be hosting a Node.js application)
  • Operating System: Linux
  • Region: Select the one closest to your location or any preferred option (Central US for the tutorial)

Create Web App Options - Azure

Leave the default for the rest of the options.

Click Review + create. On the Review page, confirm the option you selected, then click Create. Azure will begin setting up our web app environment. When the process is done, you will be directed to your web app dashboard. If not, click Go to Resource.

Setting up the Node.js project

Next, we need to clone the project we want to test and deploy to the web app we just created. For this tutorial, we are using a basic Node.js API with a single endpoint, which returns an array of todo objects. Our project also contains a test suite for testing the endpoint. Choose a location on your system for the project and run:

git clone --single-branch --branch base-project https://github.com/coderonfleek/node-azure-web-app.git

When the project has been cloned to your system, go to the root of the project and install dependencies:

cd node-azure-web-app
npm install

You can now run the application using the npm start command. This command starts the application at the address http://localhost:1337. Once the application is up and running, direct your browser to http://localhost:1337/todos and you will find the list of todos.

Todos Endpoint - Node App

Stop the application at the command line using Ctrl + C.

Run the application tests:

npm run test

The output in your CLI will show that the tests have passed.

Run tests - Node App

Next, run the rm -rf .git command at the root of the project to remove any contained .git history. Push the project to GitHub. Make sure that this is the GitHub account connected to your CircleCI account.

Go to your remote repository and create a deploy branch.

deploy branch - GitHub

This branch will be dedicated to the deployments connected to Azure web apps.

Connecting the deployment branch to Azure

We need to do some set up so that when new changes are pushed to this branch, Azure will trigger a deployment. Return to your web app page and click Deployment Center.

Note: Do not use the menu labeled Preview.

On the Deployment Center page, select GitHub, and then click Authorize at the bottom of the page.

Next, select App Service build service from the Build Provider section and click Continue. You will be prompted to select your GitHub user, organization, and branch.

Azure repo configuration - GitHub

Click Continue to go to the Summary page. Click Finish to complete the process. Azure will then make an initial deployment of the application to the web app. When that is done, visit the /todos endpoint on your web app URL. From the Azure web page, the URL is accessible when you click Browse.

Todos App live - GitHub

Setting up the project on CircleCI

Now, go to the Projects page on the CircleCI dashboard.

Add Project - CircleCI

Click Set Up Project.

Add Config - CircleCI

On the setup page, click Use Existing Config to indicate that you are adding a configuration file manually, not using the sample. You will be prompted to download a configuration file for the pipeline or start building.

Build Prompt - CircleCI

Click Start Building. This build will fail because you have not yet set up the configuration file.

Configuring write access to GitHub

The final step in the deployment strategy is to automatically push to the deploy branch after the tests have passed. To do this, you will need authenticated write access to your GitHub repository. Fortunately, CircleCI provides a way to add a User API Key to make that happen. On your project, go to Project Settings -> SSH Keys. In the User Key section, click Authorize with GitHub to make the connection.

User API Key - CircleCI

Once CircleCI and GitHub are connected, an Add User Key button is added to the User API Key section. Click this button to generate a “fingerprint” that you can use later on in the deployment pipeline. Copy it and keep it in a safe location.

Because the fingerprint will be used in the pipeline script along with your GitHub email and username, it is safer to put these in environment variables. On the side-menu of your project settings, click Environment Variables and add these three environment variables:

  • GITHUB_EMAIL: The email of your connected GitHub account
  • GITHUB_USERNAME: Your GitHub username
  • GITHUB_FINGERPRINT: The authentication fingerprint generated earlier

Writing the test and deployment script

Finally, it is time to write the deployment script. This script has four parts:

  • Checkout code from the main branch
  • Install application dependencies
  • Run tests
  • Push updates to deploy branch

To take care of the last step, we will use an npm userland module to write a deploy script in package.json. Using the module allows us to avoid performing raw git operations in our pipeline script. The gh-pages package will be used. Usually this package is used when deploying static sites to GitHub pages by pushing files to a dedicated gh-pages branch. Luckily for us, the package is easily configurable to push files from one branch to another in any repository.

Install this package at the root of the project:

npm install gh-pages --save-dev

Next, add the deploy script in the package.json file:

"scripts" : {
  ...
  "deploy" : "npx gh-pages -b deploy --message '[skip ci] Updates' -d ./"
}

This script invokes gh-pages using npx to push files from the main branch to the deploy branch. The --message parameter of [skip ci] Updates is added so that CircleCI does not re-run the pipeline when changes are pushed to this branch.

Now you can begin writing the pipeline script. At the root of your project, create a folder named .circleci and a file named config.yml within it. Inside the config.yml file, enter:

version: 2.1
jobs:
  build:
    working_directory: ~/repo
    docker:
      - image: circleci/node:10.16.3
    steps:
      - checkout
      - run:
          name: update-npm
          command: "sudo npm install -g npm@5"
      - restore_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
      - run:
          name: install-packages
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
          paths:
            - ./node_modules
      - run:
          name: Run tests
          command: npm run test
      - run:
          name: Configure Github credentials
          command: |
            git config user.email $GITHUB_EMAIL
            git config user.name $GITHUB_USERNAME
      - add_ssh_keys:
          fingerprints:
            - $GITHUB_FINGERPRINT
      - run:
          name: Deploy to Azure Web App
          command: npm run deploy

Here is what is happening in this file:

  • An appropriate image is first pulled in and the code checked out to the working directory
  • Dependencies are installed and the node_modules folder cached.
  • npm run test is called to run the project tests. If the tests pass, GitHub credentials are configured using the appropriate environment variables and the ssh key is added via the fingerprint environment variable
  • Finally, running npm run deploy invokes the deploy script to push changes to the deploy branch for Azure to pick up and deploy the application to the web app.

To show changes to our app, add one more todo object to the array in todos.js:

module.exports = [
  ......,
  {
    id: 4,
    task: "Make Dinner"
  }
];

Next, update the test suite in __tests__/apiTest.js to check that four todo objects are returned from the collection:

expect(res.body.length).toBe(4);

Save changes to the project and commit to the main branch in your remote repository. You have a successful deployment!

Deployment Successful - CircleCI

Click the workflow to review the details.

Deployment Details - CircleCI

Now, head over to the Deployment Center on your Azure web app. There is a deployment process being triggered. It starts as pending.

Deployment Pending - Azure

Then it runs.

Deployment Running - Azure

When it is done, it displays “Success (Active)” to show that you have the lastest changes to your application up and running.

Deployment Success - Azure

Now, if you visit the /todos endpoint again on your deployed web app (refresh if you need to), you should see the newly added todo object:

Todos Updated - Web App

Conclusion

DevOps is all about providing, architecting, and integrating solutions. In this tutorial, we have been able to combine the power of CircleCI in building highly customizable CI/CD pipelines with deployments to the Azure web apps hosting platform. Architects and engineers who want to host their applications on Azure can take full advantage of CircleCI’s robust pipelines.

Happy coding!


Fikayo Adepoju is a Full-stack developer, technical writer and tech content creator proficient in Web and Mobile technologies and DevOps with over 10 years experience developing scalable distributed applications. With over 40 articles written for CircleCI, Twilio, Auth0 and The New Stack blogs and also on his personal Medium page, he loves to share his knowledge to as many developers as would benefit from it. You can also check out his video courses on Udemy.