Azure DevOps Complete Guide
Introduction
Azure DevOps is Microsoft's comprehensive DevOps platform. This guide covers Azure Boards, Repos, Pipelines, Test Plans, Artifacts, and integration with Azure cloud services for enterprise-grade DevOps workflows.
1. Azure DevOps Setup
# Create Azure DevOps organization
# Visit: https://dev.azure.com
# Install Azure CLI
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# Login
az login
# Install Azure DevOps extension
az extension add --name azure-devops
# Configure defaults
az devops configure --defaults \
organization=https://dev.azure.com/myorg \
project=MyProject
# Create project
az devops project create \
--name MyProject \
--description "Production application" \
--visibility private \
--source-control git \
--process agile
# List projects
az devops project list --output table
# Personal Access Token (PAT)
# Settings → Personal Access Tokens → New Token
# Scopes: Full access or specific permissions
# Set PAT for CLI
export AZURE_DEVOPS_EXT_PAT=your_pat_token
2. Azure Boards - Agile Planning
# Azure Boards hierarchy
# Epic → Feature → User Story → Task
# Create work items via CLI
az boards work-item create \
--title "User Authentication Feature" \
--type "Feature" \
--assigned-to "user@example.com" \
--area "MyProject\\Backend" \
--iteration "MyProject\\Sprint 1"
# Create user story
az boards work-item create \
--title "Implement OAuth2 login" \
--type "User Story" \
--parent-id 123 \
--assigned-to "dev@example.com" \
--description "As a user, I want to log in with Google"
# Create task
az boards work-item create \
--title "Set up OAuth2 provider" \
--type "Task" \
--parent-id 124 \
--assigned-to "dev@example.com"
# Update work item
az boards work-item update \
--id 125 \
--state "Active" \
--discussion "Started implementation"
# Query work items
az boards query \
--wiql "SELECT [System.Id], [System.Title], [System.State]
FROM WorkItems
WHERE [System.WorkItemType] = 'User Story'
AND [System.State] = 'Active'"
# Board columns
# New → Active → Resolved → Closed
# Sprints (Iterations)
# Settings → Project configuration → Iterations
# Sprint 1, Sprint 2, Sprint 3...
# Capacity planning
# Set team capacity (hours per day)
# Track velocity (completed story points)
# Burndown chart
# Shows work remaining vs. time
3. Azure Repos - Git Repositories
# Create repository
az repos create --name myapp
# Clone repository
git clone https://dev.azure.com/myorg/MyProject/_git/myapp
# Branch policies
az repos policy create \
--repository-id repo-id \
--branch main \
--blocking true \
--enabled true \
--minimum-approver-count 2
# Branch policy settings:
- Require minimum number of reviewers
- Check for linked work items
- Check for comment resolution
- Build validation
- Automatically included reviewers
# Pull request via CLI
az repos pr create \
--title "Add user authentication" \
--description "Implements OAuth2 login" \
--source-branch feature/auth \
--target-branch main \
--work-items 123 124
# List PRs
az repos pr list --output table
# Add reviewer
az repos pr reviewer add \
--id 42 \
--reviewers reviewer@example.com
# Approve PR
az repos pr set-vote --id 42 --vote approve
# Complete PR (merge)
az repos pr update --id 42 --status completed
# Code search
# Search across all repositories
# Regex support
# File filters
# Git hooks
# Pre-commit, pre-push validation
# Integrate with work item tracking
4. Azure Pipelines - CI/CD
# azure-pipelines.yml - Node.js CI/CD
trigger:
branches:
include:
- main
- develop
paths:
exclude:
- docs/*
- README.md
pr:
branches:
include:
- main
variables:
- group: production-vars
- name: nodeVersion
value: '18.x'
stages:
- stage: Build
displayName: 'Build and Test'
jobs:
- job: BuildJob
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: $(nodeVersion)
displayName: 'Install Node.js'
- task: Npm@1
inputs:
command: 'ci'
displayName: 'npm ci'
- task: Npm@1
inputs:
command: 'custom'
customCommand: 'run build'
displayName: 'Build application'
- task: Npm@1
inputs:
command: 'custom'
customCommand: 'test'
displayName: 'Run tests'
- task: PublishTestResults@2
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '**/test-results.xml'
displayName: 'Publish test results'
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage/cobertura-coverage.xml'
displayName: 'Publish coverage'
- task: Docker@2
inputs:
command: 'buildAndPush'
repository: 'myapp'
containerRegistry: 'dockerRegistryConnection'
tags: |
$(Build.BuildId)
latest
displayName: 'Build and push Docker image'
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: 'drop'
displayName: 'Publish artifacts'
- stage: Deploy
displayName: 'Deploy to Azure'
dependsOn: Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployWeb
environment: 'production'
pool:
vmImage: 'ubuntu-latest'
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
inputs:
azureSubscription: 'Azure Service Connection'
appType: 'webAppLinux'
appName: 'myapp-prod'
package: '$(Pipeline.Workspace)/drop/**/*.zip'
displayName: 'Deploy to App Service'
# Multi-stage pipeline with approval
stages:
- stage: Dev
jobs:
- job: DeployDev
steps:
- script: echo "Deploy to Dev"
- stage: Staging
dependsOn: Dev
jobs:
- deployment: DeployStaging
environment: 'staging'
strategy:
runOnce:
deploy:
steps:
- script: echo "Deploy to Staging"
- stage: Production
dependsOn: Staging
jobs:
- deployment: DeployProduction
environment: 'production' # Requires approval
strategy:
runOnce:
deploy:
steps:
- script: echo "Deploy to Production"
# Matrix build
strategy:
matrix:
Node16:
nodeVersion: '16.x'
Node18:
nodeVersion: '18.x'
Node20:
nodeVersion: '20.x'
maxParallel: 3
# Variables groups
# Pipeline → Library → Variable groups
# Add secrets (marked as secret)
# Link to Key Vault
# Service connections
# Project settings → Service connections
# Azure, Docker, Kubernetes, AWS, etc.
5. Infrastructure as Code with Pipelines
# Terraform pipeline
trigger:
branches:
include:
- main
paths:
include:
- terraform/*
stages:
- stage: Plan
jobs:
- job: TerraformPlan
pool:
vmImage: 'ubuntu-latest'
steps:
- task: TerraformInstaller@0
inputs:
terraformVersion: '1.7.0'
- task: TerraformTaskV4@4
inputs:
provider: 'azurerm'
command: 'init'
workingDirectory: '$(System.DefaultWorkingDirectory)/terraform'
backendServiceArm: 'Azure Service Connection'
backendAzureRmResourceGroupName: 'terraform-state-rg'
backendAzureRmStorageAccountName: 'tfstate'
backendAzureRmContainerName: 'tfstate'
backendAzureRmKey: 'prod.terraform.tfstate'
- task: TerraformTaskV4@4
inputs:
provider: 'azurerm'
command: 'plan'
workingDirectory: '$(System.DefaultWorkingDirectory)/terraform'
environmentServiceNameAzureRM: 'Azure Service Connection'
- task: TerraformTaskV4@4
inputs:
provider: 'azurerm'
command: 'apply'
workingDirectory: '$(System.DefaultWorkingDirectory)/terraform'
environmentServiceNameAzureRM: 'Azure Service Connection'
# ARM Template deployment
steps:
- task: AzureResourceManagerTemplateDeployment@3
inputs:
azureResourceManagerConnection: 'Azure Service Connection'
subscriptionId: 'subscription-id'
resourceGroupName: 'myapp-rg'
location: 'East US'
templateLocation: 'Linked artifact'
csmFile: '$(System.DefaultWorkingDirectory)/arm/template.json'
csmParametersFile: '$(System.DefaultWorkingDirectory)/arm/parameters.json'
deploymentMode: 'Incremental'
# Kubernetes deployment
steps:
- task: Kubernetes@1
inputs:
connectionType: 'Azure Resource Manager'
azureSubscriptionConnection: 'Azure Service Connection'
azureResourceGroup: 'aks-rg'
kubernetesCluster: 'myapp-aks'
command: 'apply'
arguments: '-f k8s/deployment.yaml'
- task: Kubernetes@1
inputs:
connectionType: 'Azure Resource Manager'
azureSubscriptionConnection: 'Azure Service Connection'
azureResourceGroup: 'aks-rg'
kubernetesCluster: 'myapp-aks'
command: 'set'
arguments: 'image deployment/myapp myapp=$(containerRegistry)/myapp:$(Build.BuildId)'
6. Azure Test Plans
# Test Plans - Manual and automated testing
# Create test plan via CLI
az test plan create \
--name "Sprint 1 Test Plan" \
--area "MyProject" \
--iteration "Sprint 1"
# Create test suite
az test suite create \
--plan-id 1 \
--name "User Authentication Tests" \
--suite-type "RequirementBased"
# Create test case
az test case create \
--title "Login with valid credentials" \
--assigned-to "tester@example.com" \
--area "MyProject\\Backend" \
--iteration "Sprint 1"
# Test case steps:
1. Navigate to login page
2. Enter valid email
3. Enter valid password
4. Click login button
Expected: User is logged in and redirected to dashboard
# Run manual test
# Test Plans → Test suites → Select test case → Run test
# Mark steps as Pass/Fail
# Add comments and attachments
# Automated testing in pipeline
steps:
- task: VSTest@2
inputs:
testSelector: 'testAssemblies'
testAssemblyVer2: |
**\*test*.dll
!**\*TestAdapter.dll
!**\obj\**
searchFolder: '$(System.DefaultWorkingDirectory)'
codeCoverageEnabled: true
testRunTitle: 'Automated Tests'
- task: PublishTestResults@2
inputs:
testResultsFormat: 'VSTest'
testResultsFiles: '**/*.trx'
failTaskOnFailedTests: true
# Integration with test cases
# Link automated tests to test cases
# Run tests on build pipeline
# Track test execution history
# Test parameters
# Shared parameters across test cases
# Example: Login URL, test credentials
7. Azure Artifacts - Package Management
# Azure Artifacts - npm, NuGet, Maven, Python packages
# Create feed
az artifacts universal publish \
--organization https://dev.azure.com/myorg \
--feed myfeed \
--name mypackage \
--version 1.0.0 \
--description "My package" \
--path .
# npm registry
# .npmrc
registry=https://pkgs.dev.azure.com/myorg/_packaging/myfeed/npm/registry/
always-auth=true
# Authenticate
npm install -g vsts-npm-auth
vsts-npm-auth -config .npmrc
# Publish package
npm publish
# Install from feed
npm install @myorg/mypackage
# NuGet feed
# nuget.config
# Publish NuGet package
nuget push mypackage.1.0.0.nupkg -Source MyOrg -ApiKey az
# Pipeline task for publishing
steps:
- task: NuGetCommand@2
inputs:
command: 'pack'
packagesToPack: '**/*.csproj'
versioningScheme: 'byBuildNumber'
- task: NuGetCommand@2
inputs:
command: 'push'
packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
nuGetFeedType: 'internal'
publishVstsFeed: 'myfeed'
# Maven repository
# pom.xml
myorg
https://pkgs.dev.azure.com/myorg/_packaging/myfeed/maven/v1
# Upstream sources
# Connect to public registries
# npmjs.com, NuGet.org, Maven Central
# Cache packages locally
8. Security & Compliance
# Security scanning in pipeline
steps:
# Dependency scanning
- task: dependency-check-build-task@6
inputs:
projectName: 'MyApp'
scanPath: '$(Build.SourcesDirectory)'
format: 'ALL'
# Secret scanning
- task: CredScan@3
inputs:
toolMajorVersion: 'V2'
# Code analysis
- task: SonarQubePrepare@5
inputs:
SonarQube: 'SonarQube Service Connection'
scannerMode: 'CLI'
configMode: 'manual'
cliProjectKey: 'myapp'
cliProjectName: 'MyApp'
- task: SonarQubeAnalyze@5
- task: SonarQubePublish@5
inputs:
pollingTimeoutSec: '300'
# Azure Key Vault integration
steps:
- task: AzureKeyVault@2
inputs:
azureSubscription: 'Azure Service Connection'
KeyVaultName: 'myapp-keyvault'
SecretsFilter: '*'
RunAsPreJob: true
# Use secrets in subsequent tasks
- script: echo $(DatabasePassword)
# Service principal authentication
# Create service principal
az ad sp create-for-rbac --name myapp-sp \
--role contributor \
--scopes /subscriptions/{subscription-id}
# Add to pipeline as service connection
# Project settings → Service connections → Azure Resource Manager
# Managed identity
# For Azure resources (App Service, AKS, VMs)
# No credentials in code or pipeline
# Policies and approvals
# Branch policies
# Required reviewers
# Build validation
# Work item linking
# Environment approvals
# Settings → Environments → Production → Approvals
# Add approvers
# Required number of approvals
9. Monitoring & Analytics
# Pipeline analytics
# Pipelines → Analytics
- Pass rate
- Duration trends
- Failure analysis
- Test results
# Application Insights integration
steps:
- task: AzureAppServiceSettings@1
inputs:
azureSubscription: 'Azure Service Connection'
appName: 'myapp-prod'
resourceGroupName: 'myapp-rg'
appSettings: |
[
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "$(AppInsightsKey)",
"slotSetting": false
}
]
# Query Application Insights
// Failed requests last 24 hours
requests
| where timestamp > ago(24h)
| where success == false
| summarize count() by resultCode, bin(timestamp, 1h)
# Dashboards
# Azure Dashboards
# Pin charts from Analytics
# Share with team
# Notifications
# Email notifications
# Slack/Teams integration
# Webhooks
# Service hooks
# Project settings → Service hooks
# Integrate with:
- Slack
- Microsoft Teams
- Azure Service Bus
- Azure Event Grid
- Webhooks
10. Azure DevOps CLI Automation
# Complete automation example
#!/bin/bash
ORG="https://dev.azure.com/myorg"
PROJECT="MyProject"
# Create project
az devops project create \
--organization $ORG \
--name $PROJECT
# Create repository
REPO_ID=$(az repos create \
--organization $ORG \
--project $PROJECT \
--name myapp \
--query id -o tsv)
# Set branch policies
az repos policy create \
--organization $ORG \
--project $PROJECT \
--repository-id $REPO_ID \
--branch main \
--blocking true \
--enabled true \
--minimum-approver-count 2
# Create pipeline
az pipelines create \
--organization $ORG \
--project $PROJECT \
--name "MyApp-CI-CD" \
--repository $REPO_ID \
--repository-type tfsgit \
--branch main \
--yml-path azure-pipelines.yml
# Create environments
az devops invoke \
--organization $ORG \
--area distributedtask \
--resource environments \
--route-parameters project=$PROJECT \
--http-method POST \
--in-file environments.json
# Create variable group
az pipelines variable-group create \
--organization $ORG \
--project $PROJECT \
--name production-vars \
--variables \
DatabaseConnection="$(DatabaseConnection)" \
ApiKey="$(ApiKey)" \
--authorize true
# Link to Key Vault
az pipelines variable-group create \
--organization $ORG \
--project $PROJECT \
--name keyvault-vars \
--authorize true \
--variables dummy=value
# Run pipeline
az pipelines run \
--organization $ORG \
--project $PROJECT \
--name "MyApp-CI-CD" \
--branch main
# Query build status
az pipelines build list \
--organization $ORG \
--project $PROJECT \
--status completed \
--top 10 \
--output table
11. Best Practices
✓ Azure DevOps Best Practices:
- ✓ Use YAML pipelines over classic pipelines
- ✓ Store pipeline definitions in source control
- ✓ Implement branch policies for code quality
- ✓ Use variable groups for configuration
- ✓ Integrate with Azure Key Vault for secrets
- ✓ Enable automated testing in pipelines
- ✓ Use environments for deployment approvals
- ✓ Implement security scanning in CI/CD
- ✓ Use service connections instead of credentials
- ✓ Link work items to commits and PRs
- ✓ Enable branch protection on main/production branches
- ✓ Use Azure Artifacts for package management
- ✓ Set up monitoring and alerts
- ✓ Document architecture and processes
- ✓ Regular security audits and compliance checks
Conclusion
Azure DevOps provides comprehensive DevOps capabilities for enterprise teams. Leverage Boards for agile planning, Repos for source control, Pipelines for CI/CD automation, Test Plans for quality assurance, and Artifacts for package management. Integrate with Azure services for complete cloud-native DevOps workflows.
💡 Pro Tip: Use Azure DevOps templates and extensions from the marketplace to accelerate pipeline development. Store reusable pipeline templates in a separate repository and reference them across projects. This promotes consistency, reduces duplication, and simplifies maintenance of CI/CD workflows across your organization.