← Back to Guides

Azure DevOps Complete Guide

📖 17 min read | 📅 Updated: January 2025 | 🏷️ DevOps & Cloud

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:

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.