1 min read
Azure DevOps in 2022: New Features and Best Practices
I wrote “Azure DevOps in 2022: New Features and Best Practices” to share practical, production-minded guidance on this topic.
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.\n\n## Takeaways\n\nAdd a concise, personal takeaway and recommended next steps here.\n