Build a CI/CD Pipeline with Azure DevOps

Automate building and deploying a Node.js app with Azure Pipelines.

Introduction

30 Second Summary

Every time you fix a bug or add a feature, getting that change from your laptop to a live website involves multiple manual steps. Miss one, and your users are stuck waiting.

In this project, you will build an automated CI/CD pipeline that builds and deploys a Node.js web app every time you push code to your repository.

What You'll Build

A fully automated pipeline in Azure DevOps that builds a Node.js web application and deploys it to Azure App Service on every code push.

By the end of this project, you'll have:

  • A Node.js Express web app hosted in Azure Repos with Git version control.
  • A YAML pipeline that automatically builds and deploys your app on every push to the main branch.
  • A live Azure App Service running your app at a public URL that updates automatically when you push code.
  • Secret Mission: Add a staging deployment slot and configure your pipeline to deploy to staging first, then swap to production for zero-downtime deployments.

Are there any prerequisites?

Basic familiarity with Git (commit, push) is helpful. No prior Azure or DevOps experience is required.

Set Up Your Accounts and Tools

Before you can build a pipeline, you need the tools that power it. Your web app runs on Node.js, your code lives in Git, and your pipeline lives in Azure DevOps.

In this step, you will install Node.js and Git on your machine, set up VS Code as your code editor, and create an Azure DevOps project to house your pipeline.

In this step, get ready to:
  • Install Node.js and Git on your machine.
  • Set up VS Code as your code editor.
  • Create your Azure DevOps organization and project.
Install Node.js

Node.js is the JavaScript runtime your web app needs to run. It also includes npm, the package manager you will use to install libraries like Express.

  • Open your terminal. Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or Command Prompt (Windows) and press Enter to open it.
  • Check if Node.js is already installed by running this command:
node --version

✔️ I see version 20 or higher

Node.js is ready to go. The pipeline uses Node.js 20.x on the build server, and any version 20 or above works for local development.

  • Confirm npm is also available by running:
npm --version

You should see a version number printed. Move on to the next substep.

ⓧ I see an older version

Your installed version is too old for this project. You need version 20 or higher.

  • Go to nodejs.org in your browser.
  • Click the LTS download button to get the latest Long Term Support version.
  • Run the installer and accept the default options.
  • Close and reopen your terminal, then run node --version again to confirm you now see version 20 or higher.

macOS users with an older system Node

If your system still shows an old version after reinstalling, the new installer may not have updated your PATH. Close all terminal windows and open a fresh one. If the version still shows as old, use the full path (e.g. /usr/local/bin/node --version) or install via a version manager like nvm.

ⓧ Command not found

Node.js is not installed yet. Let's fix that.

  • Go to nodejs.org in your browser.
  • Click the LTS download button to download the installer for your operating system.
  • Run the installer and accept the default options on each screen.
  • Close and reopen your terminal, then run node --version again to confirm you see a version number.
  • Verify npm is available by running:
npm --version

You should see a version number. npm is included with every Node.js installation.

Still not seeing a version number?

Make sure you closed and reopened your terminal after installation. New PATH entries only take effect in a fresh terminal session.

On Windows, try opening a new Command Prompt window (not the same one). On macOS, open a new Terminal tab.

help me troubleshoot why node --version is not working after installing Node.js

Install Git

Git is the version control system that tracks your code changes. Azure DevOps uses Git repositories to store your project files.

  • Check if Git is already installed by running this command:
git --version

✔️ I see a version number

Git is ready. Move on to configuring your identity below.

ⓧ Command not found

Git is not installed yet. Let's install it.

  • Go to git-scm.com in your browser.
  • Click the download button for your operating system.
  • Run the installer and accept the default options on each screen.
  • Close and reopen your terminal, then run git --version again to confirm you see a version number.

Now configure Git with your name and email. These values appear on your commits so Azure DevOps knows who made each change.

  • Set your Git username by running this command:
git config --global user.name "[[YOUR_NAME="Your Name"]]"
  • Set your Git email by running this command:
git config --global user.email "[[YOUR_EMAIL="your@email.com"]]"

What do these commands do?

The --global flag saves these settings for all Git repositories on your machine. Every commit you make will include this name and email, so collaborators can see who authored each change.

Getting a permission error?

On macOS, you should not need sudo for git config --global. If you see a permissions error, check that your home directory is writable. On Windows, make sure you are running Command Prompt normally (not as Administrator for this step).

help me fix a permission error when running git config --global

Finally, install VS Code. This is the code editor you will use to write your application and pipeline files.

  • Go to code.visualstudio.com in your browser.
  • Click the download button for your operating system and run the installer.
  • Once installed, open VS Code. Press Cmd+Space (macOS) or the Windows key (Windows), type Visual Studio Code, and press Enter.
Create your Azure DevOps account

Azure DevOps is the platform where your pipeline will live. It hosts your code repository, runs your build steps, and deploys your app.

  • Go to dev.azure.com in your browser.
  • Sign in with your Microsoft account. If you do not have one, click Create one and follow the prompts to make a free account.
  • When prompted, create a new organization. Enter a name for your organization (your own name or a team name works fine) and select a region close to you.
  • Click Continue to finish creating the organization.

No credit card needed

Azure DevOps does not require a credit card to get started. The free tier gives you 1 Microsoft-hosted parallel job with 1,800 minutes per month, which is more than enough for this project.

Now create the project that will hold your code and pipeline.

  • Click New Project in the top right corner.
  • Enter my-webapp-pipeline as the project name.
  • Leave the visibility set to Private.
  • Under Advanced, confirm that Version control is set to Git.
  • Click Create.

You should see your new project dashboard with sections like Boards, Repos, and Pipelines in the left sidebar.

Request your free parallel job early

New Azure DevOps organizations may need to request a free parallel job grant before pipelines can run. This request can take several business days to process.

Submit your request now at https://aka.ms/azpipelines-parallelism-request so it is approved by the time you need it in Step 6. If your organization already has pipeline minutes, you can skip this.

Don't see the New Project button?

Make sure you completed the organization creation step first. The New Project button appears on your organization's home page after setup is complete. If you landed on a different page, navigate back to dev.azure.com/your-org-name.

help me find the New Project button in Azure DevOps

Your development environment is ready. Next up, you will build the Node.js web app that your pipeline will deploy.

Create Your Node.js Web App

Your accounts and tools are ready. Now it's time to build the actual web application that your CI/CD pipeline will deploy.

You'll create a simple Node.js web app using Express. Express is a lightweight framework for building web servers in JavaScript.

In this step, get ready to:
  • Initialize a Node.js project and install Express.
  • Write the application code that serves a web page.
  • Test the app locally to confirm it works before deploying.
Initialize the project

Every Node.js project starts with a package.json file that tracks your project's name, version, and dependencies.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir my-webapp-pipeline
cd my-webapp-pipeline
  • Confirm the folder was created by running:
ls

You should see the my-webapp-pipeline folder listed in the output.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Visual Studio Code and press Enter to open it.
  • In VS Code, click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop and select the my-webapp-pipeline folder, then click Open.

Why open the folder in VS Code?

Opening the project folder gives VS Code context about your entire project. The integrated terminal also starts inside this folder, so all commands you run will target the right location.

  • Open the integrated terminal in VS Code by pressing Ctrl+` (backtick key, located above Tab on most keyboards).
  • Initialize your Node.js project by running this command:
npm init -y

What does this command do?

The npm init -y command creates a package.json file with default values. The -y flag skips the interactive questionnaire and fills in sensible defaults based on your folder name.

You should see a JSON output in the terminal showing the generated package.json contents.

  • Install Express by running this command:
npm install express

What is Express?

Express is a minimal web framework for Node.js. It handles incoming HTTP requests and sends responses. You'll use it to serve your web page.

You should see output showing packages being added. The terminal displays a message confirming the install is complete.

Seeing permission errors or network failures?

Make sure you have an active internet connection. If you see EACCES permission errors on macOS, try prefixing the command with sudo.

Help me troubleshoot npm install

Write the application code

Your project structure is ready. Now you'll write the actual server code that responds to web requests.

  • In VS Code's Explorer sidebar on the left, click the new file icon (a page with a plus sign) at the top of the file tree.
  • Name the file index.js and press Enter.
  • Paste the following code into index.js:
// Import the Express framework
const express = require('express');
const app = express();

// Use the PORT environment variable (Azure sets this) or default to 8080
const port = process.env.PORT || 8080;

// Serve an HTML response on the homepage
app.get('/', (req, res) => {
  res.send('<h1>Hello from Azure DevOps CI/CD!</h1><p>This app was deployed automatically using Azure Pipelines.</p>');
});

// Start the server
app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

What does this code do?

This creates a web server with one route. When someone visits the homepage (/), the server responds with an HTML message.

The process.env.PORT || 8080 line is important. Azure App Service injects its own PORT value when your app runs in the cloud. Locally, it falls back to port 8080.

  • Save index.js by pressing Cmd+S (macOS) or Ctrl+S (Windows).

File not appearing in the sidebar?

Make sure you opened the my-webapp-pipeline folder in VS Code, not a parent folder. Check the Explorer sidebar title matches your folder name.

Help me find my index.js file

✔️ Awesome, I've got everything!

Great. Make sure you've saved index.js before moving on.

ⓧ I'd like to double check the full code

Here's the complete index.js file:

const express = require('express');
const app = express();
const port = process.env.PORT || 8080;

app.get('/', (req, res) => {
  res.send('<h1>Hello from Azure DevOps CI/CD!</h1><p>This app was deployed automatically using Azure Pipelines.</p>');
});

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});
Add a start script and test locally

Before deploying anywhere, you need a way to start the app with a single command. The start script in package.json tells Node.js (and Azure) exactly how to run your application.

  • Open package.json in VS Code by clicking on it in the Explorer sidebar.
  • Replace the entire contents of package.json with the following:
{
  "name": "my-webapp-pipeline",
  "version": "1.0.0",
  "description": "A simple Node.js web app deployed via Azure DevOps CI/CD",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.21.0"
  }
}

What changed in package.json?

The key change is the "start": "node index.js" script. This tells npm start to run your server file. Azure App Service uses this exact command to launch your app after deployment.

The description field helps you identify the project later. The dependencies section locks Express to the version you installed.

  • Save package.json by pressing Cmd+S (macOS) or Ctrl+S (Windows).

✔️ Awesome, I've got everything!

Great. Make sure you've saved package.json before testing.

ⓧ I'd like to double check the full code

Here's the complete package.json file:

{
  "name": "my-webapp-pipeline",
  "version": "1.0.0",
  "description": "A simple Node.js web app deployed via Azure DevOps CI/CD",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.21.0"
  }
}
  • Test your app locally by running this command in the VS Code terminal:
npm start

You should see Server running on port 8080 in the terminal output.

  • Open your browser and visit http://localhost:8080.

You should see a heading that says "Hello from Azure DevOps CI/CD!" and a paragraph below it.

Not seeing the page in your browser?

Make sure the terminal still shows "Server running on port 8080" and you haven't closed it. If you see an error about the port already being in use, another process is using port 8080. Stop it or close other terminal sessions and try again.

Check that you saved both index.js and package.json before running npm start.

Help me troubleshoot my local server

  • Once confirmed, stop the server by pressing Ctrl+C in the terminal.

Your web app is built and tested locally. Next up, you'll push this code to Azure Repos so your pipeline can access it.

Push Your Code to Azure Repos

Your app runs perfectly on your local machine. For Azure Pipelines to automatically build and deploy it later, the code needs to live in a repository that Azure DevOps can access.

Azure Repos is a Git hosting service built into your Azure DevOps project. You'll push your local project files there so your pipeline can pick them up in the next step.

In this step, get ready to:
  • Initialize a local Git repository and push your code to Azure Repos.
  • Verify your files are visible in the Azure DevOps browser.
Initialize Git and push to Azure Repos

Before you can push code, you need the clone URL for your Azure Repos repository. This URL tells Git where to send your files.

  • In your browser, open your Azure DevOps project at dev.azure.com.
  • Click Repos in the left sidebar.
  • Since the repository is empty, Azure DevOps displays a page with clone instructions.
  • Click the copy button next to the HTTPS clone URL at the top of the page.

What does the clone URL look like?

The URL follows the format https://dev.azure.com/your-org/my-webapp-pipeline/_git/my-webapp-pipeline. Your organization name replaces your-org.

Your project folder contains node_modules which holds thousands of downloaded dependency files. You do not want to push these to your repository.

  • Create a .gitignore file to exclude node_modules by running this command in VS Code's terminal:
echo "node_modules" > .gitignore

What is a .gitignore file?

A .gitignore file tells Git which files and folders to skip when tracking changes. The node_modules folder is regenerated by npm install, so there's no need to store it in your repository.

  • Initialize a Git repository, add your Azure Repos remote, and push your code by running these commands:
git init
git remote add origin [[REPO_URL="your Azure Repos HTTPS clone URL"]]
git add .
git commit -m "Initial commit"
git branch -M main
git push -u origin main

What do these commands do?

git init creates a new Git repository in your project folder. git remote add origin connects it to your Azure Repos URL.

git add . stages all your files for commit. git commit saves a snapshot of your code. git branch -M main ensures the branch is named main. git push uploads everything to Azure Repos.

Why name the branch main?

Azure DevOps expects your default branch to be called main. The -M flag renames whatever your local branch is called to main so it matches what Azure Repos expects.

When you run the push command, a sign-in window may appear asking you to authenticate with your Microsoft account. Sign in with the same account you used for Azure DevOps.

  • Confirm the push succeeded by checking your terminal output. You should see a message showing your files were uploaded to the main branch.

Push failed or authentication error?

If you see an authentication error, make sure you sign in with the same Microsoft account you used to create your Azure DevOps organization.

If you see "remote origin already exists", run git remote remove origin first, then re-run the git remote add origin command with the correct URL.

help me fix a git push error to Azure Repos

Verify your code in Azure DevOps

Now that you've pushed your code, confirm Azure DevOps received all your files.

  • In your browser, go back to your Azure DevOps project.
  • Click Repos in the left sidebar.
  • Click Files if you're not already viewing the file list.
  • Confirm you can see index.js, package.json, package-lock.json, and .gitignore listed in the repository.

Don't see your files?

Make sure you're looking at the main branch. Use the branch dropdown at the top of the file list to switch branches if needed.

If the repository still looks empty, go back to your terminal and run git log to confirm your commit exists. Then run git push -u origin main again.

help me figure out why my files aren't showing in Azure Repos

Your code is now hosted in Azure Repos and ready for automation. Next up, you'll create the Azure App Service that will host your live web app.

Create the Azure App Service

Your code is hosted in Azure Repos and ready to be deployed. But deployed to where? You need a destination for your web app to live on the internet.

In this step, you will create an Azure App Service resource. This is the cloud server that will run your Node.js app at a public URL.

In this step, get ready to:
  • Create a resource group and App Service in the Azure Portal.
  • Verify the default placeholder page loads at your app's public URL.
Create the App Service in Azure Portal

Azure App Service is the hosting platform where your web app runs. You will configure it with the Free plan so there are no charges.

  • Open your browser and go to portal.azure.com.
  • Sign in with the same Microsoft account you used for Azure DevOps.

What is Azure Portal?

Azure Portal is the web-based management console for all your Azure cloud resources. You will use it to create the App Service that hosts your web app.

  • In the Azure Portal search bar at the top, type app services.
  • Under Services, select App Services.
  • Click Create, then select Web App from the dropdown.

The Create Web App form opens on the Basics tab. Fill it out with the following settings:

  • Under Resource group, click Create new. Type rg-webapp-pipeline and click OK.
  • Under Name, enter a globally unique name for your web app. This name becomes part of your URL, so it must be unique across all of Azure.
  • Under Publish, select Code.
  • Under Runtime stack, select Node 20 LTS.
  • Under Operating System, select Linux.
  • Under Region, select the region closest to your physical location.
  • Under Pricing plan, select Free F1 from the dropdown.

Why a resource group?

A resource group is a logical container in Azure that holds related resources together. By placing your App Service in rg-webapp-pipeline, you can manage and delete everything for this project in one place.

  • Click the Review + create button at the bottom of the page.
  • Review the summary. Confirm the pricing plan shows Free F1.
  • Click Create.

Azure will take a moment to provision your resources. Wait for the deployment to complete.

  • Once deployment finishes, click Go to resource.

Don't see Free F1 in the pricing dropdown?

Some regions may not offer the Free tier for Linux. Try selecting a different region (such as East US or West Europe) and check the pricing dropdown again.

Also verify that the pricing plan dropdown shows Free F1 after creation. If your plan auto-upgraded to Standard, go to your App Service plan settings and scale it back down to Free.

Help me find the Free F1 pricing tier for my Azure App Service.

Verify the app is running

Your App Service is now live. It shows a default placeholder page until you deploy your own code.

  • On the App Service Overview page, find the Default domain field near the top of the page.
  • Click the URL (it looks like your-app-name.azurewebsites.net).
  • Confirm you see the Azure default placeholder page in a new browser tab.

What should I see?

You should see a page saying your web app is running and waiting for your content. This is Azure's built-in placeholder. In a later step, your CI/CD pipeline will replace this page with your actual Node.js app.

Seeing an error page instead?

The App Service can take 1-2 minutes to fully start. Wait a moment and refresh the page. If you still see an error, confirm the App Service status shows Running on the Overview page.

My Azure App Service default page is not loading. Help me troubleshoot.

Your cloud hosting is ready and waiting. Next up, you will write the YAML pipeline that builds and deploys your app automatically.

Write Your YAML Pipeline

Your code is in Azure Repos and your App Service is ready to host it. The missing piece is a set of instructions that tells Azure DevOps exactly how to build your app and deploy it automatically.

Those instructions live in a YAML pipeline file. Azure Pipelines reads this file on every push and executes the steps you define, from installing dependencies to deploying to your App Service.

In this step, get ready to:
  • Create an azure-pipelines.yml file with build steps.
  • Add a deployment task that pushes your app to Azure App Service.
  • Commit and push the pipeline file to Azure Repos.
Create the pipeline file

The pipeline file tells Azure DevOps what to do when new code lands on the main branch. You will define the build steps first: install Node.js, install your app's packages, and zip everything into a deployable archive.

  • In VS Code, create a new file called azure-pipelines.yml at the root of your project (the same level as index.js and package.json).
  • Paste the following code into azure-pipelines.yml:
trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: NodeTool@0
    inputs:
      versionSpec: '20.x'
    displayName: 'Install Node.js'

  - script: |
      npm install
    displayName: 'Install dependencies'

  - task: ArchiveFiles@2
    inputs:
      rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
      includeRootFolder: false
      archiveType: 'zip'
      archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
      replaceExistingArchive: true
    displayName: 'Archive files'

What does this code do?

  • trigger: - main tells Azure Pipelines to run this pipeline every time code is pushed to the main branch.
  • vmImage: 'ubuntu-latest' picks a Linux virtual machine from Microsoft's hosted pool to run your build.
  • NodeTool@0 installs Node.js 20.x on that VM so your app's dependencies can be resolved.
  • npm install downloads the packages listed in package.json (like Express).
  • ArchiveFiles@2 zips your entire project into a single file that Azure App Service can unpack and run.
  • Save azure-pipelines.yml.
  • Confirm the file appears in your VS Code Explorer sidebar at the same level as index.js.

File not appearing in the sidebar?

Make sure you created the file at the project root, not inside a subfolder. The file must be named exactly azure-pipelines.yml (not .yaml or any other extension).

Help me find where azure-pipelines.yml should go in my project

Add the deployment step

The build steps prepare your app, but nothing actually deploys it yet. The AzureWebApp@1 task takes the zipped archive and pushes it to your App Service.

  • In azure-pipelines.yml, add the following task below the Archive files step (at the same indentation level as the other steps):
  - task: AzureWebApp@1
    inputs:
      azureSubscription: '[[SERVICE_CONNECTION="your-service-connection-name"]]'
      appType: 'webAppLinux'
      appName: '[[APP_SERVICE_NAME="your-app-service-name"]]'
      package: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
    displayName: 'Deploy to Azure App Service'

What does this task do?

  • azureSubscription is the name of the service connection that links Azure DevOps to your Azure subscription. You will create this in the next step, so leave the placeholder for now.
  • appType: 'webAppLinux' tells the task you are deploying to a Linux App Service (the one you created in the previous step).
  • appName is the unique name you gave your App Service when you created it in Azure Portal.
  • package points to the zip file that the ArchiveFiles@2 task created in the previous step.

Replace the app name now

You already know your App Service name from the previous step. Replace the appName placeholder with your actual App Service name now. Leave azureSubscription as a placeholder for now. You will fill it in after creating the service connection in the next step.

  • Save azure-pipelines.yml.

Seeing red squiggles in VS Code?

YAML is sensitive to indentation. Each - task: line must start with exactly two spaces of indentation under steps:. Check that your new task lines up with the - task: ArchiveFiles@2 line above it.

Help me fix YAML indentation errors in my pipeline file

✔️ Awesome, I've got everything!

Great. Double check you have saved azure-pipelines.yml with all four steps listed under steps:.

ⓧ I'd like to double check the full code

Here is the complete azure-pipelines.yml file. Compare it with yours line by line:

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: NodeTool@0
    inputs:
      versionSpec: '20.x'
    displayName: 'Install Node.js'

  - script: |
      npm install
    displayName: 'Install dependencies'

  - task: ArchiveFiles@2
    inputs:
      rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
      includeRootFolder: false
      archiveType: 'zip'
      archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
      replaceExistingArchive: true
    displayName: 'Archive files'

  - task: AzureWebApp@1
    inputs:
      azureSubscription: '[[SERVICE_CONNECTION="your-service-connection-name"]]'
      appType: 'webAppLinux'
      appName: '[[APP_SERVICE_NAME="your-app-service-name"]]'
      package: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
    displayName: 'Deploy to Azure App Service'
Commit and push the pipeline file

Azure DevOps reads the pipeline file from your repository. Until you push it, the pipeline does not exist.

  • Add, commit, and push your new file by running these commands in your terminal:
git add azure-pipelines.yml
git commit -m "Add CI/CD pipeline configuration"
git push

What do these commands do?

  • git add stages the new file so Git knows to include it in the next commit.
  • git commit saves a snapshot of your staged changes with a descriptive message.
  • git push uploads your commit to the Azure Repos remote, making it visible in your Azure DevOps project.
  • Go to your Azure DevOps project in the browser.
  • Click Repos in the left sidebar.
  • Confirm that azure-pipelines.yml now appears alongside your other files on the main branch.

Push rejected or authentication error?

If Git asks for credentials, Azure Repos may need you to generate a personal access token or use the Git credential manager. Go to your Azure DevOps profile icon in the top right, select Security, then Personal access tokens to create one with Code (Read & Write) scope.

Help me push code to Azure Repos

Your pipeline definition is in place. Next up, you will connect Azure DevOps to your Azure subscription and run the pipeline for the first time.

Connect Azure DevOps to Azure and Run the Pipeline

Your YAML pipeline file is sitting in Azure Repos, ready to go. Now you need to tell Azure DevOps to actually use it.

In this step, you will create a pipeline in Azure DevOps that reads your azure-pipelines.yml file, authorize a service connection that links Azure DevOps to your Azure subscription, and watch the pipeline build and deploy your app live.

In this step, get ready to:
  • Create a pipeline that uses your YAML file.
  • Authorize a service connection to your Azure subscription.
  • Watch the pipeline deploy your app and confirm it is live.
Create the pipeline in Azure DevOps

Azure DevOps needs you to explicitly create a pipeline object and point it at your YAML file. This tells the system which file defines your build and deploy instructions.

  • In your Azure DevOps project (my-webapp-pipeline), click Pipelines in the left sidebar.
  • Click New Pipeline (or Create Pipeline if this is your first pipeline).
  • Under "Where is your code?", select Azure Repos Git.
  • Select your repository (my-webapp-pipeline).
  • On the "Configure your pipeline" screen, select Existing Azure Pipelines YAML file.
  • In the dialog that appears, ensure the branch is set to main and the path is set to /azure-pipelines.yml.
  • Click Continue.

What is happening here?

Azure DevOps now knows your pipeline definition lives in azure-pipelines.yml on the main branch. Every time you push to main, Azure DevOps will read this file and execute the steps inside it.

You should now see a review screen showing the contents of your YAML file.

Authorize the service connection

Your YAML file references a service connection name in the azureSubscription field. A service connection is a secure link between Azure DevOps and your Azure subscription that grants the pipeline permission to deploy resources.

  • On the review screen, click Run to start the pipeline.
  • Azure DevOps will display a permissions prompt asking you to authorize the pipeline to use your Azure subscription.
  • Click View on the permissions banner, then click Permit to authorize the service connection.
  • If prompted to select an Azure subscription, choose the subscription that contains your rg-webapp-pipeline resource group.
  • Approve any pop-up authentication dialogs that appear (you may need to sign in to your Microsoft account again).

What is a service connection?

A service connection stores the credentials Azure DevOps needs to interact with your Azure subscription. It uses a service principal behind the scenes, so the pipeline can deploy to your App Service without you manually signing in each time.

You can view and manage your service connections later under Project Settings > Service connections.

Pipeline stuck or not starting?

If your pipeline stays queued and never runs, your Azure DevOps organization may not have the free parallel jobs grant enabled yet. New organizations must request this grant separately, and it can take several business days to process.

Submit a request at https://aka.ms/azpipelines-parallelism-request and provide your organization name and whether you need the grant for a private project. In the meantime, you can set up a self-hosted agent on your local machine as a workaround.

Why is my pipeline stuck in the queue and not running?

Watch the pipeline run and confirm your live app

Once authorized, the pipeline begins executing the steps defined in your YAML file. This is your CI/CD pipeline in action.

  • Click on the running pipeline job to open the live logs.
  • Watch each step execute in order: Install Node.js, Install dependencies, Archive files, and Deploy to Azure App Service.
  • Wait for all steps to show green checkmarks. The full run typically takes 2-4 minutes.

What is happening during the run?

  • Install Node.js uses the NodeTool@0 task to set up Node.js 20.x on the build agent.
  • Install dependencies runs npm install to download your Express package.
  • Archive files zips your entire application into a deployment package.
  • Deploy to Azure App Service uses the AzureWebApp@1 task to push the zip to your App Service.
  • Once all steps complete successfully, open a new browser tab.
  • Visit your App Service URL: https://your-app-service-name.azurewebsites.net (replace with your actual app name).
  • You should see your app displaying "Hello from Azure DevOps CI/CD!" in a heading, with the paragraph text below it.

Pipeline failed or app not loading?

If the Deploy to Azure App Service step failed, check that the appName value in your azure-pipelines.yml exactly matches your App Service name in the Azure Portal.

If the pipeline succeeded but your site shows a default Azure page, wait 1-2 minutes for the deployment to propagate, then refresh. You can also check App Service > Deployment Center in the Azure Portal to confirm the deployment arrived.

Help me troubleshoot my Azure DevOps pipeline deployment failure.

Your pipeline is running and your app is live. Next up, you will prove that continuous deployment actually works by pushing a code change and watching it go live automatically.

Verify Continuous Deployment

Your pipeline ran successfully and your app is live. Now it's time to prove that the continuous part of CI/CD actually works.

The real power of a CI/CD pipeline is that you never have to manually deploy again. In this step, you will make a code change, push it, and watch Azure DevOps pick it up and deploy it automatically.

In this step, get ready to:
  • Update your application code with a new message.
  • Push the change and watch the pipeline trigger automatically.
  • Confirm the updated app is live on Azure.
Make a code change

The pipeline you created is configured to trigger on every push to the main branch. To test this, you will change the homepage message in your app and push the update.

  • Open index.js in VS Code.
  • Find this line:
  res.send('<h1>Hello from Azure DevOps CI/CD!</h1><p>This app was deployed automatically using Azure Pipelines.</p>');
  • Replace Hello from Azure DevOps CI/CD! with Updated via CI/CD Pipeline! so the line looks like this:
  res.send('<h1>Updated via CI/CD Pipeline!</h1><p>This app was deployed automatically using Azure Pipelines.</p>');
  • Save index.js.
  • Commit and push the change by running these commands in your terminal:
git add .
git commit -m "Update homepage message"
git push

Why is this push special?

Your azure-pipelines.yml has a trigger set on the main branch. Every time you push to main, Azure Pipelines automatically starts a new pipeline run. You do not need to click anything in the portal.

Push rejected or failed?

Make sure you are pushing to the correct remote (your Azure Repos URL, not GitHub). Run git remote -v to check.

If you see an authentication error, confirm you are signed into the same Microsoft account used for Azure DevOps.

help me fix a git push error to Azure Repos

Watch the automatic trigger

The moment your push lands on the main branch, Azure DevOps detects the change and queues a new pipeline run. You can watch it happen in real time.

  • Go to your Azure DevOps project at dev.azure.com.
  • Click Pipelines in the left sidebar.
  • You should see a new pipeline run that was triggered automatically by your push. Look for the Update homepage message commit message in the run list.
  • Click on the run to watch it execute. Wait for it to finish with a green checkmark.

What should I see?

The pipeline run goes through the same stages as before: installing Node.js, running npm install, archiving the files, and deploying to your App Service. The entire process typically takes 1-3 minutes.

No new run appearing?

Confirm your push actually reached Azure Repos. Click Repos in the left sidebar and check that index.js shows your updated message.

If the code is there but no pipeline triggered, check that your azure-pipelines.yml trigger section includes - main and that you pushed to the main branch (not a different branch).

help me figure out why my Azure pipeline didn't trigger automatically

Confirm the deployment

Once the pipeline run completes successfully, your updated code is live. Time to see the result.

  • Open your browser and visit your App Service URL: https://your-app-service-name.azurewebsites.net.
  • You should see the heading Updated via CI/CD Pipeline! displayed on the page.

You made a code change, pushed it, and the pipeline automatically built and deployed it to a live URL. That is Continuous Deployment in action.

Still seeing the old message?

Try a hard refresh in your browser. Press Cmd+Shift+R (macOS) or Ctrl+Shift+R (Windows) to bypass the cache.

If the old message persists, go back to Pipelines and confirm the latest run completed with a green checkmark. A failed run means the old code is still deployed.

help me troubleshoot why my Azure App Service is not showing updated content

✔️ Awesome, I've got everything!

Great. Double check you have saved your file.

ⓧ I'd like to double check the full code

Here is the complete index.js after your update:

const express = require('express');
const app = express();
const port = process.env.PORT || 8080;

app.get('/', (req, res) => {
  res.send('<h1>Updated via CI/CD Pipeline!</h1><p>This app was deployed automatically using Azure Pipelines.</p>');
});

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

Secret mission

Add a Staging Slot with Swap

Real teams never deploy straight to production. They deploy to a staging environment first, verify everything works, then swap staging into production with zero downtime. Can you extend your pipeline to follow this professional deployment pattern?

Clean Up Your Resources

Clean Up Your Resources

Your Azure App Service on the Free (F1) plan costs nothing. Azure DevOps free tier gives you 1,800 minutes per month of pipeline time. You can leave everything running at no cost.

Cost warning

If you completed the Secret Mission, you upgraded to the Standard (S1) plan at approximately $54.75/month. Scale back down to Free (F1) or delete your resources to avoid ongoing charges.

Resources you used:

  • Azure resource group (contains your App Service).
  • Azure DevOps organization and project.
  • Local project files on your computer.

Keep everything running

No action needed. Choose this if you are still actively building or want to experiment more with your pipeline.

  • Your App Service on the Free (F1) plan has no ongoing charges.
  • Azure DevOps free tier includes 1,800 minutes per month of pipeline time.
  • Your local project files remain on your Desktop.

Pause - I'll come back to this later

The Free plan has no ongoing charges, so there is nothing to pause. Your resources stay idle at no cost until you return.

  • If you completed the Secret Mission, scale your App Service plan back down to Free (F1) in the Azure Portal to stop charges.
  • Your pipeline will not run unless you push new code to the main branch.

Delete - I don't want to use this again

Remove all project resources and start fresh. This is irreversible.

Delete your Azure resource group (this removes the App Service and all associated resources):

  • Go to portal.azure.com and sign in.
  • Search for and select Resource groups in the top search bar.
  • Click on your resource group (e.g. rg-webapp-pipeline).
  • Select Delete resource group at the top.
  • Type the resource group name to confirm, then click Delete.

Delete your Azure DevOps project:

  • Go to dev.azure.com and sign in to your organization.
  • Select Organization settings in the bottom-left corner.
  • Select Projects in the left sidebar.
  • Check the box next to your my-webapp-pipeline project, then select Delete.
  • Type the project name to confirm, then click Delete.

Delete your local project files:

macOS

  • Open your terminal and run:
rm -rf ~/Desktop/my-webapp-pipeline

Windows

  • Open PowerShell and run:
Remove-Item -Recurse -Force ~\Desktop\my-webapp-pipeline

That's a Wrap!

That's a Wrap!

You did it! You built a fully automated CI/CD pipeline from scratch using Azure DevOps.

You've learned how to:

  • Written a YAML pipeline that automatically builds and deploys a Node.js web app to Azure App Service on every push to main.
  • Connected Azure DevOps to your Azure subscription using a service connection, enabling automated deployments.
  • Verified continuous deployment by pushing a code change and watching it go live at a public URL without any manual steps.
  • Secret Mission: Added a staging deployment slot and configured your pipeline to swap staging into production for zero-downtime deployments.

Ready to quiz yourself?