DEV Community

Cover image for How to Build a CI/CD Pipeline Using Azure DevOps: A Practical Guide
Pavani
Pavani

Posted on Edited on

How to Build a CI/CD Pipeline Using Azure DevOps: A Practical Guide

Modern software teams need reliable ways to build, test, and deploy applications. Manually compiling code, running tests, copying deployment packages, and releasing applications can introduce errors and slow down development.

Continuous Integration and Continuous Delivery (CI/CD) help automate these activities. Azure DevOps provides tools such as Azure Repos, Azure Pipelines, and Azure Artifacts to support the software delivery lifecycle.

In this guide, we will build a basic CI/CD workflow using Azure Pipelines and YAML. The example uses a simple application, but the same principles can be adapted to different technology stacks.

1. Understand the Azure DevOps CI/CD Workflow

A typical CI/CD pipeline contains several stages:

• Source: Developers push code to a Git repository.
• Build: The pipeline restores dependencies and compiles the application.
• Test: Automated tests validate the changes.
• Package: The build output is published as an artifact.
• Deploy: The artifact is deployed to a target environment.
• Validate: The deployment is checked to confirm that the application is running correctly.

Azure Pipelines can automate these stages whenever code is pushed or a pull request is created.

A useful starting architecture is:

Developer → Azure Repos → Azure Pipeline → Build → Test → Artifact → Deploy

The pipeline definition is stored as code in a YAML file, making it easier to review, version, and maintain.

2. Create an Azure DevOps Project and Repository

First, create an organisation and project in Azure DevOps.

Then create or import a Git repository containing your application.

For this example, assume the repository contains a simple .NET application.

Your repository might look like this:

my-application/
├── src/
│ └── MyApplication/
│ └── MyApplication.csproj
├── tests/
│ └── MyApplication.Tests/
│ └── MyApplication.Tests.csproj
├── .gitignore
└── README.md

Keep application code and test projects organised. This makes it easier to configure pipeline tasks and maintain the project as it grows.

3. Configure a CI Pipeline Using YAML

Continuous Integration automatically validates code changes. When a developer pushes code, the pipeline can restore dependencies, compile the application, and execute tests.

Create a file named azure-pipelines.yml in the repository root.

Here is a basic CI pipeline:

trigger:
branches:
include:
- main

pool:
vmImage: 'ubuntu-latest'

variables:
buildConfiguration: 'Release'

steps:

  • task: UseDotNet@2
    displayName: 'Install .NET SDK'
    inputs:
    packageType: 'sdk'
    version: '8.x'

  • script: dotnet restore
    displayName: 'Restore dependencies'

  • script: dotnet build --configuration $(buildConfiguration) --no-restore
    displayName: 'Build application'

  • script: dotnet test --configuration $(buildConfiguration) --no-build
    displayName: 'Run automated tests'

This pipeline performs four main operations:

  1. Selects an Ubuntu-hosted build agent.
  2. Installs the required .NET SDK.
  3. Restores application dependencies and builds the project.
  4. Executes automated tests.

The trigger section ensures that pushes to the main branch start the pipeline.

For a production repository, consider adding pull-request validation so that changes are tested before they are merged.

4. Publish Build Artifacts

After the application is built and tested, the output should be stored as a pipeline artifact.

Artifacts separate the build process from deployment. This means the same tested package can be promoted through different environments without rebuilding it each time.

Add the following task after the build and test steps:

  • task: DotNetCoreCLI@2
    displayName: 'Publish application'
    inputs:
    command: 'publish'
    projects: 'src/MyApplication/MyApplication.csproj'
    arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)'
    publishWebProjects: false

  • task: PublishPipelineArtifact@1
    displayName: 'Publish build artifact'
    inputs:
    targetPath: '$(Build.ArtifactStagingDirectory)'
    artifact: 'application-package'

The publish command generates the deployment output. The artifact task stores it so a later deployment stage can download it.

Avoid placing credentials, private keys, or connection strings inside the published package.

5. Configure a CD Pipeline

Continuous Delivery automates the release process after successful validation.

For this example, assume the application will be deployed to an Azure App Service.

Before configuring deployment:

• Create the target App Service.
• Configure the required runtime.
• Create an Azure Resource Manager service connection in Azure DevOps.
• Grant the connection only the permissions it needs.
• Store sensitive configuration outside the source repository.

Add a deployment job that downloads the artifact and deploys it:

  • stage: Deploy displayName: 'Deploy application' dependsOn: Build condition: succeeded()

jobs:
- deployment: DeployWeb
displayName: 'Deploy to Azure App Service'
environment: 'development'

  strategy:
    runOnce:
      deploy:
        steps:
          - download: current
            artifact: application-package

          - task: AzureWebApp@1
            displayName: 'Deploy web application'
            inputs:
              azureSubscription: 'YOUR-SERVICE-CONNECTION'
              appType: 'webApp'
              appName: 'YOUR-APP-SERVICE-NAME'
              package: '$(Pipeline.Workspace)/application-package'
Enter fullscreen mode Exit fullscreen mode

Replace the placeholder values with your actual service connection and App Service name.

The deployment job uses an environment named development. Azure DevOps environments can also support approvals, deployment history, and checks depending on your configuration.

6. Add Quality Checks Before Deployment

A successful build does not automatically mean that an application is ready for release.

Add quality checks to catch problems earlier.

Consider including:

• Unit tests to validate individual components.
• Integration tests to check interactions between services.
• Code quality analysis to identify maintainability issues.
• Dependency scanning to identify vulnerable packages.
• Secret scanning to detect accidentally committed credentials.
• Configuration validation before deployment.

For example, a deployment stage should depend on successful completion of the build and test stages. If tests fail, the deployment should not proceed.

For production environments, configure approval checks and restrict who can approve releases.

7. Manage Secrets and Permissions Securely

Security is an important part of pipeline design.

Do not hardcode passwords, API keys, access tokens, or database credentials in YAML files.

Use Azure DevOps secret variables, variable groups, or Azure Key Vault integration to manage sensitive configuration.

Follow these practices:

• Apply least-privilege permissions to service connections.
• Use separate connections for different environments when appropriate.
• Avoid printing secrets in pipeline logs.
• Restrict access to production deployment environments.
• Review pipeline permissions regularly.
• Rotate credentials according to your organisation's security policies.

For cloud deployments, prefer managed identities where supported instead of storing long-lived credentials.

8. Troubleshoot Common Pipeline Failures

Pipeline failures are part of everyday DevOps work. Understanding how to investigate them is as important as writing the YAML configuration.

Dependency restoration fails

Check package sources, network connectivity, SDK versions, and authentication to private feeds.

Build succeeds but tests fail

Review test output and logs. Confirm that test data, environment variables, and dependencies are configured correctly.

Artifact cannot be downloaded

Verify the artifact name, publishing task, and pipeline stage dependencies.

Azure deployment fails

Check the service connection, role assignments, App Service name, deployment package path, and target environment.

Application deploys but does not start

Review application logs, runtime configuration, startup commands, environment variables, and health endpoints.

A good troubleshooting approach is to identify the first failing task and investigate its logs before changing unrelated pipeline settings.

9. Improve the Pipeline as the Project Grows

Once the basic pipeline works, improve it incrementally.

Useful improvements include:

• Separate build, test, and deployment stages.
• Use templates to avoid repeated YAML.
• Cache dependencies where appropriate.
• Run different test suites in parallel.
• Use separate development, staging, and production environments.
• Add deployment approvals and rollback procedures.
• Monitor deployment success and application health.

Avoid adding unnecessary complexity at the beginning. Build a reliable basic pipeline first, then introduce additional automation based on actual project requirements.

Learning Azure DevOps Through Practical Projects

Understanding CI/CD becomes easier when you build and troubleshoot pipelines yourself. When evaluating an Azure course in Bangalore, look for practical exercises involving Git repositories, YAML pipelines, automated testing, cloud deployment, and release management.

A structured Azure training in Bangalore programme should help learners understand how development workflows connect with cloud infrastructure and operational practices.

Eduleem School of Cloud and AI offers affordable fees, expert and certified trainers, hands-on labs, one year of LMS access, resume guidance, mock interview preparation, and placement support.

New batch starting soon!

Final Thoughts

Building a CI/CD pipeline using Azure DevOps is a useful project for understanding modern software delivery. It connects source control, automated builds, testing, artifact management, deployment, and security into one workflow.

Start with a simple application and a basic YAML pipeline. Once it works reliably, add quality checks, secure credentials, deployment environments, and monitoring.

The objective is not simply to automate deployment. It is to create a repeatable and reliable process that helps engineering teams deliver software with greater confidence.

Top comments (0)