Build a CI/CD Pipeline with Azure DevOps

Build a YAML pipeline that auto-deploys a Node.js app to Azure App Service.

Introduction

30 Second Summary

Every time a developer makes a code change, someone has to build, test, and deploy it. Doing this manually is slow, error-prone, and doesn't scale. That's why professional teams automate the entire process.

In this project, you will build a CI/CD pipeline in Azure DevOps that automatically builds and deploys a web application every time you push code.

What You'll Build

A multi-stage YAML pipeline that takes your code from commit to live website with zero manual deployment steps.

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

  • A Node.js web application hosted in Azure Repos with a complete YAML pipeline that builds and deploys on every push.
  • A live Azure App Service instance running your deployed app, connected through a service connection you configured yourself.
  • A multi-stage pipeline with separate Build and Deploy stages that mirrors how real development teams ship code.
  • Secret Mission: Add a staging environment with a manual approval gate before production deployment.

Are there any prerequisites?

You should have basic familiarity with Git (commits and pushes). You will also need a Microsoft account (personal, work, or school) or a GitHub account for signing up to Azure DevOps.

Set Up Your Accounts and Tools

Before you can build a pipeline, you need the right tools on your machine and accounts in the cloud.

In this step, you will install Git for version control, set up Visual Studio Code as your code editor, and create free accounts on both Azure and Azure DevOps.

In this step, get ready to:
  • Install Git and verify it works in your terminal.
  • Install Visual Studio Code as your code editor.
  • Create your free Azure and Azure DevOps accounts.
Install Git

Git is the version control system that tracks changes to your code. Azure DevOps uses Git repositories to store your source code and detect when to trigger your pipeline.

  • Open your terminal by pressing 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 Git is already installed by running this command:
git --version

✔️ I see a version number

You should see something like git version 2.x.x. Git is ready to go.

ⓧ Command not found

Git is not installed yet. Follow the instructions for your operating system below.

macOS

  • Running git --version on macOS may automatically prompt you to install the Xcode Command Line Tools. If you see that prompt, click Install and wait for it to finish.
  • If no prompt appeared, download Git from git-scm.com and run the installer.
  • After installation completes, close and reopen your terminal, then run git --version again to confirm.

Windows

  • Go to git-scm.com and download the Windows installer.
  • Double-click the downloaded .exe file to launch the setup wizard.
  • Accept the default options at each screen and click Next until the installation completes.
  • Close and reopen Command Prompt, then run git --version again to confirm.

Still not seeing a version number?

Make sure you closed and reopened your terminal after installing. Some systems need a fresh terminal session to recognize newly installed programs.

help me troubleshoot why git --version is not working after I installed Git

Install Visual Studio Code

Visual Studio Code (VS Code) is the code editor where you will write your pipeline YAML file and your application code.

  • Go to code.visualstudio.com and click the download button for your operating system.
  • Run the installer and accept the default options.
  • Once installed, open VS Code by pressing Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Visual Studio Code and press Enter to open it.

You should see the VS Code Welcome tab open in a fresh editor window.

Add the Azure Pipelines extension

This is optional but helpful. The Azure Pipelines extension adds YAML syntax highlighting and autocompletion for pipeline files.

  • Press Cmd+Shift+X (macOS) or Ctrl+Shift+X (Windows) to open the Extensions panel.
  • Search for Azure Pipelines and install the one published by Microsoft.
Create your Azure and Azure DevOps accounts

You need two accounts for this project. A free Azure account gives you access to cloud resources like Azure App Service where your app will live. An Azure DevOps organization is where your pipeline runs.

Both are free and use the same Microsoft account.

  • Go to azure.microsoft.com/free and click Start free.
  • Sign in with an existing Microsoft account, or create a new one.
  • Complete the signup form. Azure provides $200 credit for 30 days and always-free services including the App Service F1 tier.

What is the Azure free account?

The free Azure account gives you $200 in credit to use within 30 days, plus 20+ services that remain free indefinitely. The App Service F1 tier you will use in this project is one of those always-free services.

Now create your Azure DevOps organization.

  • Go to dev.azure.com.
  • Sign in with the same Microsoft account you used for the Azure free account.
  • When prompted, click Create new organization.
  • Accept the terms of service and choose a region close to you.
  • Enter a name for your organization (your name or a short project name works fine) and click Continue.

Why do I need both Azure and Azure DevOps?

Azure is the cloud platform where your web app runs (the deployment target). Azure DevOps is the separate service that hosts your code repository and runs your build/deploy pipeline.

They share a Microsoft login but serve different purposes. Your pipeline in Azure DevOps will push code to your Azure App Service automatically.

You should now be signed into both the Azure Portal at portal.azure.com and your Azure DevOps organization at dev.azure.com.

Having trouble creating your accounts?

Make sure you are using the same Microsoft account for both Azure and Azure DevOps. A mismatch here causes problems later when you create the service connection between them.

No credit card is required to start with Azure DevOps. If you are being asked for payment details, you may be on the wrong signup page.

help me troubleshoot signing up for Azure and Azure DevOps

Your tools are installed and your accounts are live. Next up, you will create your Azure DevOps project and build a sample Node.js app to deploy through the pipeline.

Create Your Azure DevOps Project and Sample App

Your tools are installed and your accounts are ready. Now you need a place to store your code and a simple app for the pipeline to build and deploy.

In this step, you will create a project in Azure DevOps, build a basic Node.js web application, and push it to Azure Repos. This gives your pipeline something to work with in the next step.

In this step, get ready to:
  • Create a new Azure DevOps project with a Git repository.
  • Build a Node.js Express web app and push it to Azure Repos.
Create a new project in Azure DevOps

A project in Azure DevOps is a container that holds your code repository, pipelines, and work items. You will create one now to house your app.

  • Open your browser and navigate to your Azure DevOps organization at dev.azure.com.
  • Click + New project in the top right corner of the Projects page.
  • Enter my-cicd-app as the project name.
  • Set Visibility to Private.
  • Under Version control, select Git.
  • Click Create.

Azure DevOps displays the project welcome page. Your project now has an empty Git repository ready to receive code.

What is an Azure DevOps project?

A project groups related repositories, pipelines, and boards under one roof. Each project isolates its data from other projects in your organization.

Clone the repo and create the Node.js app

You will clone the empty repository to your local machine, then create a small Express web app inside it.

  • In your Azure DevOps project, click Repos in the left sidebar.
  • Click the Clone button in the upper-right corner of the Files view.
  • Copy the HTTPS clone URL from the popup.

What does the clone URL look like?

The URL follows the pattern https://dev.azure.com/{org}/my-cicd-app/_git/my-cicd-app. Your organization name replaces {org}.

  • 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.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Clone the empty repository by running this command:
git clone [[REPO_URL="your Azure Repos HTTPS clone URL"]]

What happens when you clone?

Git may prompt you to sign in with your Microsoft account. A browser window or credential dialog will appear. Sign in with the same account you used for Azure DevOps.

After authentication, Git creates a local folder named my-cicd-app on your Desktop containing the empty repo.

  • Move into the cloned folder by running:
cd my-cicd-app

Now you need Node.js installed to use npm for creating your project files. Check that it is available by running:

node --version

✔️ I see a version number

Node.js is installed and ready. Continue to the next instruction below.

ⓧ Command not found

You need Node.js to use npm. Download and install it from nodejs.org. Choose the LTS version.

After installation, close and reopen your terminal, then run node --version again to confirm it works.

  • Scaffold a package.json file by running:
npm init -y

What does npm init -y do?

This command creates a package.json file with default values based on your folder name. You will replace its contents with the exact configuration your app needs.

  • Open the my-cicd-app folder in VS Code. 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, select the my-cicd-app folder, and click Open.

Now replace the generated package.json with the correct content for your app.

  • Open package.json in the VS Code Explorer sidebar.
  • Select all the content (Cmd+A on macOS or Ctrl+A on Windows) and replace it with the following:
{
  "name": "my-cicd-app",
  "version": "1.0.0",
  "description": "A simple Node.js app deployed via Azure DevOps CI/CD",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.21.0"
  }
}

What does this package.json do?

This file tells Node.js about your project. The start script defines how Azure App Service will launch your app.

The express dependency is the web framework your app uses to serve HTTP responses.

  • Save package.json (Cmd+S on macOS or Ctrl+S on Windows).
  • Install the Express dependency by running this command in your terminal:
npm install

You should see a node_modules folder appear in your project. This contains the Express library files.

npm install failing?

Make sure you saved package.json before running the command. Also confirm your terminal is inside the my-cicd-app folder by checking the folder name in your terminal prompt.

Help me debug this

Next, create the main application file.

  • In VS Code, hover over the Explorer sidebar and click the New File icon.
  • Name the file app.js and press Enter.
  • Paste the following code into app.js:
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.send(`
    <!DOCTYPE html>
    <html>
    <head>
      <title>My CI/CD App</title>
      <style>
        body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #f0f4f8; }
        .container { text-align: center; padding: 2rem; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        h1 { color: #0078d4; }
        p { color: #555; }
      </style>
    </head>
    <body>
      <div class="container">
        <h1>Hello from Azure DevOps!</h1>
        <p>This app was deployed automatically via a CI/CD pipeline.</p>
        <p>Build time: ${new Date().toISOString()}</p>
      </div>
    </body>
    </html>
  `);
});

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

What does this app do?

This is a minimal Express web server. When someone visits the root URL, it responds with an HTML page showing a greeting and the current build timestamp.

The process.env.PORT || 3000 line lets Azure App Service assign its own port at runtime. Locally, it defaults to port 3000.

  • Save app.js.

Finally, create a .gitignore file so the large node_modules folder is not committed to your repository.

  • Click the New File icon in the Explorer sidebar again.
  • Name the file .gitignore and press Enter.
  • Paste the following content into .gitignore:
node_modules/
  • Save .gitignore.

✔️ Awesome, I've got everything!

Great. Double check you have saved all three files before moving on.

ⓧ I'd like to double check the full code

Here is what each file should contain at this point.

package.json:

{
  "name": "my-cicd-app",
  "version": "1.0.0",
  "description": "A simple Node.js app deployed via Azure DevOps CI/CD",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.21.0"
  }
}

app.js:

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

app.get('/', (req, res) => {
  res.send(`
    <!DOCTYPE html>
    <html>
    <head>
      <title>My CI/CD App</title>
      <style>
        body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #f0f4f8; }
        .container { text-align: center; padding: 2rem; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        h1 { color: #0078d4; }
        p { color: #555; }
      </style>
    </head>
    <body>
      <div class="container">
        <h1>Hello from Azure DevOps!</h1>
        <p>This app was deployed automatically via a CI/CD pipeline.</p>
        <p>Build time: ${new Date().toISOString()}</p>
      </div>
    </body>
    </html>
  `);
});

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

.gitignore:

node_modules/
Push your code to Azure Repos

Your files are ready locally. Now commit and push them so Azure DevOps has your code in its repository.

  • Stage all your files by running this command in your terminal (make sure you are still in the my-cicd-app folder):
git add .
  • Commit the files with a descriptive message by running:
git commit -m "Initial app"
  • Push your commit to the main branch on Azure Repos by running:
git push origin main

What do these three commands do?

The git add . command stages every new and modified file for commit. The git commit command saves them as a snapshot in your local repository.

The git push origin main command uploads that snapshot to the remote repository in Azure DevOps. Your pipeline will read code from this remote repository.

  • Go back to your browser and navigate to Repos in the left sidebar, then click Files.
  • Refresh the page. You should see app.js, package.json, .gitignore, and package-lock.json listed in the file tree.

Files not showing in Azure Repos?

Make sure the git push command completed without errors. If you see a rejection about the branch name, try running git branch -M main and then git push origin main again.

If you are prompted for credentials, sign in with the same Microsoft account you used for Azure DevOps.

Help me troubleshoot this

Your source code is now live in Azure Repos. Next up, you will create the Azure App Service that will host your running application.

Create the Azure App Service Deployment Target

Your Node.js app is pushed to Azure Repos and ready to go. Now it needs a live destination on the internet where users can actually visit it.

Azure App Service is a managed hosting platform that runs your web app without requiring you to manage servers. In this step, you'll create the App Service instance and set up a service connection so Azure Pipelines can deploy to it automatically.

In this step, get ready to:
  • Create an Azure App Service with the Free F1 pricing tier.
  • Verify the default page loads at your app's URL.
  • Create a service connection in Azure DevOps to authorize deployments.
Create an App Service in Azure Portal

Azure App Service gives your app a live URL and handles scaling, patching, and infrastructure for you. You'll create one on the Free F1 tier so it costs nothing.

  • Open the Azure Portal at portal.azure.com and sign in with your Microsoft account.
  • In the search bar at the top, type App Services and select App Services from the results.
  • Click + Create and select Web App.
  • Under Subscription, select your Azure subscription.
  • Under Resource Group, click Create new and enter rg-cicd-demo.
  • Under Name, enter a globally unique name for your app (for example, nextwork-cicd-jane). This name becomes part of your live URL.
  • Under Publish, select Code.
  • Under Runtime stack, select Node 20 LTS.
  • Under Operating System, select Linux.
  • Under Region, choose a region close to you.
  • Under Pricing plan, select Free F1.
  • Click Review + create at the bottom of the page.
  • Click Create.

What is a resource group?

A resource group is a container that holds related Azure resources together. By putting your App Service inside rg-cicd-demo, you can manage, monitor, and delete all the project's resources in one place.

App name must be globally unique

Your app name becomes the subdomain in your URL (e.g., nextwork-cicd-jane.azurewebsites.net). If the name is already taken, Azure will show a validation error. Try adding your initials or a number.

Verify the App Service is running

Once Azure finishes provisioning (this takes about 30 seconds), you can confirm the App Service is live.

  • When deployment is complete, click Go to resource.
  • On the App Service overview page, find the URL under Default domain (top right area). It looks like your-app-name.azurewebsites.net.
  • Click the URL to open it in a new browser tab.
  • You should see the Azure default placeholder page confirming your app is running and ready for deployment.

Seeing a 404 or blank page?

The App Service can take a minute to fully start up after creation. Wait 60 seconds and refresh the page.

If you still see nothing, go back to App Services in the Azure Portal, click your app, and check that the Status shows Running. Help me troubleshoot my App Service not loading.

Create a service connection in Azure DevOps

A service connection authorizes Azure Pipelines to deploy code to your Azure subscription. Without it, the pipeline has no permission to push your app to App Service.

  • Go back to your Azure DevOps project at dev.azure.com.
  • Click Project settings (gear icon in the bottom left corner of the sidebar).
  • Under Pipelines, click Service connections.
  • Click New service connection.
  • Select Azure Resource Manager and click Next.
  • Select Service principal (automatic) and click Next.
  • For Scope level, select Subscription.
  • Select your Azure subscription from the dropdown.
  • Under Resource group, select rg-cicd-demo (the resource group you just created).
  • Under Service connection name, enter azure-connection.
  • Check the Grant access permission to all pipelines checkbox.
  • Click Save.

What is a service connection?

A service connection stores authentication credentials that let Azure Pipelines interact with external services. When you choose Service principal (automatic), Azure DevOps creates an app registration in your directory and assigns it Contributor access to your selected scope.

The connection name azure-connection is what your pipeline YAML will reference later to authenticate deployment tasks.

Why scope to the resource group?

By scoping the service connection to rg-cicd-demo, you follow the principle of least privilege. The pipeline can only deploy to resources inside that specific resource group, not your entire Azure subscription.

Azure DevOps asks you to sign in again?

When creating a service connection, Azure DevOps may open a pop-up window to authenticate with your Microsoft account. Make sure pop-ups are allowed in your browser for dev.azure.com.

If your subscription does not appear in the dropdown, confirm you are signed into Azure DevOps with the same Microsoft account you used for the Azure Portal. Help me fix my service connection issue.

Your deployment target is live and connected. Next up, you'll write the YAML pipeline that automatically builds and deploys your app every time you push code.

Write Your YAML Pipeline

You have a working app in Azure Repos and a deployment target ready in Azure App Service. The missing piece is the automation that connects them.

In this step, you'll write a YAML pipeline file that tells Azure DevOps exactly how to build and deploy your app whenever code is pushed to the main branch.

In this step, get ready to:
  • Define the pipeline trigger and shared variables.
  • Write a Build stage that packages your app into a deployable artifact.
  • Write a Deploy stage that sends the artifact to Azure App Service.
Create the pipeline file with trigger and variables

Azure DevOps looks for a file named azure-pipelines.yml in the root of your repository. This file defines what happens every time code is pushed.

  • In VS Code, make sure you have your my-cicd-app folder open.
  • Create a new file in the root of the folder named azure-pipelines.yml.
  • Add the trigger and variables section by pasting this code into the file:
# Trigger the pipeline on any push to the main branch
trigger:
  - main

# Variables shared across all stages
variables:
  azureSubscription: 'azure-connection'
  appName: '[[APPNAME="your-app-name"]]'
  vmImageName: 'ubuntu-latest'

What does this code do?

trigger: - main tells Azure DevOps to run this pipeline automatically whenever anyone pushes code to the main branch.

The variables block defines reusable values. azureSubscription references the service connection you created in the last step. appName is your App Service's unique name. vmImageName sets the virtual machine image the pipeline runs on.

  • Save azure-pipelines.yml.
  • Confirm the file exists by running:
ls azure-pipelines.yml

You should see azure-pipelines.yml printed in your terminal output.

Don't see the file?

Make sure you created the file in the root of your my-cicd-app folder, not inside a subfolder. Azure DevOps expects the pipeline file at the repository root for automatic detection.

Also check the file extension is .yml and not .yaml or .txt.

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

Why YAML instead of the visual editor?

Azure DevOps also has a Classic visual pipeline editor, but Microsoft recommends YAML for all new projects. YAML pipelines live inside your repository alongside your code, so every change is version-controlled and reviewable in pull requests.

This is the approach you'll encounter on professional teams.

Add the Build stage

The Build stage installs your app's dependencies and packages everything into a single ZIP file. This ZIP becomes a pipeline artifact that the Deploy stage picks up later.

  • In azure-pipelines.yml, add the following code below the variables block:
stages:
  # Build stage: install dependencies and package the app
  - stage: Build
    displayName: 'Build stage'
    jobs:
      - job: Build
        pool:
          vmImage: $(vmImageName)
        steps:
          - task: NodeTool@0
            inputs:
              versionSpec: '20.x'
            displayName: 'Install Node.js'

          - script: npm install
            displayName: 'Install dependencies'

          # Archive the app into a ZIP for deployment
          - task: ArchiveFiles@2
            displayName: 'Archive files'
            inputs:
              rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
              includeRootFolder: false
              archiveType: zip
              archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
              replaceExistingArchive: true

          # Publish the ZIP as a pipeline artifact called 'drop'
          - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
            displayName: 'Upload artifact'
            artifact: drop

What does this code do?

  • NodeTool@0 installs Node.js version 20.x on the pipeline's virtual machine so npm commands work.
  • npm install pulls in your app's dependencies (Express) from package.json.
  • ArchiveFiles@2 zips the entire working directory into a single deployable file.
  • The upload step publishes that ZIP as an artifact named drop. Other stages can download this artifact by name.
  • Save azure-pipelines.yml.

You won't see the pipeline run yet. The next chunk completes the file with a Deploy stage.

Seeing red squiggles in VS Code?

YAML is sensitive to indentation. Every level must use exactly 2 spaces (not tabs). Make sure the stages: key starts at column 1, flush with trigger: and variables: above it.

help me fix YAML indentation errors in my azure-pipelines.yml

Add the Deploy stage

The Deploy stage picks up the artifact from the Build stage and pushes it to your live App Service. It only runs if the Build stage succeeds.

  • In azure-pipelines.yml, add the following code directly below the Build stage (inside the same stages: list):
  # Deploy stage: push the artifact to Azure App Service
  - stage: Deploy
    displayName: 'Deploy stage'
    dependsOn: Build
    condition: succeeded()
    jobs:
      - deployment: Deploy
        pool:
          vmImage: $(vmImageName)
        environment: 'production'
        strategy:
          runOnce:
            deploy:
              steps:
                - task: AzureWebApp@1
                  displayName: 'Deploy to Azure App Service'
                  inputs:
                    azureSubscription: $(azureSubscription)
                    appType: 'webAppLinux'
                    appName: $(appName)
                    package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip'

What does this code do?

  • dependsOn: Build ensures this stage waits for the Build stage to finish first.
  • condition: succeeded() means Deploy only runs if Build passed with no errors.
  • The deployment job type targets an environment called production. Azure DevOps creates this environment automatically on the first run.
  • AzureWebApp@1 is the built-in task that deploys a package to Azure App Service. It uses your service connection (azureSubscription) for authentication and the artifact ZIP as the deployment package.
  • Save azure-pipelines.yml.

Your pipeline definition is now complete. You won't see it run until you push the file to Azure Repos in the next step.

Not sure about the indentation?

The - stage: Deploy line must be at the same indentation level as - stage: Build above it (4 spaces from the left edge). Both are items in the same stages: list.

help me verify my Deploy stage is correctly indented in my azure-pipelines.yml file

✔️ Awesome, I've got everything!

Great. Double check you've saved azure-pipelines.yml in VS Code before moving on.

ⓧ I'd like to double check the full code

Here is the complete azure-pipelines.yml file. Compare yours against it:

trigger:
  - main

variables:
  azureSubscription: 'azure-connection'
  appName: 'your-app-name'
  vmImageName: 'ubuntu-latest'

stages:
  - stage: Build
    displayName: 'Build stage'
    jobs:
      - job: Build
        pool:
          vmImage: $(vmImageName)
        steps:
          - task: NodeTool@0
            inputs:
              versionSpec: '20.x'
            displayName: 'Install Node.js'

          - script: npm install
            displayName: 'Install dependencies'

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

          - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
            displayName: 'Upload artifact'
            artifact: drop

  - stage: Deploy
    displayName: 'Deploy stage'
    dependsOn: Build
    condition: succeeded()
    jobs:
      - deployment: Deploy
        pool:
          vmImage: $(vmImageName)
        environment: 'production'
        strategy:
          runOnce:
            deploy:
              steps:
                - task: AzureWebApp@1
                  displayName: 'Deploy to Azure App Service'
                  inputs:
                    azureSubscription: $(azureSubscription)
                    appType: 'webAppLinux'
                    appName: $(appName)
                    package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip'

Your pipeline definition is complete and saved locally. Next, you'll push it to Azure Repos and watch Azure DevOps pick it up automatically.

Run Your Pipeline and Verify

Your pipeline definition is written and saved locally. Now it is time to push it to Azure Repos and watch the automation come to life.

When Azure DevOps detects an azure-pipelines.yml file in your repository, it automatically creates and runs a pipeline from that definition. One push is all it takes to trigger a full build-and-deploy cycle.

In this step, get ready to:
  • Commit and push your pipeline file to trigger the first run.
  • Watch both stages (Build and Deploy) complete successfully.
  • Verify your live app and confirm automatic redeployment on code changes.
Commit and push the pipeline file

Your azure-pipelines.yml file exists locally but Azure DevOps cannot see it until you commit and push it to the remote repository.

  • Stage, commit, and push your pipeline file by running these commands in your terminal:
git add .
git commit -m "Add CI/CD pipeline"
git push origin main

What do these commands do?

  • git add . stages all files in the current directory for commit.
  • git commit -m "Add CI/CD pipeline" saves the staged changes as a snapshot in your local Git history.
  • git push origin main uploads your commit to the remote Azure Repo on the main branch, which triggers the pipeline.
  • After the push completes, you should see output confirming the objects were written to the remote.

Push failed or permission denied?

Make sure you are inside the cloned repository folder in your terminal. If you see an authentication error, re-enter your Git credentials or check that you are using the correct HTTPS URL from Azure Repos.

help me troubleshoot a failed git push to Azure Repos

Watch the pipeline run

Azure DevOps automatically detected your azure-pipelines.yml file and created a pipeline. Now you can watch it execute both stages in real time.

  • In your browser, go to your Azure DevOps project at dev.azure.com.
  • Click Pipelines in the left sidebar.
  • You should see your pipeline listed. Click on it to open the run details.

Seeing a permission prompt? Do not panic

On the first run, Azure DevOps displays a message saying the pipeline needs permission to access a resource. Click View next to the message, then click Permit to authorize the pipeline to use your service connection and environment.

This is a one-time security gate. Azure DevOps requires you to explicitly approve new pipelines before they can access protected resources. You will not see this prompt on future runs.

  • After granting permissions, watch the Build stage complete first. It installs Node.js, runs npm install, archives your files, and uploads the artifact.
  • Once Build succeeds, the Deploy stage begins automatically. It downloads the artifact and deploys it to your App Service.
  • Wait for both stages to show green checkmarks. This typically takes 2-5 minutes.

What is happening behind the scenes?

Azure DevOps spins up a fresh ubuntu-latest virtual machine for each stage. The Build stage packages your app into a ZIP file and publishes it as an artifact named drop. The Deploy stage picks up that artifact and pushes it to your Azure App Service instance using the service connection you created earlier.

Pipeline stuck or failing?

If the Build stage fails, check that your package.json is committed and in the repository root. A missing package.json causes npm install to fail.

If the Deploy stage fails, verify that the appName variable in your azure-pipelines.yml matches your actual App Service name exactly.

help me debug my Azure DevOps pipeline failure

Verify your live app and test automatic redeployment

Both stages passed. Your app is now live on the internet, deployed entirely through the pipeline.

  • Open a new browser tab and navigate to your App Service URL:
https://[[APPNAME="your-app-name"]].azurewebsites.net
  • You should see your app displaying Hello from Azure DevOps! along with a build timestamp.

Still seeing the Azure default page?

The deployment may take a minute to propagate after the pipeline completes. Try a hard refresh with Ctrl+Shift+R (Windows) or Cmd+Shift+R (macOS). If the default page persists after 2 minutes, check the Deploy stage logs for errors.

help me figure out why my App Service is not showing my deployed app

Now prove that the pipeline runs automatically on every push. You will make a small code change and watch the full cycle repeat without any manual intervention.

  • In VS Code, open app.js and find this line:
        <p>This app was deployed automatically via a CI/CD pipeline.</p>
  • Change the text to something new, for example:
        <p>This app was deployed automatically via my Azure DevOps pipeline.</p>
  • Save app.js, then commit and push your change by running:
git add .
git commit -m "Update app text"
git push origin main
  • Go back to Pipelines in Azure DevOps. You should see a new pipeline run triggered automatically by your push.
  • Wait for both stages to complete (no permission prompt this time).
  • Refresh your App Service URL. You should see the updated text and a new build timestamp.

Why did the pipeline run without any manual trigger?

Your azure-pipelines.yml includes trigger: - main at the top. This tells Azure DevOps to start a new pipeline run every time a commit lands on the main branch. This is Continuous Integration in action.

You just experienced a full CI/CD cycle. Code went from your editor to a live website with zero manual deployment steps.

Secret mission

Add a Staging Environment with Approval Gates

Real teams never deploy directly to production. Can you add a staging environment with a manual approval gate that protects your production deployment?

Clean Up Your Resources

Clean Up Your Resources

This project uses Azure App Service on the Free F1 tier and Azure DevOps within its free usage limits. There are no ongoing costs while these resources remain on their free tiers.

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Azure App Service (Free F1 tier).
  • Azure DevOps organization and project.
  • Azure Resource Group containing your App Service.
  • Local project folder on your Desktop.

Keep everything running

No action needed. Choose this if you are still actively building or want to keep your pipeline running.

  • Your App Service stays live at no cost on the Free F1 tier.
  • Your Azure DevOps pipeline continues to run on pushes within the 1,800 free minutes per month.
  • Your local project folder remains on your Desktop for future edits.

Pause - I'll come back to this later

Stop running processes to free up resources while keeping your configuration intact.

  • In the Azure Portal, search for App Services and select your app.
  • On the Overview page, click Stop to halt the app.
  • Your pipeline will still trigger on pushes, but the deployed site will not respond until you click Start again.
  • Your local project folder and Azure DevOps project remain untouched.

Delete - I don't want to use this again

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

Delete the Azure Resource Group (this removes your App Service and all related resources):

  • In the Azure Portal, search for Resource groups and select rg-cicd-demo.
  • Click Delete resource group.
  • Type the resource group name to confirm, then click Delete.

Delete the Azure DevOps project:

  • Go to dev.azure.com and sign in.
  • Click Organization settings (gear icon in the bottom-left).
  • Select Projects in the left sidebar.
  • Check the box next to your project and click Delete.
  • Confirm by typing the project name, then click Delete.

Delete your local project folder:

macOS

rm -rf ~/Desktop/my-cicd-app

Windows

Remove-Item -Recurse -Force ~\Desktop\my-cicd-app

That's a Wrap!

That's a Wrap!

Nice work! You just built a professional CI/CD pipeline from scratch using Azure DevOps.

Your code now goes from commit to live website with zero manual deployment steps.

You've learned how to:

  • Write a multi-stage YAML pipeline that automatically builds and deploys your code on every push to Azure Repos.
  • Create a service connection linking Azure DevOps to your Azure subscription for automated deployments.
  • Deploy a live Node.js web app to Azure App Service entirely through your pipeline.
  • Add a staging environment with manual approval gates for safe production deployments.

Ready to quiz yourself?