Create a CI Build Pipeline
Learn how to create a CI build pipeline in Azure. This lesson covers the core concept, step-by-step setup, hands-on exercise, troubleshooting, and next steps. Master the essentials and improve your DevOps workflow.
Focus: create a ci build pipeline
You've got your code compiling locally, but the moment a teammate pushes a change, the build breaks — and nobody notices until the end of the sprint. That pain is real, and it's exactly why you need a CI build pipeline: to catch broken builds early, automate the repetitive work, and give your team confidence in every commit. In this lesson, you'll learn what a CI build pipeline is, how to create one in Azure DevOps, and how to connect it to the next steps in your Azure learning path.
The problem this lesson solves
Without a CI build pipeline, your team relies on manual builds — someone runs dotnet build or npm run build on their own machine, prays it works, and then spends hours fixing integration issues on release day. This approach is slow, error-prone, and scales terribly. As your Azure project grows — adding functions, web apps, or containerized services — the risk of a silent build break multiplies. The same frustration applies to Python projects, JS apps, or any language: a missing dependency, a syntax error, or an environment mismatch can derail your deployment.
A CI build pipeline automates the build process: every time a developer pushes code, the pipeline checks out the repository, restores dependencies, compiles the project, runs tests, and produces artifacts — all in a clean, repeatable environment. This gives you fast feedback, reduces manual work, and ensures that what gets tested is what gets deployed.
Core concept / mental model
Think of a CI build pipeline as an automated assembly line for your code. You have a starting point (source code), a series of stations (steps like restore, build, test, publish), and an output (build artifacts). The pipeline runs automatically when code changes, so the assembly line is always moving.
In Azure DevOps, a pipeline is defined in a YAML file (or through the classic visual editor). The pipeline is made of stages, jobs, steps, and tasks:
- Stage: A high-level phase (e.g., Build, Test, Deploy)
- Job: A collection of steps that runs on an agent (the build machine)
- Step: A single unit of work (e.g., run a script, execute a task)
- Task: A pre-built step (e.g.,
UsePythonVersion,dotnet build,PublishBuildArtifacts)
The pipeline runs on a Microsoft-hosted agent (a clean virtual machine with common tools pre-installed) or a self-hosted agent you manage. Each run creates a new agent for isolation.
Pro tip: Think of the pipeline as a recipe: the YAML file is the ingredients list and steps, and the agent is the kitchen. If the kitchen is clean (fresh agent), you won't get weird flavors from previous meals.
How it works step by step
Creating a CI build pipeline in Azure DevOps follows a logical sequence. Whether you use YAML or the classic editor, the flow stays the same:
- Get your code into Azure Repos (or GitHub, Bitbucket, etc.).
- Define the pipeline in your repository as
azure-pipelines.yml. - Point Azure DevOps to the pipeline and connect it to your repository.
- Add the build steps: restore dependencies, build the project, run tests, publish artifacts.
- Trigger the pipeline manually or automatically on commits and pull requests.
- Inspect the run logs and fix any failures.
- Use the published artifacts in a later release stage.
For a Python project, the steps map to: install Python, install dependencies with pip, run tests with pytest, and optionally build a package. For a .NET app, it's dotnet restore, dotnet build, dotnet test, and dotnet publish.
Hands-on walkthrough
Let's build a CI pipeline for a simple Python app. We'll assume you have an Azure DevOps organization and a project with a repository (or az devops CLI setup).
1. Create the pipeline file
Create azure-pipelines.yml in the root of your repository:
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.10'
addToPath: true
- script: |
python -m pip install --upgrade pip
pip install -r requirements.txt
displayName: 'Install dependencies'
- script: |
python -m pytest
displayName: 'Run tests'
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
condition: succeeded()
This pipeline triggers on every push to main, runs on an Ubuntu agent, installs Python 3.10, installs dependencies from requirements.txt, runs pytest, and publishes build artifacts.
2. Push and create the pipeline
If you have the code locally, push it to your Azure Repo:
git add .
git commit -m "Add CI pipeline"
git push origin main
Then, in Azure DevOps, go to Pipelines > Create Pipeline and select the repository and the azure-pipelines.yml file. Alternatively, you can use the CLI:
az devops configure --defaults organization=https://dev.azure.com/yourorg project=YourProject
az pipelines create --name "CI Pipeline" --repository-type tfsgit --repository-name YourRepo --branch main --yaml-path azure-pipelines.yml
3. Run and observe
The pipeline will run automatically. Expect output similar to this (truncated):
Starting: Run tests
==============================================================================
Task : Command line
Description : Run a command line script using bash
==============================================================================
/usr/bin/bash --noprofile --norc -e -o pipefail /home/vsts/work/_temp/6cc33c9c-5e2e-4c8d-9b7c-0d7e6f4a2b1e.sh
============================= test session starts =============================
platform linux -- Python 3.10.12, pytest-7.4.0
collected 5 items
tests/test_app.py ..... [100%]
============================== 5 passed in 0.12s ==============================
Finishing: Run tests
If a test fails, the pipeline stops (unless you use continueOnError), and you see a red X in the pipeline results.
Compare options / when to choose what
You have several choices when creating a CI pipeline in Azure. Here's how to decide:
| Option | Best for | Pros | Cons |
|---|---|---|---|
| Azure Pipelines (YAML) | Version-controlled pipelines, code review | Pipeline as code, reusable templates, easy branching | Learning curve for YAML syntax |
| Azure Pipelines (Classic) | Simple projects, quick setup | Visual editor, no YAML required | Hard to version control, less portable |
| GitHub Actions | Repos hosted on GitHub | Tight GitHub integration, huge marketplace | Separate ecosystem from Azure DevOps |
| Jenkins / other | Existing CI tooling, on-prem | Familiar to many teams, plugin ecosystem | More ops overhead, not Azure-native |
For new Azure projects, YAML pipelines are the recommended choice because they're declarative, reviewable, and align with infrastructure-as-code practices.
Troubleshooting & edge cases
Even a simple pipeline can fail. Here are common issues and fixes:
Command not found: python— The agent may not see your Python. Use theUsePythonVersiontask and setaddToPath: true. Alternatively, usepython3explicitly.- Dependency install fails — Check
requirements.txtfor typos or incompatible versions. Usepip installwith--no-cache-dirto avoid stale caches. - Tests pass locally but fail in CI — This is a environment difference. Pin exact dependency versions in
requirements.txt, and avoid relying on the developer's machine (e.g., don't use local paths). - Artifact not published — Ensure the task runs after a successful build. Use
condition: succeeded()to only publish on success. - Pipeline not triggering — Check the
triggerbranches. If you're on a feature branch, commit and push to that branch; the pipeline won't run iftriggeris set tomainonly. azCLI not found — Use theAzureCLI@2task or install the CLI in a script step.
What you learned & what's next
You now understand the core concept of a CI build pipeline, can create a YAML-based pipeline in Azure DevOps, and can troubleshoot common failures. You've seen how to run Python tests in a clean agent and publish artifacts for later use.
This is a foundational skill for your DevOps journey. Next in the tuple track, you'll learn how to take these build artifacts and deploy them to Azure services like App Service or Azure Functions — the CD half of CI/CD. With a solid CI pipeline in place, you're ready to automate the entire release process.
Key takeaway: The CI build pipeline is your guardrail — catch issues early, automate the boring stuff, and never ship a broken build again.
Practice recap
Create a new azure-pipelines.yml for a small sample project (Python or .NET). Push it to your repo, run the pipeline, and deliberately introduce a failing test — observe the red X in the pipeline results. Then fix the test and rerun. Next, add a PublishBuildArtifacts step and confirm the artifact appears in the pipeline summary. This hands-on loop will cement the concepts.
Common mistakes
- Forgetting to pin dependency versions in requirements.txt — CI runs on a fresh environment, so unpinned ranges can pull breaking updates.
- Setting
triggerto onlymainand wondering why feature branch pushes don't run tests. - Ignoring environment differences: code that works locally but fails on the agent due to case-sensitive filesystems or missing system packages.
- Not publishing artifacts — without a
PublishBuildArtifactsstep, the release pipeline has nothing to deploy. - Using
condition: always()on publish steps — this publishes even when the build failed, masking failures.
Variations
- Use variables and templates to parameterize your pipeline (e.g., Python version, build configuration) for reuse across branches and projects.
- Replace the Azure Pipelines YAML with GitHub Actions if your code lives on GitHub — the logic is similar but uses a different syntax.
- For .NET projects, swap the Python steps for
dotnet restore,dotnet build, anddotnet testtasks; the pipeline skeleton stays the same.
Real-world use cases
- Automating builds and tests for a multi-developer web app so every pull request is validated before merge.
- Building a Python package and publishing it as a feed artifact for downstream containerized deployments.
- Running code quality gates (linting, security scans) in CI as part of a compliance-driven release process.
Key takeaways
- A CI build pipeline automates the build and test process for every code push, giving fast feedback.
- Azure Pipelines YAML is the modern, version-controlled way to define a pipeline.
- The pipeline runs on a clean agent, so dependencies must be declared explicitly.
- Each pipeline is composed of stages, jobs, steps, and tasks — understand the hierarchy to debug effectively.
- Publish build artifacts so your CI output can feed into a release pipeline.
- Troubleshooting is systematic: check the environment, dependencies, and conditions in your YAML.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.