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:
- Selects an Ubuntu-hosted build agent.
- Installs the required .NET SDK.
- Restores application dependencies and builds the project.
- 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: falsetask: 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'
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)