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 provide 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 >= 20.0.0)
- 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 you will use to create the custom deployment pipeline to Azure:
- Push your project to a remote GitHub repository
- Configure CircleCI to run tests automatically on every push to the main branch
- If the tests pass, use the Azure CLI to deploy the application to Azure Web App via
az webapp deploy
- This gives us full control of the CI/CD workflow while keeping deployments fast and reliable.
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 your Azure subscription and a resource group. Resource groups are used to organize and manage related Azure services. You can choose an existing group or create a new one.
Next, in the Instance Details section, enter a name for your web app — for example, node-cd-api
. Azure will automatically generate a default hostname in the format:
https://<AppName>-<HASH>.<Region>.azurewebsites.net
So if your app name is node-cd-api
and it’s deployed to East US, the resulting URL might look like:
https://node-cd-api-hbdufzepe6g5febr.eastus-01.azurewebsites.net
This new format helps prevent subdomain takeover by appending a unique hash and region to your app name.
Fill in the next set of options:
Publish
: CodeRuntime stack
: Node 20 LTS (you will be hosting a Node.js application)Operating System
: LinuxRegion
: Select the one closest to your location (or any you prefer). This tutorial usesEast US
.
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 the 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, clone the project you want to test and deploy to the web app you just created. For this tutorial, you are using a basic Node.js API with two endpoints:
-
GET /
returns a simple message confirming the app is running -
GET /todos
returns an array oftodo
objects
The project also includes a test suite to validate these endpoints.
Choose a location on your system and run:
git clone --single-branch --branch base-project https://github.com/CIRCLECI-GWP/node-cd-api.git
Once cloned, navigate into the project directory and install dependencies:
cd node-cd-api
npm install
You can now start the application using:
npm start
By default, the app runs at http://localhost:3000
. Once it’s up, try visiting the following URLs in your browser:
-
http://localhost:3000/
returns:"App is up and running!"
-
http://localhost:3000/todos
returns the list of todos in JSON format
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.
> cd-nodejs-azure-vm@1.0.0 test
> jest
PASS __tests__/apiTest.js
✓ Fetch Todos (16 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.325 s, estimated 6 s
Ran all test suites.
Creating an Azure service principal account
Microsoft Azure recommends using a service principal account for accessing Azure resources in automated or non-interactive scenarios like CI/CD pipelines.
A service principal is a special identity created for use by applications, scripts, or tools rather than a human user, allowing secure access to Azure resources via the CLI.
In this tutorial, you will create a service principal that CircleCI will use to authenticate and deploy the application securely from the pipeline.
To create one, run the following command:
az ad sp create-for-rbac --name "circleci-service-account" --role contributor \
--scopes /subscriptions/<your-subscription-id>
Replace <your-subscription-id>
with your Azure subscription ID.
Note: If you don’t remember your Azure Subscription ID, retrieve it with:
az account list --output table
Copy the SubscriptionId
of the subscription you want to use.
Azure returns credentials like:
{
"appId": "xxxxx",
"password": "xxxxx",
"tenant": "xxxxx"
}
Take note of these, you’ll use them as environment variables in CircleCI. The appId
is the Client ID, the password
is the Client Secret, and the tenant
is the Tenant ID.
These credentials will allow CircleCI to deploy to your Azure Web App securely using the Azure CLI.
Setting up CI/CD with CircleCI
Create a .circleci/config.yml
file in the root of your project and add the following configuration script to it:
version: 2.1
orbs:
azure-cli: circleci/azure-cli@1.3.2
node: circleci/node@7.1.0
jobs:
build-and-test:
executor: node/default
steps:
- checkout
- run:
name: Install dependencies
command: npm install
- run:
name: Run tests
command: npm test
deploy:
executor: node/default
steps:
- checkout
- azure-cli/install
- azure-cli/login-with-service-principal:
azure-sp: AZURE_CLIENT_ID
azure-sp-password: AZURE_CLIENT_SECRET
azure-sp-tenant: AZURE_TENANT_ID
- run:
name: Set subscription
command: |
az account set --subscription $AZURE_SUBSCRIPTION_ID
- run:
name: Zip app and deploy
command: |
zip -r app.zip . -x "*.git*" "node_modules/*"
az webapp deploy \
--resource-group $AZURE_RG \
--name $AZURE_APP_NAME \
--src-path app.zip \
--type zip
workflows:
version: 2
build-test-and-deploy:
jobs:
- build-and-test
- deploy:
requires:
- build-and-test
In the configuration above, CircleCI automates the testing and deployment of the Node.js web application to Azure Web App. The build-and-test
job runs in a Node.js Docker environment, installs the project dependencies using npm install
, and executes the test suite with npm test
. Once tests pass, the deploy
job takes over to handle deployment. It checks out the project code, installs the Azure CLI, and logs into Azure using a service principal with credentials stored in environment variables. After setting the active Azure subscription, it zips the application (excluding unnecessary files) and deploys it using the az webapp deploy
command. The workflow ensures that deployment only happens if the test job succeeds, enabling a secure and efficient CI/CD pipeline.
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.
Now, go to the Projects page on the CircleCI dashboard. Select the associated GitHub account to add the project and click Set Up Project. You will be prompted to enter the branch housing your configuration file. CircleCI will detect the .circleci/config.yml
file and start building the project.
This will trigger the pipeline, but it will fail because you haven’t set up the environment variables for the required credentials in CircleCI yet:
Setting up environment variables in CircleCI
Head over to your project settings on the CircleCI dashboard and navigate to the Environment Variables section. Here, you’ll define the values CircleCI needs to authenticate with Azure and deploy your app.
Add the following environment variables:
-
AZURE_CLIENT_ID – the App ID of your service principal
-
AZURE_CLIENT_SECRET – the password (secret) of your service principal
-
AZURE_TENANT_ID – the tenant ID of your Azure Active Directory
-
AZURE_SUBSCRIPTION_ID – your Azure subscription ID
-
AZURE_RG – the name of the resource group containing your web app
-
AZURE_APP_NAME – the name of your Azure Web App
Once these are in place, trigger the pipeline again by pushing a commit or re-running the last job. If everything is correctly configured, the pipeline should pass and deploy your app to Azure.
To retrieve the deployed app’s URL, you can run:
az webapp show \
--resource-group $AZURE_RG \
--name $AZURE_APP_NAME \
--query defaultHostName \
--output tsv
This will return a URL like:
https://node-cd-api-hbdufzepe6g5febr.eastus-01.azurewebsites.net/
You can now visit your live application. Try editing the todos.js
file to add a new todo item, push your changes to the main
branch, and watch as CircleCI runs tests and redeploys the updated app automatically.
Conclusion
DevOps is all about designing, automating, and integrating solutions to deliver software reliably and efficiently. In this tutorial, you combined the power of CircleCI’s flexible CI/CD pipelines with the scalability of Azure Web Apps to create a seamless deployment workflow. Whether you’re a developer, architect, or engineer, this approach helps you ship updates faster while keeping control over your deployment process.