Build a CI/CD Pipeline with Azure DevOps
Automate building and deploying a Node.js app with Azure DevOps Pipelines.
Introduction
30 Second Summary
Every time you update an app or visit a website that just got a new feature, someone had to get that code from a developer's computer to a live server. What if that entire process happened automatically every time a developer saved their work?
In this project, you will build an automated pipeline that takes your code from a Git push all the way to a live website, with zero manual deployment steps in between.
What You'll Build
You'll create a multi-stage CI/CD pipeline in Azure DevOps that automatically builds, tests, and deploys a Node.js web application to Azure App Service.
By the end of this project, you'll have:
- An Azure DevOps project with a code repository and a multi-stage YAML pipeline that defines your build and deploy process as code.
- A live Node.js web application running on Azure App Service, deployed entirely through your pipeline.
- A working CI/CD loop where pushing code automatically triggers a build, runs tests, and deploys to production.
- Secret Mission: Add a staging environment with approval gates so deployments to production require manual sign-off.
Are there any prerequisites?
You should have a basic understanding of Git (commit, push, branch) and familiarity with what a web application is. No prior experience with Azure or CI/CD is required.
Set Up Your Accounts
Before you can build a CI/CD pipeline, you need the platforms that host and run it. Think of it like setting up a kitchen before cooking. You need the workspace (Azure DevOps) and the serving counter (Azure App Service) ready before you start building your recipe.
In this step, you will create your free Azure account, set up an Azure DevOps organization, and verify that Git is installed on your machine. These are the foundations everything else builds on.
In this step, get ready to:
- Create a free Azure account for hosting your web app.
- Set up an Azure DevOps organization for your pipeline.
- Verify Git is installed on your computer.
Create a free Azure account
Azure is Microsoft's cloud platform where your web app will live. The Free tier includes everything you need for this project at no cost.
- Open your browser and navigate to portal.azure.com.
- Click Start free or Sign in.
- Sign in with your existing Microsoft account or GitHub account. If you do not have one, create a new Microsoft account.
- Complete the sign-up process. Azure may ask for a credit card for identity verification, but you will not be charged for the Free tier resources used in this project.
Why does Azure ask for a credit card?
Azure uses it to verify your identity and prevent abuse. The App Service Free (F1) tier costs $0/month, so your card will not be charged during this project.
- Once signed in, confirm you can see the Azure Portal dashboard. You should see a search bar at the top and a list of Azure services.
Create an Azure DevOps organization
Azure DevOps is the platform that will host your code repository and run your CI/CD pipeline. It is free for up to 5 users.
- Open a new browser tab and navigate to dev.azure.com.
- Sign in with the same Microsoft account you used for the Azure Portal.
- If prompted, click Create new organization.
- Choose a name for your organization (this can be anything, like your name or a short project label).
- Select a region closest to you when prompted, then click Continue.
You should now see the Azure DevOps landing page for your organization. It may prompt you to create a project, but skip that for now.
Request the free parallelism grant now
New Azure DevOps organizations do not automatically receive free pipeline minutes. You must request them separately, and approval can take several business days.
- Open https://aka.ms/azpipelines-parallelism-request in a new tab.
- Fill out the form with your name, your Azure DevOps organization name, and select that you need the grant for private projects.
- Submit the request. You will receive an email once approved.
Submit this request right away so it can process while you continue the project. If it has not been approved by the time you reach Step 5, your pipeline will not run until the grant is active.
What is a parallelism grant?
Azure DevOps uses hosted build agents to run your pipeline. The parallelism grant gives your organization permission to use one of these agents for free. Without it, your pipeline jobs will stay queued indefinitely.
Ensure Git is installed
Git is the version control tool you will use to push code to Azure Repos. Most modern systems have it pre-installed.
- Open your terminal by pressing Cmd+Space (macOS) or the Windows key (Windows) and typing Terminal, then pressing Enter.
- Check your Git installation by running this command:
git --version
✔️ I see a version number
You are good to go. Git is installed and ready.
ⓧ Command not found
Git is not installed on your machine. Follow these steps to install it.
- Open your browser and navigate to git-scm.com.
- Download the installer for your operating system.
- Run the installer and accept all default settings.
- Close and reopen your terminal, then run git --version again to confirm the installation.
Still not seeing a version number?
On macOS, running git --version may trigger an Xcode Command Line Tools install prompt. Click Install and wait for it to finish, then try again.
On Windows, make sure you restarted your terminal after installing Git. The system PATH only updates in new terminal sessions.
Help me troubleshoot my Git installation.
Your accounts are ready and your parallelism grant is processing. Next up, you will create your Azure DevOps project and build the sample Node.js app that your pipeline will deploy.
Create Your Azure DevOps Project and Sample App
Your accounts are ready and your tools are installed. Now you need a project to house your code and pipeline.
In this step, you'll create a new project in Azure DevOps, add a simple Node.js web application to its repository, and push everything to the cloud. This app is what your pipeline will automatically build and deploy in later steps.
In this step, get ready to:
- Create a new project in Azure DevOps.
- Clone the repository and add a Node.js application.
- Push the code to Azure Repos.
Create a new project in Azure DevOps
Azure DevOps organizes everything into projects. Each project gets its own Git repository, pipeline configuration, and team settings.
- In your browser, go to dev.azure.com where you're already signed in.
- Select the Azure DevOps logo in the top left to open the Projects page.
- Click New project in the top right corner.
- Enter hello-cicd as the project name.
- Set Visibility to Private.
- Under Version control, select Git.
- Click Create.
Azure DevOps displays the welcome page for your new project. You now have an empty Git repository ready to hold your application code.
What is Azure Repos?
Azure Repos is the Git hosting service built into Azure DevOps. It works just like GitHub or GitLab, storing your code and tracking changes. Every Azure DevOps project automatically gets one Git repository with the same name as the project.
Don't see the New project button?
You must be a member of the Project Collection Administrators group to create projects. If you created the organization yourself, you already have this permission. If someone else owns the organization, ask them to grant you access.
help me find the New project button in Azure DevOps
Clone the repo and add a Node.js app
Cloning creates a local copy of your Azure Repos repository on your machine. You'll add your application files locally, then push them back to the cloud.
- In your Azure DevOps project, click Repos in the left sidebar.
- Click Clone in the upper-right corner of the Files view.
- Copy the clone URL. It looks like https://dev.azure.com/your-org/hello-cicd/_git/hello-cicd.
- Open your terminal by pressing Cmd+Space (macOS) or the Windows key (Windows), typing Terminal, and pressing Enter.
- Move to your Desktop by running this command:
cd ~/Desktop
- Clone your Azure Repos repository by running this command:
git clone [[CLONE_URL="your Azure Repos clone URL"]]
What does this command do?
git clone downloads a complete copy of the remote repository to your machine. It creates a new folder with the repository name (hello-cicd) and sets up a connection back to Azure Repos so you can push changes later.
- Move into the cloned folder by running:
cd hello-cicd
Seeing an authentication prompt?
Git may ask you to sign in when cloning from Azure Repos. If a browser window opens, sign in with your Microsoft account. If you see a credential prompt in the terminal, enter your Azure DevOps username and a Personal Access Token (PAT) as the password.
help me authenticate with Azure Repos during git clone
Now you'll create three files that make up your Node.js application. Open the hello-cicd folder in your preferred text editor to create and edit files.
- Create a new file named package.json and paste the following code:
{
"name": "hello-cicd",
"version": "1.0.0",
"description": "A simple Node.js app for Azure DevOps CI/CD demo",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "node test.js"
},
"dependencies": {
"express": "4.21.2"
}
}
What does this file do?
package.json defines your Node.js project. It lists your app's metadata, runnable scripts, and dependencies.
- scripts.start tells Node.js how to launch your app.
- scripts.test defines the command your pipeline will run to verify the app works.
- dependencies lists Express, a lightweight web framework that handles HTTP requests.
- Save package.json.
- Create a new file named index.js and paste the following code:
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send('Hello from Azure DevOps CI/CD!');
});
app.listen(port, () => {
console.log(`App running on port ${port}`);
});
module.exports = app;
What does this file do?
index.js is your web application. It creates a server that responds with a greeting when someone visits the root URL.
- process.env.PORT || 3000 lets Azure App Service assign its own port in production, while defaulting to port 3000 locally.
- module.exports = app makes the app importable by the test file.
- Save index.js.
- Create a new file named test.js and paste the following code:
const assert = require('assert');
const app = require('./index');
// Simple test to verify the app module loads
assert(app !== undefined, 'App should be defined');
console.log('All tests passed!');
process.exit(0);
What does this file do?
test.js is a basic test that confirms your app loads without crashing.
- It imports index.js and checks that the exported app object exists.
- process.exit(0) signals success to the pipeline. An exit code of 0 means all tests passed.
- Save test.js.
- Confirm all three files exist by running:
ls
You should see index.js, package.json, and test.js in the output.
Missing a file?
Make sure you saved all three files inside the hello-cicd folder on your Desktop, not in a parent or sibling directory. If a file is missing, check that your editor saved it to the correct location.
help me find my missing project files
✔️ Awesome, I've got everything!
Great. Make sure all three files are saved before moving on.
ⓧ I'd like to double check the full code
Here are all three files for comparison:
{
"name": "hello-cicd",
"version": "1.0.0",
"description": "A simple Node.js app for Azure DevOps CI/CD demo",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "node test.js"
},
"dependencies": {
"express": "4.21.2"
}
}
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send('Hello from Azure DevOps CI/CD!');
});
app.listen(port, () => {
console.log(`App running on port ${port}`);
});
module.exports = app;
const assert = require('assert');
const app = require('./index');
// Simple test to verify the app module loads
assert(app !== undefined, 'App should be defined');
console.log('All tests passed!');
process.exit(0);
Push the code to Azure Repos
Your files exist locally. Now you'll commit them to Git and push them to Azure Repos so they're stored in the cloud and ready for your pipeline.
- Stage, commit, and push all files by running these commands:
git add .
git commit -m "Add Node.js application"
git push origin main
What do these commands do?
- git add . stages all new files in the current folder for the next commit.
- git commit -m saves a snapshot of your staged changes with a descriptive message.
- git push origin main uploads your commit to the main branch on Azure Repos.
- Return to your browser and navigate to Repos > Files in your Azure DevOps project.
- Refresh the page if needed.
You should see index.js, package.json, and test.js listed in the repository file view.
Files not showing up in Azure Repos?
If the push failed, check your terminal for error messages. A common cause is an authentication issue. Make sure you signed in when prompted. Another cause is pushing to the wrong branch. Run git branch to verify you're on main.
help me push my code to Azure Repos
Your code is now live in Azure Repos. Next up, you'll create the Azure App Service that will host your deployed website.
Create the Azure App Service
Your code is safely stored in Azure Repos. Now you need a place in the cloud for that code to actually run.
In this step, you will create an Azure App Service Web App. This is the live server that will host your Node.js application. You will also connect Azure DevOps to your Azure subscription so the pipeline can deploy to this server automatically.
In this step, get ready to:
- Create an Azure App Service Web App with the free pricing plan.
- Create a service connection linking Azure DevOps to your Azure subscription.
Create a free-tier App Service
The App Service is where your Node.js app will live on the internet. Azure gives you a free tier so you can host it at no cost.
- Return to the Azure Portal at portal.azure.com.
- In the top search bar, type app service.
- Select App Services under the Services heading.
- Click Create, then select Web App.
You are now on the Create Web App page. Fill in the Basics tab with these settings:
- Under Subscription, confirm your active Azure subscription is selected.
- Under Resource group, click Create new. Enter a name like hello-cicd-rg and click OK.
- Under Name, enter a globally unique name for your web app. This name becomes part of your URL (your-app-name.azurewebsites.net), 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 a region close to you.
- Under Pricing plan, select Free F1 from the dropdown. If you need to create a new App Service plan first, click Create new and give it a name.
- Click Review + create at the bottom of the page.
- After validation passes, click Create.
- Wait for the deployment to complete, then click Go to resource.
What is Azure App Service?
Azure App Service is a fully managed platform for hosting web applications. It handles the server infrastructure for you, so you only need to provide your code.
The F1 (Free) tier gives you a live URL at no cost. It has limited resources, but it is perfect for learning and demo projects.
- On the App Service Overview page, find the Default domain field. It shows your app's URL in the format your-app-name.azurewebsites.net.
- Write down or copy your app name. You will need it when writing the pipeline YAML file.
Choose a memorable app name
Pick something short and recognizable like yourname-hello-cicd. The name must be unique across all of Azure, use only alphanumeric characters and hyphens, and cannot start or end with a hyphen.
Don't see Free F1 in the pricing list?
Some regions do not offer the Free tier for Linux. Try selecting a different region (for example, East US or West Europe) and the Free F1 option should appear.
If you still cannot find it, make sure you are looking under Pricing plan in the Pricing plans section at the bottom of the Basics tab.
help me find the Free F1 pricing plan for Azure App Service
Create a service connection in Azure DevOps
A service connection gives Azure DevOps permission to deploy code to your Azure subscription. Without it, the pipeline cannot reach your App Service.
- Switch back to your Azure DevOps project at dev.azure.com.
- In the bottom-left corner of the page, click Project Settings (the gear icon).
- In the left sidebar under Pipelines, click Service connections.
- Click New service connection (or Create service connection if this is your first one).
- Select Azure Resource Manager and click Next.
- Select Service Principal (automatic) and click Next.
- Under Subscription, select the Azure subscription that contains your App Service.
- Under Resource group, select the resource group you created earlier (for example, hello-cicd-rg).
- Under Service connection name, enter a descriptive name. Write this name down because you will reference it in your pipeline YAML.
- Check the box for Grant access permission to all pipelines.
- Click Save.
What is a service connection?
A service connection stores the credentials Azure DevOps needs to interact with external services. By using Service Principal (automatic), Azure DevOps creates a service principal in your Azure Active Directory and grants it permissions to deploy to the resource group you selected.
This means your pipeline can deploy to App Service without you manually pasting credentials anywhere.
Why grant access to all pipelines?
Checking this box allows any pipeline in your project to use this connection. For a single-pipeline learning project, this keeps things simple. In a production environment you would restrict access per-pipeline for security.
You should now see your new service connection listed on the Service connections page.
Getting a permissions error?
You need Owner or Contributor access on the Azure subscription to create a service principal. If you see an authorization error, check that you are signed in with the same Microsoft account you used for the Azure Portal.
If your account does not have sufficient permissions, ask your subscription administrator to grant you the Contributor role on the resource group.
help me fix a permissions error when creating a service connection in Azure DevOps
Your deployment target is ready and Azure DevOps has permission to push code to it. Next up, you will write the YAML pipeline that ties everything together.
Write Your YAML Pipeline
Your App Service is ready and your service connection links Azure DevOps to your Azure subscription. Now it's time to tell Azure DevOps exactly what to do when you push code.
You'll define your entire pipeline in a single YAML file called azure-pipelines.yml. This file describes two stages: a Build stage that installs, tests, and packages your app, and a Deploy stage that pushes it live to Azure.
In this step, get ready to:
- Create the pipeline YAML file with a Build stage.
- Add a Deploy stage that ships your app to Azure App Service.
- Commit and push the file to trigger pipeline detection.
Create the azure-pipelines.yml file with the Build stage
The Build stage is the Continuous Integration half of your pipeline. It installs dependencies, runs tests, and packages your app into a ZIP artifact that the Deploy stage can pick up.
- In your local clone of the hello-cicd repository, create a new file named azure-pipelines.yml at the root of the project (the same level as package.json).
- Paste the following code into azure-pipelines.yml:
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
azureSubscription: '[[SERVICE_CONNECTION="your-service-connection-name"]]'
webAppName: '[[APP_NAME="your-app-name"]]'
stages:
- stage: Build
displayName: 'Build Stage'
jobs:
- job: BuildJob
displayName: 'Build and Test'
steps:
- task: NodeTool@0
inputs:
versionSpec: '20.x'
displayName: 'Install Node.js'
- script: npm install
displayName: 'Install dependencies'
- script: npm test
displayName: 'Run tests'
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
replaceExistingArchive: true
displayName: 'Archive files'
- publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
artifact: drop
displayName: 'Publish artifact'
What does this code do?
trigger: - main tells Azure DevOps to run this pipeline every time someone pushes to the main branch. The pool section requests a Microsoft-hosted Linux build agent (ubuntu-latest).
The variables block stores your service connection name and app name so you can reuse them later without repeating yourself.
Inside the Build stage, the pipeline installs Node.js 20.x, runs npm install and npm test, then zips up the project and publishes it as an artifact named drop. The Deploy stage will download this artifact in the next chunk.
You won't see a change yet. The next chunk adds the Deploy stage that completes the pipeline.
Add the Deploy stage
The Deploy stage is the Continuous Deployment half. It downloads the artifact from the Build stage and deploys it to your Azure App Service.
- In azure-pipelines.yml, add the following code directly below the last line of the Build stage (after the Publish artifact step, at the same indentation level as - stage: Build):
- stage: Deploy
displayName: 'Deploy Stage'
dependsOn: Build
condition: succeeded()
jobs:
- deployment: DeployJob
displayName: 'Deploy to App Service'
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
inputs:
azureSubscription: '$(azureSubscription)'
appType: 'webAppLinux'
appName: '$(webAppName)'
package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip'
displayName: 'Deploy to Azure App Service'
What does this code do?
dependsOn: Build ensures the Deploy stage only runs after Build succeeds. condition: succeeded() adds an extra safety check that skips deployment if any previous stage failed.
The deployment job type (instead of a regular job) enables environment tracking in Azure DevOps. The AzureWebApp@1 task handles the actual deployment, pushing your zipped artifact to the App Service using your service connection credentials.
- Save azure-pipelines.yml.
Getting indentation errors?
YAML is sensitive to indentation. Make sure - stage: Deploy is at the same indent level as - stage: Build (two spaces in from stages:). Use spaces, not tabs.
help me fix YAML indentation in my azure-pipelines.yml file
✔️ Awesome, I've got everything!
Great. Double check you've saved azure-pipelines.yml before moving on.
ⓧ I'd like to double check the full code
Here is the complete azure-pipelines.yml file for comparison:
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
azureSubscription: '[[SERVICE_CONNECTION="your-service-connection-name"]]'
webAppName: '[[APP_NAME="your-app-name"]]'
stages:
- stage: Build
displayName: 'Build Stage'
jobs:
- job: BuildJob
displayName: 'Build and Test'
steps:
- task: NodeTool@0
inputs:
versionSpec: '20.x'
displayName: 'Install Node.js'
- script: npm install
displayName: 'Install dependencies'
- script: npm test
displayName: 'Run tests'
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
replaceExistingArchive: true
displayName: 'Archive files'
- publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
artifact: drop
displayName: 'Publish artifact'
- stage: Deploy
displayName: 'Deploy Stage'
dependsOn: Build
condition: succeeded()
jobs:
- deployment: DeployJob
displayName: 'Deploy to App Service'
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
inputs:
azureSubscription: '$(azureSubscription)'
appType: 'webAppLinux'
appName: '$(webAppName)'
package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip'
displayName: 'Deploy to Azure App Service'
Commit and push the YAML file
Azure DevOps automatically detects an azure-pipelines.yml file at the root of your repository. Once you push it, you'll be able to create a pipeline from it in the next step.
- Commit and push the new file by running these commands in your terminal:
git add azure-pipelines.yml
git commit -m "Add CI/CD pipeline"
git push
- Return to your Azure DevOps project in the browser.
- Click Repos in the left sidebar, then click Files.
- Confirm that azure-pipelines.yml appears in the file list alongside package.json, index.js, and test.js.
Don't see the file in Azure Repos?
Make sure you ran git push (not just git commit). Check that your terminal is inside the hello-cicd folder and that the remote points to your Azure Repos URL.
help me push my azure-pipelines.yml file to Azure Repos
Your pipeline definition is live in the repo. Next up, you'll create the pipeline in Azure DevOps and watch it build and deploy your app automatically.
Run Your Pipeline and Verify
Your pipeline YAML is written and pushed to Azure Repos. The definition is complete, but Azure DevOps does not know to treat it as a pipeline yet.
In this step, you will create the pipeline in Azure DevOps, watch it build and deploy your app, then prove the full CI/CD loop by pushing a code change and seeing it deploy automatically.
In this step, get ready to:
- Create your CI/CD pipeline in Azure DevOps.
- Watch the Build and Deploy stages run successfully.
- Verify the live app and trigger an automatic deployment with a code change.
Create the pipeline in Azure DevOps
Azure DevOps needs you to explicitly create a pipeline object that points to your YAML file. Once created, it will run automatically on every push to the main branch.
- In Azure DevOps, click Pipelines in the left sidebar.
- Click New pipeline (or Create pipeline if this is the first pipeline in your project).
- Under Where is your code?, select Azure Repos Git.
- Select your repository (e.g. hello-cicd).
- On the Configure your pipeline screen, select Existing Azure Pipelines YAML file.
- In the panel that appears, keep the branch as main and select /azure-pipelines.yml from the Path dropdown.
- Click Continue.
Azure DevOps displays a review of your YAML. This is the same file you already pushed.
What is happening here?
Azure DevOps is linking the pipeline object to the YAML file in your repository. From now on, any push to main triggers this pipeline automatically because of the trigger: - main line at the top of your file.
Watch the pipeline run
- Click Run in the top-right corner to save and start the pipeline.
- Azure DevOps takes you to the run summary page. You will see Build Stage and Deploy Stage listed.
What happens during the first run?
The pipeline may pause and show a message saying it needs permission to access a resource (like your service connection or the environment). This is normal for a first run.
If you see this, click View next to the message, then click Permit on the confirmation screen. This grants your pipeline access to the service connection and environment it needs.
- Click on Build Stage to expand its logs. Watch it install Node.js, run npm install, execute npm test, archive the files, and publish the artifact.
- Once the Build stage completes with a green checkmark, the Deploy stage starts automatically.
- Click on Deploy Stage to watch it deploy your ZIP artifact to Azure App Service.
- Wait for both stages to show green checkmarks. The entire run typically takes 2-4 minutes.
Pipeline stuck or not starting?
If your pipeline queues but never starts, your organization may not have the free parallelism grant yet. Check Organization settings > Parallel jobs to confirm you have at least 1 Microsoft-hosted parallel job. If you see zero, you need to wait for the grant you requested in Step 1 to be processed.
If the Deploy stage fails with a permission error on the service connection, click View and then Permit to authorize your pipeline to use the connection.
Help me troubleshoot my Azure DevOps pipeline run. It is either stuck, failing on permissions, or showing a parallelism error.
Verify your live app and trigger an automatic deployment
The pipeline has deployed your code to Azure App Service. Time to confirm it is live and prove the CI/CD loop works end-to-end.
- Open a new browser tab.
- Navigate to your App Service URL: your-app-name.azurewebsites.net.
- You should see the message Hello from Azure DevOps CI/CD! displayed in the browser.
Seeing a default Azure page instead?
App Service may take a minute or two to restart after the deployment. Refresh the page after 30 seconds. If the issue persists, go to your App Service in the Azure Portal, click Overview, and confirm the Status shows Running.
My App Service is not showing my deployed app. Help me figure out why.
Now prove that the CI/CD loop works by making a code change and watching it deploy automatically.
- In your local repository, open index.js.
- Find this line:
res.send('Hello from Azure DevOps CI/CD!');
- Change the message to something new, for example:
res.send('Hello from Azure DevOps CI/CD - Updated!');
- Save the file, then commit and push the change by running these commands:
git add .
git commit -m "Update greeting message"
git push
- Return to the Pipelines section in Azure DevOps.
- You should see a new pipeline run triggered automatically by your push.
- Wait for both stages to complete with green checkmarks again.
- Refresh your App Service URL in the browser. You should see your updated message.
What just happened?
You just completed the full CI/CD loop. You pushed code, the pipeline detected the change, built the app, ran the tests, and deployed it to your live website. No manual steps required.
This is the same workflow that professional engineering teams use every day to ship code quickly and safely.
Secret mission
Add a Staging Environment with Approval Gates
Your pipeline currently deploys straight to production on every push. In real teams, that's risky. Add a staging environment that catches issues first, then require manual approval before anything touches production.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely on free tiers, so there are no ongoing costs regardless of which option you choose.
Resources you used:
- Azure App Service (Production) on F1 Free tier.
- Azure App Service (Staging) on F1 Free tier.
- Azure DevOps project (including pipeline, repos, environments, and service connection).
- Local project files (package.json, index.js, test.js, azure-pipelines.yml).
Keep everything running
No action needed. Choose this if you are still actively building or want to experiment further with your pipeline.
- Both App Services remain on the F1 Free tier at $0/month.
- Azure DevOps is free for up to 5 users with 1,800 free pipeline minutes per month.
- Your local files stay on your machine ready for future edits.
Pause - I'll come back to this later
Shut down running processes to free up memory. Since everything is on free tiers, there is nothing to pause on the cloud side.
- Both App Services sit idle at no cost on the F1 tier. No action required.
- The pipeline only runs when you push code, so it will not consume minutes while idle.
- Close any open terminals or editor windows for the project on your local machine.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow every item below to fully clean up.
Delete Azure resources:
- In the Azure Portal, type the name of your resource group in the search box and select it.
- Click Delete resource group in the toolbar at the top of the resource group overview.
- Confirm deletion by typing the resource group name when prompted, then click Delete.
- This removes both your production and staging App Services and all associated resources in one action.
Delete the Azure DevOps project:
- Go to dev.azure.com and sign in to your organization.
- Click Organization settings in the bottom left.
- Select Projects in the left sidebar.
- Find your project, click the ellipsis (…) next to it, and choose Delete.
- Confirm by typing the project name and click Delete.
Delete local files:
- Remove your local project folder by running this command:
macOS
rm -rf ~/Desktop/hello-cicd
Windows
Remove-Item -Recurse -Force ~\Desktop\hello-cicd
Nice Work!
Nice Work!
Nice work! You just built a professional CI/CD pipeline from scratch using Azure DevOps and deployed a live web application to Azure App Service.
You've learned how to:
- Created a multi-stage YAML pipeline in Azure DevOps with separate Build and Deploy stages that run automatically on every push to the main branch.
- Automated building, testing, and deploying a Node.js application using industry-standard pipeline tasks like NodeTool@0, ArchiveFiles@2, and AzureWebApp@1.
- Demonstrated the full CI/CD loop end to end. A code push triggers the build, which triggers automatic deployment to a live Azure App Service URL.
- Added a staging environment with manual approval gates so production deployments require sign-off before going live.
Ready to quiz yourself?