August 13th, 2025
0 reactions

Azure Developer CLI: From Dev to Prod with Azure DevOps Pipelines

Building on our previous post about implementing dev-to-prod promotion with GitHub Actions, this follow-up demonstrates the same “build once, deploy everywhere” pattern using Azure DevOps Pipelines. You’ll learn how to leverage Azure DevOps YAML pipelines with Azure Developer CLI (azd). This approach ensures consistent, reliable deployments across environments.

Environment-Specific Infrastructure

The infrastructure approach is identical to our previous GitHub Actions implementation. It uses conditional Bicep deployment with a single envType parameter. This drives environment-specific resource configuration. The same Bicep templates work seamlessly across both CI/CD platforms.

For the complete infrastructure setup details, refer to the GitHub Actions post.

From File Backup to Pipeline Artifacts

The original approach used local file backups (copying zip files within the same job). However, the community pointed out that using native CI/CD artifact systems is more idiomatic. This provides several key advantages:

  • Cross-job compatibility: Artifacts work seamlessly across multiple jobs and stages.
  • Automatic cleanup: The platform handles retention policies automatically.
  • Better traceability: Artifacts are visible in the platform UI with download history.
  • Platform integration: Native features with built-in security and access controls.

This artifact-based approach represents the industry standard for “build once, deploy everywhere” patterns. It works across modern CI/CD platforms. We have updated the original GitHub Actions implementation to use the same pattern demonstrated in this Azure DevOps version.

Azure DevOps Pipeline Enhancement

Azure DevOps pipelines require a different approach than GitHub Actions. However, they achieve the same outcome. We’ll demonstrate a multi-stage pipeline that provides proper separation of concerns and enterprise-ready deployment patterns. This staged approach offers better isolation, approval workflows, and traceability compared to single-job pipelines.

The enhanced pipeline follows a three-stage structure:

1. Pipeline Structure

The multi-stage pipeline uses separate stages for build, development deployment, and production promotion:

# Run when commits are pushed to main
trigger:
  - main

pool:
  vmImage: ubuntu-latest

stages:
- stage: build_and_test
- stage: deploy_development
  dependsOn: build_and_test
- stage: promote_to_Prod
  dependsOn: deploy_development

2. Build and Package Stage

The first stage focuses solely on building and packaging the application for deployment:

- stage: build_and_test
  jobs:
  - job: buildAndPackage
    pool:
      vmImage: ubuntu-latest
    steps:
    - task: Bash@3
      displayName: Install azd
      inputs:
        targetType: 'inline'
        script: |
          curl -fsSL https://aka.ms/install-azd.sh | bash
    
    - task: PowerShell@2
      displayName: Configure AZD to Use AZ CLI Authentication.
      inputs:
        targetType: inline
        script: |
          azd config set auth.useAzCliAuth "true"
        pwsh: true
    
    - task: AzureCLI@2
      displayName: Package Application
      inputs:
        azureSubscription: azconnection
        scriptType: bash
        scriptLocation: inlineScript
        keepAzSessionActive: true
        inlineScript: |
          mkdir -p ./dist
          azd package app --output-path ./dist/app-package.zip  --no-prompt
          echo "✅ Application packaged successfully"
    
    - task: PublishPipelineArtifact@1
      displayName: Upload Package Artifact
      inputs:
        targetPath: './dist/app-package.zip'
        artifact: 'app-package'
        publishLocation: 'pipeline'

3. Deploy to Development Stage

The second stage provisions development infrastructure and deploys the packaged application:

- stage: deploy_development
  dependsOn: build_and_test
  jobs:
  - job: deployToDevelopment
    pool:
      vmImage: ubuntu-latest
    steps:
    - task: Bash@3
      displayName: Install azd
      inputs:
        targetType: 'inline'
        script: |
          curl -fsSL https://aka.ms/install-azd.sh | bash
    
    - task: PowerShell@2
      displayName: Configure AZD to Use AZ CLI Authentication.
      inputs:
        targetType: inline
        script: |
          azd config set auth.useAzCliAuth "true"
        pwsh: true
    
    - task: AzureCLI@2
      displayName: Provision DEV Infrastructure
      inputs:
        azureSubscription: azconnection
        scriptType: bash
        scriptLocation: inlineScript
        keepAzSessionActive: true
        inlineScript: |
          azd provision --no-prompt

    - task: DownloadPipelineArtifact@2
      displayName: Download Package Artifact
      inputs:
        buildType: 'current'
        artifactName: 'app-package'
        targetPath: './artifacts'

    - task: AzureCLI@2
      displayName: Deploy to Development
      inputs:
        azureSubscription: azconnection
        scriptType: bash
        scriptLocation: inlineScript
        keepAzSessionActive: true
        inlineScript: |
          azd deploy app --from-package ./artifacts/app-package.zip --no-prompt

4. Validation Gate

Add validation checks before promotion to production:

    - task: AzureCLI@2
      displayName: Validate Application
      inputs:
        azureSubscription: azconnection
        scriptType: bash
        scriptLocation: inlineScript
        keepAzSessionActive: true
        inlineScript: |
          echo "🔍 Validating application in development environment..."
          # TODO: Add actual validation here
          # Examples:
          # - Health checks and integration tests
          # - Security and compliance scanning
          # - Performance validation
          sleep 3  # Simulate validation time
          echo "✅ Application validation passed"

5. Promote to Production Stage

The final stage uses environment-specific variables to deploy to production:

- stage: promote_to_Prod
  dependsOn: deploy_development
  jobs:
  - job: deployProduction
    # use prod settings to override default environment variables
    # this variables become ENV VARS for all tasks in this job
    variables:
      AZURE_ENV_NAME: $(AZURE_PROD_ENV_NAME)
      AZURE_ENV_TYPE: $(AZURE_PROD_ENV_TYPE)
      AZURE_LOCATION: $(AZURE_PROD_LOCATION)
      AZURE_SUBSCRIPTION_ID: $(AZURE_PROD_SUBSCRIPTION_ID)
    pool:
      vmImage: ubuntu-latest
    steps:
    - task: Bash@3
      displayName: Install azd
      inputs:
        targetType: 'inline'
        script: |
          curl -fsSL https://aka.ms/install-azd.sh | bash
    
    - task: PowerShell@2
      displayName: Configure AZD to Use AZ CLI Authentication.
      inputs:
        targetType: inline
        script: |
          azd config set auth.useAzCliAuth "true"
        pwsh: true
    
    - task: DownloadPipelineArtifact@2
      displayName: Download Package Artifact
      inputs:
        buildType: 'current'
        artifactName: 'app-package'
        targetPath: './artifacts'

    - task: AzureCLI@2
      displayName: Deploy to PROD
      inputs:
        azureSubscription: azconnection
        scriptType: bash
        scriptLocation: inlineScript
        keepAzSessionActive: true
        inlineScript: |
          azd deploy app --from-package ./artifacts/app-package.zip --no-prompt

Try It Out

You can try this approach using the complete implementation here

Watch the walkthrough:

Prerequisites

You’ll need a Personal Access Token (PAT) to set up Azure DevOps pipelines with azd. For detailed guidance on PAT creation and pipeline setup, refer to the Microsoft Learn documentation.

Setup Steps

1. Initialize Project

azd init -t https://github.com/puicchan/azd-dev-prod-appservice-storage

Use environment name like projazdo-dev.

2. Edit azure.yaml

Make sure you configure Azure DevOps as the CICD tool and add these pipeline variables:

pipeline:
  provider: azdo
  variables:
    - AZURE_PROD_ENV_NAME
    - AZURE_PROD_ENV_TYPE
    - AZURE_PROD_LOCATION
    - AZURE_PROD_SUBSCRIPTION_ID

3. Set Up Development Environment

azd up

4. Set Up Production Environment

We will run azd provision and rely on Azure Pipeline to deploy the app to production:

azd env new projazdo-prod
azd env set AZURE_ENV_TYPE prod
azd provision

5. Test the Flow

Switch back to Development:

azd env select projazdo-dev

Edit your application code and make sure you run azd env set to configure the following environment variables the pipeline requires:

  • AZURE_PROD_ENV_NAME
  • AZURE_PROD_ENV_TYPE
  • AZURE_PROD_LOCATION
  • AZURE_PROD_SUBSCRIPTION_ID

6. Configure CI/CD Pipeline

azd pipeline config

Go to your Azure Pipelines Organization and check out the pipeline run.

Pro Tip: Enhance Your Pipeline with AI

Need help with Azure DevOps and azd? GitHub Copilot for Azure with Azure MCP can help you enhance your azure-dev.yml file directly in VS Code.

With the GitHub Copilot for Azure extension installed, and the GitHub Copilot for Azure and Azure MCP tools enabled in Agent mode, GitHub Copilot can:

  • Debug pipeline issues: Analyze YAML syntax errors and configuration problems.
  • Add validation steps: Suggest health checks, security scans, or integration tests.
  • Optimize deployment strategies: Recommend blue-green deployments or canary releases.
  • Configure environment-specific logic: Help set up conditional steps for different environments.

Simply ask GitHub Copilot questions like:

  • “Add a health check validation step to my Azure DevOps pipeline”
  • “How can I add manual approval gates before production deployment?”

Agent mode with GitHub Copilot for Azure provides contextual understanding of your Azure resources. It can suggest pipeline improvements based on your specific infrastructure setup.

Conclusion

This Azure DevOps implementation demonstrates how the “build once, deploy everywhere” pattern translates seamlessly across different CI/CD platforms. The core azd and Bicep logic remains identical. Meanwhile, platform-specific features like service connections and task definitions provide the Azure DevOps-native experience.

Whether you choose GitHub Actions or Azure DevOps, the fundamental approach remains consistent. Conditional infrastructure deployment and package promotion ensure reliable deployments across your development lifecycle.

Questions about implementation or want to share your Azure DevOps approach? Join the discussion here.

0 comments