Back to Blog
3 min read

Azure DevOps in 2022: New Features and Best Practices

Azure DevOps continues to evolve with powerful new features for 2022. Let’s explore the latest capabilities and best practices for modern DevOps workflows.

YAML Pipelines Evolution

Multi-stage pipelines with environment approvals:

trigger:
  branches:
    include:
      - main
  paths:
    exclude:
      - docs/**
      - README.md

pool:
  vmImage: 'ubuntu-latest'

variables:
  - group: 'Production-Secrets'
  - name: buildConfiguration
    value: 'Release'

stages:
  - stage: Build
    jobs:
      - job: BuildJob
        steps:
          - task: UseDotNet@2
            inputs:
              version: '6.0.x'

          - task: DotNetCoreCLI@2
            displayName: 'Restore'
            inputs:
              command: 'restore'
              projects: '**/*.csproj'

          - task: DotNetCoreCLI@2
            displayName: 'Build'
            inputs:
              command: 'build'
              arguments: '--configuration $(buildConfiguration)'

          - task: DotNetCoreCLI@2
            displayName: 'Test'
            inputs:
              command: 'test'
              arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage"'

          - task: PublishCodeCoverageResults@1
            inputs:
              codeCoverageTool: 'Cobertura'
              summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'

          - task: DotNetCoreCLI@2
            displayName: 'Publish'
            inputs:
              command: 'publish'
              arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'

          - publish: $(Build.ArtifactStagingDirectory)
            artifact: 'drop'

  - stage: DeployDev
    dependsOn: Build
    condition: succeeded()
    jobs:
      - deployment: DeployDev
        environment: 'Development'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: 'drop'
                - task: AzureWebApp@1
                  inputs:
                    azureSubscription: 'Azure-Dev'
                    appName: 'myapp-dev'
                    package: '$(Pipeline.Workspace)/drop/**/*.zip'

  - stage: DeployProd
    dependsOn: DeployDev
    condition: succeeded()
    jobs:
      - deployment: DeployProd
        environment: 'Production'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: 'drop'
                - task: AzureWebApp@1
                  inputs:
                    azureSubscription: 'Azure-Prod'
                    appName: 'myapp-prod'
                    package: '$(Pipeline.Workspace)/drop/**/*.zip'

Reusable Templates

Create reusable pipeline templates:

# templates/dotnet-build.yml
parameters:
  - name: buildConfiguration
    default: 'Release'
  - name: dotnetVersion
    default: '6.0.x'
  - name: projects
    default: '**/*.csproj'

steps:
  - task: UseDotNet@2
    inputs:
      version: ${{ parameters.dotnetVersion }}

  - task: DotNetCoreCLI@2
    displayName: 'Restore packages'
    inputs:
      command: 'restore'
      projects: ${{ parameters.projects }}

  - task: DotNetCoreCLI@2
    displayName: 'Build solution'
    inputs:
      command: 'build'
      projects: ${{ parameters.projects }}
      arguments: '--configuration ${{ parameters.buildConfiguration }} --no-restore'

Use the template:

# azure-pipelines.yml
stages:
  - stage: Build
    jobs:
      - job: Build
        steps:
          - template: templates/dotnet-build.yml
            parameters:
              buildConfiguration: 'Release'
              dotnetVersion: '6.0.x'

Service Connections with Workload Identity

# Using workload identity federation (no secrets!)
- task: AzureCLI@2
  inputs:
    azureSubscription: 'WorkloadIdentityConnection'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
      az account show
      az webapp list --query "[].name"

Pull Request Validation

# pr-validation.yml
trigger: none

pr:
  branches:
    include:
      - main
      - release/*

jobs:
  - job: Validate
    steps:
      - task: DotNetCoreCLI@2
        displayName: 'Build'
        inputs:
          command: 'build'

      - task: DotNetCoreCLI@2
        displayName: 'Test'
        inputs:
          command: 'test'
          arguments: '--logger trx --results-directory $(Agent.TempDirectory)'

      - task: PublishTestResults@2
        inputs:
          testResultsFormat: 'VSTest'
          testResultsFiles: '**/*.trx'
          searchFolder: '$(Agent.TempDirectory)'

      - task: BuildQualityChecks@8
        inputs:
          checkCoverage: true
          coverageFailOption: 'fixed'
          coverageType: 'lines'
          coverageThreshold: '80'

Artifacts and Feeds

- task: NuGetCommand@2
  displayName: 'Push to feed'
  inputs:
    command: 'push'
    packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
    nuGetFeedType: 'internal'
    publishVstsFeed: 'MyOrg/MyFeed'

Azure DevOps in 2022 offers a mature, flexible platform for any DevOps workflow.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.