Azure Devops How To

Azure Devops How To

Reading time1 min
#DevOps#Cloud#Automation#AzureDevOps#YAMLPipelines#ContinuousDelivery

Azure DevOps: Automated, Secure Multi-Stage Deployments with YAML Pipelines

Release management workflows in most organizations swing between “wild west” practices and rigid, bottlenecked approval chains. Azure DevOps YAML pipelines, used correctly, strike a balance—enabling rapid, auditable, and secure deployments across multiple environments.


Scenario: You’re supporting a mission-critical .NET Core API that must move swiftly from code commit to a hardened production environment, with multiple checkpoints, zero credential leakage, and clear separation of duties.


Role of Multi-Stage Pipelines

Why multi-stage? Simple: monolithic pipelines hide complexity and amplify risk. With discrete stages (Build → Test → DeployDev → Approve → DeployProd), you can:

  • Contain blast radius: A broken build never reaches staging.
  • Implement fine-grained access controls: Only QA or product managers interact at designated approval gates.
  • Maintain visibility: Git history ties to deployment history, by user and pipeline run.
  • Reduce cognitive load: Each stage’s intent and outputs are explicit.

Diagram:

    +------> Build ----> Test ----> DeployDev ----> [Manual Approval] ----> DeployProd
    |            |            |             |                    |                   |
  devs       linter/unit CI   QA         App config        Change Mgmt           Ops

Pipeline Structure: .NET 6 Example

Place this in azure-pipelines.yml at repo root. Replace as needed for Node.js, etc.

trigger:
  - main

stages:
  - stage: Build
    displayName: 'Build (.NET 6)'
    jobs:
      - job: BuildJob
        pool: { vmImage: 'ubuntu-latest' }
        steps:
          - task: UseDotNet@2
            inputs:
              packageType: 'sdk'
              version: '6.0.x'
          - script: dotnet build --configuration Release --no-restore
            displayName: 'dotnet build'
  - stage: Test
    displayName: 'Unit Tests'
    dependsOn: Build
    jobs:
      - job: TestJob
        pool: { vmImage: 'ubuntu-latest' }
        steps:
          - script: dotnet test --no-restore --no-build
            displayName: 'dotnet test'
  - stage: DeployDev
    displayName: 'Deploy to Development'
    dependsOn: Test
    jobs:
      - deployment: DeployToDev
        environment: dev-environment
        strategy:
          runOnce:
            deploy:
              steps:
                - script: |
                    echo "Deploying artifact to dev slot..."
                  displayName: 'Deploy Dev Placeholder'
  - stage: Approval
    displayName: 'Manual Approval Required'
    dependsOn: DeployDev
    jobs:
      - job: ApprovalGate
        pool: { vmImage: 'ubuntu-latest' }
        steps:
          - task: ManualValidation@0
            inputs:
              notifyUsers: 'release-admin@contoso.com'
              instructions: 'Review change log, approve if ready for production.'
  - stage: DeployProd
    displayName: 'Deploy to Production'
    dependsOn: Approval
    condition: succeeded('Approval')
    jobs:
      - deployment: DeployToProd
        environment: prod-environment
        strategy:
          runOnce:
            deploy:
              steps:
                - script: |
                    echo "Deploying to production..."
                  displayName: 'Production deploy'

Note: Even with approval tasks in YAML, tighter controls are possible using Azure DevOps Environment-level checks.


Secret Management: Integrating Azure Key Vault

Credentials in plain YAML? Unacceptable. Use Azure Key Vault. Here’s what works in production:

Preparation

  • Azure Key Vault provisioned in same subscription as app.
  • Service principal (used by DevOps) has “get” access to required secrets.

YAML: Fetch secrets before deploy

- task: AzureKeyVault@2
  inputs:
    azureSubscription: 'Contoso-Prod-ServiceConnection'
    KeyVaultName: 'contoso-prod-kv'
    SecretsFilter: '*'
    RunAsPreJob: true

Secrets are now available as runtime variables: reference as $(mySecretName) in subsequent tasks.

Known issue: Key Vault task can only fetch secrets, not certificates without additional scripting.


Environment Checks: Out-of-Band Control

Skip complex approval logic in YAML. Instead, configure “Approvals and Checks” under Pipelines > Environments in the portal:

  • Assign approver groups (e.g., IT Operations for prod).
  • Add checks like business-hour restrictions, required work item linkage, or policy compliance scans.

Pro-tip: Combining YAML manual validations + environment-level checks covers both audit trails and policy power.


Lessons From the Field

  • Automated deploys with enforced manual approval reduce error rates by making intent clear—no more “accidental prod deploy at 2AM.”
  • Use stageDependencies to pass artifacts or output variables between stages instead of global variables; this prevents accidental scope leakage.
  • For staged rollouts, consider ring-based environments (Dev → Test → PreProd → Canary → Prod), though that adds pipeline overhead.
  • Pipeline failures relating to AzureKeyVault@2 often stem from broken service connections or unassigned Key Vault permissions—not the Key Vault itself. Check error like:
    ##[error]Operation returned an invalid status code 'Forbidden'
    
  • Build and test images (e.g., ubuntu-latest) occasionally break with upstream updates; pin to a specific VM image (e.g., ubuntu-22.04) for reproducibility if needed.

Summary

Multi-stage Azure DevOps YAML pipelines—secured by Key Vault integration and layered with approval gates—are not about adding ceremony, but about repeatability and auditability. The trade-off lies in a slightly steeper learning curve and the maintenance burden for secrets/service connections, mitigated by clear team ownership and documentation.

For evolving orgs, start with a two-stage pipeline (build-test, deploy-prod) and layer on approvals once confidence grows. Avoid sprawling pipeline definitions—modularity and clarity pay downstream.


Sample code and GitHub repo scaffolding available on request. For enum-based deployment slots, or integration with ServiceNow for change management approvals—further customization is possible.

No “one click to prod”—that’s the whole point.