Continuous deployment for Azure web apps
Fullstack Developer and Tech Author
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:
- Node.js installed on your system (version >= 10.3)
- An Azure account
- A CircleCI account
- 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.
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
: CodeRuntime stack
: Node 12 LTS (as we would be hosting a Node.js application)Operating System
: LinuxRegion
: Select the one closest to your location or any preferred option (Central US
for the tutorial)
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
.
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.
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.
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.
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.
Setting up the project on CircleCI
Now, go to the Projects page on the CircleCI dashboard.
Click Set Up Project.
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.
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.
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 accountGITHUB_USERNAME
: Your GitHub usernameGITHUB_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 thessh
key is added via the fingerprint environment variable- Finally, running
npm run deploy
invokes thedeploy
script to push changes to thedeploy
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!
Click the workflow to review the details.
Now, head over to the Deployment Center on your Azure web app. There is a deployment process being triggered. It starts as pending.
Then it runs.
When it is done, it displays “Success (Active)” to show that you have the lastest changes to your application up and running.
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:
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!