Build a CI/CD Pipeline with Azure DevOps
Write a YAML pipeline that builds and tests a Node.js app automatically.
Introduction
30 Second Summary
Every time a developer pushes code to a shared repository, there's a risk that new changes break something. Teams need a way to automatically check that code still works before it reaches users.
In this project, you will build an automated pipeline that builds and tests a Node.js application every time code is pushed. You'll write the pipeline definition as code in a YAML file and watch it execute on a cloud-hosted agent.
What You'll Build
A multi-stage CI/CD pipeline in Azure DevOps that automatically builds, tests, and packages a Node.js application on every code push to the main branch.
By the end of this project, you'll have:
- A forked Node.js sample app on GitHub connected to Azure DevOps.
- An Azure DevOps organization and project with a fully configured pipeline.
- A YAML pipeline file (azure-pipelines.yml) that separates build and test into independent stages with published artifacts.
- Secret Mission: Deploy your app to Azure App Service to complete the full CI/CD loop.
Are there any prerequisites?
None. This guide includes full setup instructions for every tool you need, including Node.js, Git, GitHub, and Azure DevOps. No credit card is required to sign up for Azure DevOps.
Set Up Your Accounts and Tools
Before you can build an automated pipeline, you need the tools that make it possible. Azure DevOps runs your pipeline in the cloud, GitHub hosts your application code, and Node.js is the runtime your app depends on.
In this step, you will install Node.js and Git on your machine, create a GitHub account, and set up an Azure DevOps organization with your first project.
In this step, get ready to:
- Install Node.js and Git.
- Create a GitHub account.
- Set up an Azure DevOps organization and project.
Install Node.js and Git
Your pipeline will build a Node.js application, so you need the Node.js runtime installed locally to verify the app works before automating it. Git is the version control tool that connects your code to both GitHub and Azure DevOps.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal and press Enter to open it.
- Check if Node.js is already installed by running this command:
node --version
✔️ I see a version number
Node.js is installed and ready to go. Move on to checking Git below.
ⓧ Command not found
- Open https://nodejs.org in your browser.
- Download the LTS version (the button on the left).
- Run the installer and accept all the default options.
- Close and reopen your terminal, then run node --version again to confirm it works.
- Check if Git is already installed by running this command:
git --version
✔️ I see a version number
Git is installed and ready to go. You can move on to the next substep.
ⓧ Command not found
- Open https://git-scm.com in your browser.
- Click Download and run the installer for your operating system.
- Accept all the default options during installation.
- Close and reopen your terminal, then run git --version again to confirm it works.
Still seeing command not found?
On macOS, you may be prompted to install Xcode Command Line Tools when you first run git --version. Accept the prompt and wait for the installation to finish, then try again.
On Windows, make sure you checked the option to add Git to your PATH during installation. If you missed it, reinstall Git and watch for that checkbox.
Help me troubleshoot why node or git isn't recognized in my terminal.
Create a GitHub account
GitHub will host the application code that your pipeline builds. If you already have a GitHub account, skip ahead to the next substep.
- Open https://github.com in your browser.
- Click Sign up in the top right corner.
- Follow the prompts to enter your email, create a password, and choose a username.
- Verify your email address when GitHub sends you a confirmation.
- Once verified, sign in to GitHub so you are ready for the next step.
Sign up for Azure DevOps
Azure DevOps is the platform where your pipeline will live. It provides the build agents, pipeline editor, and dashboards you need to automate your workflow. No credit card is required to get started.
- Open https://dev.azure.com in your browser.
- Click Start free.
- Sign in with a Microsoft account. If you do not have one, click Create one! and follow the prompts to set one up.
What is a Microsoft account?
A Microsoft account is any email address registered with Microsoft (like an Outlook, Hotmail, or Live address). You can also register a personal Gmail or other email as a Microsoft account during signup.
- Once signed in, Azure DevOps will prompt you to create a new organization. Enter a name for your organization (your own name works fine) and select a region close to you.
- After your organization is created, click New project.
- Enter my-first-pipeline as the project name.
- Set the visibility to Private.
- Click Create project.
You should now see your project dashboard with sections like Boards, Repos, and Pipelines in the left sidebar.
Request your free pipeline grant now
New Azure DevOps organizations do not automatically receive the free parallel job grant needed to run pipelines. You must request it, and approval can take several business days.
Submit your request now at https://aka.ms/azpipelines-parallelism-request so it is approved by the time you need to run your pipeline. Provide your name, your organization name, and mention that you need the free grant for a private project.
If your grant has not been approved by the time you reach Step 3, your pipeline run will show an error. You can continue writing the YAML and come back to run it once the grant is processed.
Having trouble signing up?
If Azure DevOps asks you to "Try Azure for free" and redirects you to the Azure portal, navigate back to dev.azure.com directly and sign in with your Microsoft account. The Azure DevOps free tier does not require an Azure subscription.
Help me troubleshoot my Azure DevOps signup issue.
Your tools are locked and loaded. Next up, you will fork the sample Node.js app and connect it to your Azure DevOps project so the pipeline has code to build.
Fork the Sample App and Connect to Azure DevOps
Your accounts and tools are ready. Now you need an application for your pipeline to actually build and test.
You'll fork a sample Node.js app from Microsoft's official repository, then connect it to Azure DevOps so the pipeline knows where to find your code.
In this step, get ready to:
- Fork a sample Node.js repository on GitHub.
- Connect your GitHub account to Azure DevOps Pipelines.
- Select the starter pipeline template.
Fork the sample repository
Your pipeline needs code to build. Microsoft maintains an official sample repository with a simple Express.js app and a test suite. You'll fork it to create your own copy on GitHub.
- Open this URL in a new browser tab: https://github.com/MicrosoftDocs/pipelines-javascript.
- Click the Fork button in the top right corner of the page.
- On the Create a new fork page, leave all defaults as they are.
- Click Create fork.
GitHub redirects you to your new copy of the repository. The URL changes to show your username instead of MicrosoftDocs.
What is this repository?
The pipelines-javascript repo is a simple Node.js application with a package.json that includes build and test scripts. Your pipeline will run these scripts automatically every time you push code.
Don't see the Fork button?
Make sure you are signed into your GitHub account. The Fork button only appears when you are logged in.
help me find the Fork button on GitHub
Connect GitHub to Azure DevOps
Now you'll tell Azure DevOps where your code lives by connecting it to your GitHub account.
- Switch to your Azure DevOps browser tab.
- Make sure you are inside your my-first-pipeline project.
- Click Pipelines in the left navigation sidebar.
- Click New pipeline (or Create Pipeline if this is your first pipeline).
- On the Where is your code? screen, select GitHub.
- A pop-up window asks you to authorize Azure Pipelines to access your GitHub account. Click Authorize AzurePipelines to grant permission.
- After authorization, you'll see a list of your GitHub repositories. Select your forked pipelines-javascript repository.
Why connect GitHub instead of Azure Repos?
Azure DevOps has its own source control (Azure Repos), but most teams and open-source projects use GitHub. Connecting GitHub teaches you the more transferable skill and matches the workflow you'll encounter in real engineering teams.
Pipeline may not run yet
The free grant of parallel jobs for new Azure DevOps organizations is temporarily disabled. New organizations must request access before pipelines can execute on Microsoft-hosted agents.
To request your free grant, go to Organization Settings then Pipelines then Parallel jobs and submit a request. Approval takes several business days. You can still write your pipeline YAML while you wait.
Authorization pop-up not appearing?
Some browsers block pop-ups by default. Check for a blocked pop-up indicator in your browser's address bar and allow it.
help me authorize Azure Pipelines to access my GitHub account
Select the starter pipeline template
Azure DevOps analyzes your repository and suggests a pipeline template based on the code it finds. You'll skip the auto-generated template so you can learn how each piece of the YAML works.
- On the Configure your pipeline screen, Azure DevOps suggests a Node.js template because it detected a Node.js project.
- Select Starter pipeline instead.
The YAML editor opens with a minimal starter template. You'll see a trigger, a pool, and two placeholder script steps.
Why choose the starter pipeline?
The Node.js template gives you a pre-built pipeline, but you won't understand what each line does. Writing the YAML from scratch teaches you how triggers, pools, and steps work together. This is the difference between copying someone else's configuration and truly understanding pipeline-as-code.
Your pipeline editor is ready. Next up, you'll replace this starter content with real CI pipeline YAML that builds and tests your Node.js app.
Write Your CI Pipeline YAML
Your Azure DevOps project is connected to GitHub and the YAML editor is ready for you to write your pipeline.
A pipeline definition file tells Azure DevOps exactly when to run, where to run, and what to do. You will write each section from scratch so you understand how the pieces fit together.
In this step, get ready to:
- Write a YAML pipeline with a trigger, agent pool, and build steps.
- Add artifact publishing to package your build output.
- Save and run the pipeline to watch it execute in real time.
Replace the starter content with your pipeline YAML
Every YAML pipeline has three core sections. The trigger defines when the pipeline runs. The pool defines where it runs (which virtual machine image). The stages section defines what it does.
- Select all the existing starter content in the YAML editor.
- Delete it and replace it with this code:
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: Build
displayName: 'Build Stage'
jobs:
- job: BuildJob
displayName: 'Install and Build'
steps:
- task: NodeTool@0
inputs:
versionSpec: '18.x'
displayName: 'Install Node.js 18.x'
- script: npm install
displayName: 'Install dependencies'
- script: npm run build --if-present
displayName: 'Build application'
What does this YAML do?
- trigger tells Azure Pipelines to run automatically whenever code is pushed to the main branch.
- pool assigns a Microsoft-hosted build agent running Ubuntu Linux to execute the work.
- stages organizes the pipeline into logical groups. Here you have one stage called Build containing one job with three steps.
- NodeTool@0 installs Node.js 18.x on the agent so your app can build.
- npm install downloads all project dependencies listed in package.json.
- npm run build --if-present runs the build script if the project defines one. The --if-present flag prevents failures when no build script exists.
You will not see a visible change yet. The next chunk adds the final piece before you save and run.
Seeing red squiggly lines in the editor?
- YAML is indentation-sensitive. Make sure each level is indented with exactly two spaces, not tabs.
- Check that you deleted all of the original starter content before pasting. Leftover lines will cause syntax errors.
help me fix YAML indentation errors in my Azure DevOps pipeline editor
Add build artifact publishing
A CI pipeline that builds your code is useful, but teams also need to keep a copy of the build output. Publishing an artifact saves the result so it can be deployed or inspected later.
- In the YAML editor, place your cursor at the end of the last line (after displayName: 'Build application').
- Press Enter to create a new blank line.
- Add this code below, making sure the indentation matches the steps above (10 spaces for the - task line):
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: '$(System.DefaultWorkingDirectory)'
artifactName: 'app-build'
displayName: 'Publish build artifact'
What does this task do?
PublishBuildArtifacts@1 takes the contents of a directory and uploads them to Azure DevOps as a named artifact.
- pathToPublish points to $(System.DefaultWorkingDirectory), which is the folder where your repository was cloned on the build agent.
- artifactName gives the artifact a label (app-build) so you can find it later in the pipeline run results.
You will not see a change yet. The next substep saves and runs the pipeline.
Not sure about indentation?
The - task: PublishBuildArtifacts@1 line should start at the same column as - task: NodeTool@0 and - script: npm install above it. Count 10 spaces from the left edge.
help me align my PublishBuildArtifacts task with the other steps in my YAML pipeline
✔️ Awesome, I've got everything!
Great. Double check you have no red error indicators in the editor before saving.
ⓧ I'd like to double check the full code
Here is the complete azure-pipelines.yml as it should look in the editor:
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: Build
displayName: 'Build Stage'
jobs:
- job: BuildJob
displayName: 'Install and Build'
steps:
- task: NodeTool@0
inputs:
versionSpec: '18.x'
displayName: 'Install Node.js 18.x'
- script: npm install
displayName: 'Install dependencies'
- script: npm run build --if-present
displayName: 'Build application'
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: '$(System.DefaultWorkingDirectory)'
artifactName: 'app-build'
displayName: 'Publish build artifact'
Save and run the pipeline
With the YAML written, you can now commit it to your repository and trigger the first pipeline run in a single action.
- Click Save and run in the top right corner of the editor.
- A dialog appears asking where to commit. Leave the default option to commit directly to the main branch.
- Click Save and run again in the dialog to confirm.
What just happened?
Azure DevOps committed the azure-pipelines.yml file to the main branch of your forked pipelines-javascript repository on GitHub.
Because the pipeline trigger watches the main branch, this commit immediately starts the first pipeline run. The run executes on a Microsoft-hosted agent running ubuntu-latest.
- You are now on the pipeline run page. Watch the build logs appear in real time as each step executes.
- Wait for the Build Stage to finish. You should see a green checkmark next to Build Stage when it completes successfully.
Pipeline stuck or showing a parallelism error?
If your pipeline never starts running, or you see an error that says No hosted parallelism has been purchased or granted, your new Azure DevOps organization has not yet received the free parallelism grant.
Microsoft temporarily requires new organizations to request the free grant manually. Submit a request at https://aka.ms/azpipelines-parallelism-request. Approval can take 2-3 business days.
help me resolve the hosted parallelism error in Azure DevOps
Your first pipeline run is complete. Next up, you will extend this pipeline into multiple stages so that build and test run as separate, independent phases.
Extend to a Multi-Stage Pipeline
Your pipeline builds and packages your app in a single stage. That works, but real-world pipelines separate concerns so each phase can fail independently.
In this step, you'll add a dedicated Test stage to your pipeline. You'll also configure it to only run after the Build stage succeeds, mirroring how professional teams prevent broken builds from wasting test resources.
In this step, get ready to:
- Add a Test stage that runs your app's test suite.
- Configure stage dependencies so tests only run after a successful build.
- Trigger the pipeline and verify both stages pass with green checkmarks.
Add a Test stage to your pipeline
Right now your pipeline has one stage (Build) that installs dependencies, builds the app, and publishes an artifact. You'll add a second stage that runs the test suite in isolation.
- In the Azure DevOps left sidebar, click Pipelines.
- Click on your pipeline in the list.
- Click Edit in the top right corner to open the YAML editor.
- Scroll to the bottom of your existing YAML, after the last line of the Build stage.
- Add the following Test stage code below the Build stage by pasting it at the end of the file:
- stage: Test
displayName: 'Test Stage'
dependsOn: Build
jobs:
- job: TestJob
displayName: 'Run Tests'
steps:
- task: NodeTool@0
inputs:
versionSpec: '18.x'
displayName: 'Install Node.js 18.x'
- script: npm install
displayName: 'Install dependencies'
- script: npm test
displayName: 'Run tests'
What does this code do?
- stage: Test declares a new stage named Test that runs separately from the Build stage.
- dependsOn: Build tells Azure Pipelines to only start this stage after the Build stage passes. If Build fails, Test is skipped entirely.
- The steps install Node.js and dependencies fresh on a new agent, then run npm test to execute the app's test suite.
Why use dependsOn?
Without dependsOn, stages run in parallel by default. That means your tests could start before the build finishes, wasting agent time on code that might not even compile.
By making Test depend on Build, you create a sequential pipeline. Tests only run on code that already built successfully. This is how professional teams avoid running expensive test suites against broken builds.
Commit and verify the multi-stage pipeline
- Click Save in the top right corner of the YAML editor.
- In the commit dialog, leave the commit message as-is or type a message like Add Test stage with dependsOn.
- Select Commit directly to the main branch.
- Click Save.
Azure Pipelines automatically detects the commit to main and triggers a new pipeline run.
- Click Pipelines in the left sidebar to see your runs.
- Click on the latest run to open it.
- You should see two stages displayed side by side: Build Stage and Test Stage.
- Wait for both stages to complete. Each stage should show a green checkmark when it passes.
Seeing a failed stage?
- Click on the failed stage to expand its logs. Look for YAML indentation errors. Every level in YAML must be indented with exactly two spaces, not tabs.
- Make sure - stage: Test is at the same indentation level as - stage: Build (two spaces inside the stages: block).
- If you see a test failure rather than a YAML error, that means your pipeline structure is correct but the app's tests found an issue. Check the test output for details.
help me debug my failing Azure DevOps pipeline stage
✔️ Awesome, I've got everything!
Great. Double check you see both stages passing with green checkmarks before moving on.
ⓧ I'd like to double check the full code
Here is the complete azure-pipelines.yml file after this step:
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: Build
displayName: 'Build Stage'
jobs:
- job: BuildJob
displayName: 'Install and Build'
steps:
- task: NodeTool@0
inputs:
versionSpec: '18.x'
displayName: 'Install Node.js 18.x'
- script: npm install
displayName: 'Install dependencies'
- script: npm run build --if-present
displayName: 'Build application'
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: '$(System.DefaultWorkingDirectory)'
artifactName: 'app-build'
displayName: 'Publish build artifact'
- stage: Test
displayName: 'Test Stage'
dependsOn: Build
jobs:
- job: TestJob
displayName: 'Run Tests'
steps:
- task: NodeTool@0
inputs:
versionSpec: '18.x'
displayName: 'Install Node.js 18.x'
- script: npm install
displayName: 'Install dependencies'
- script: npm test
displayName: 'Run tests'
You now have a multi-stage CI pipeline that builds, packages, and tests your app automatically on every push. In the Secret Mission, you'll take this even further by adding a deployment stage that pushes your app live to Azure App Service.
Secret mission
Add a Deployment Stage
Your pipeline builds and tests your app automatically. But code that passes all tests still needs to reach users. In this secret mission, you'll add a Deploy stage that pushes your app to Azure App Service, completing the full CI/CD loop.
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 uses Azure DevOps and GitHub free tiers, so there are no ongoing costs.
Resources you used:
- Azure DevOps organization and project (my-first-pipeline).
- GitHub forked repository (pipelines-javascript).
- Node.js installed locally.
- Git installed locally.
Keep everything running
No action needed. Choose this if you are still actively building or plan to extend this pipeline further.
- Your Azure DevOps project and pipeline remain available at dev.azure.com.
- Your GitHub fork stays in your account for future reference.
- Node.js and Git remain installed on your machine for other projects.
Pause - I'll come back to this later
There is nothing to pause for this project. Azure DevOps Pipelines only run when you push code, so the pipeline does not consume resources while idle.
- Leave everything as-is. The pipeline only triggers on new pushes to the main branch.
- No charges accrue while the pipeline is idle.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow these steps to delete everything.
Delete your Azure DevOps project:
- Sign in to your organization at dev.azure.com.
- Select Organization settings in the bottom-left corner.
- Select Projects in the left sidebar.
- Check the box next to my-first-pipeline, then click Delete.
- Confirm by typing the project name and clicking Delete.
Delete your GitHub forked repository:
- Navigate to your forked pipelines-javascript repository on GitHub.
- Click Settings (under the repository name).
- Scroll down to the Danger Zone section and click Delete this repository.
- Click I want to delete this repository.
- Click I have read and understand these effects.
- Type the repository name to confirm, then click Delete this repository.
Uninstall Node.js (optional):
- On Windows, open Settings, select Apps, find Node.js in the list, and click Uninstall.
- On macOS, if you installed via the official installer, remove it by running the commands in the Node.js uninstall guide for your platform.
Uninstall Git (optional):
- On Windows, open Settings, select Apps, find Git in the list, and click Uninstall.
- On macOS, Git is bundled with Xcode Command Line Tools. Removing it is not recommended as many other tools depend on it.
That's a Wrap!
That's a Wrap!
Nice work! You just built a fully automated CI pipeline from scratch using Azure DevOps.
You've learned how to:
- Write a YAML pipeline from scratch that builds, tests, and packages a Node.js app automatically.
- Connect a GitHub repository to Azure Pipelines with automatic triggers on every push to the main branch.
- Structure a pipeline into multiple stages with dependencies, mirroring how professional engineering teams control execution flow.
- Deployed your app to Azure App Service with a third pipeline stage to complete the full CI/CD loop.
Ready to quiz yourself?