Azure Developer CLI: Build Once, Deploy Everywhere from Dev to Prod with One Click
Authored by PuiChee (PC) Chan and Kristen Womack, this post guides readers through implementing a ‘build once, deploy everywhere’ CI/CD pipeline using Azure Developer CLI, conditional Bicep templates, and GitHub Actions for efficient dev-to-prod promotions.
Azure Developer CLI: Build Once, Deploy Everywhere from Dev to Prod with One Click
By PuiChee (PC) Chan, Kristen Womack
This article details how to implement a build once, deploy everywhere pattern using Azure Developer CLI (azd). The approach centers on provisioning environment-specific infrastructure and promoting applications from development to production using the same build artifacts. Readers will learn how to:
- Use conditional Bicep deployments
- Inject environment variables
- Preserve packages across environments for consistent artifact promotion
- Automate CI/CD processes using GitHub Actions
Environment-Specific Infrastructure
When deploying across environments (development, production, etc.), each stage may require different infrastructure settings. The authors provide a comparison table:
Component | Development | Production |
---|---|---|
Networking | Public access | VNet integration + Private endpoints |
Storage | Public (with restrictions) | Private endpoints only |
App Service Plan | B2 Basic | S1 Standard |
Security | Managed Identity | Enhanced network isolation |
Managing One Template, Multiple Environments: Instead of maintaining multiple infrastructure templates, use a single Bicep file that adapts based on an environment variable. This method prevents infrastructure drift and keeps your deployments consistent.
Conditional Provisioning with Environment Variables
Use the environment variable AZURE_ENV_TYPE
to drive decisions in your Bicep files. azd
passes this as the envType
parameter. Example:
@description('Environment type - determines networking configuration (dev/test/prod)')
@allowed(['dev', 'test', 'prod'])
param envType string = 'dev'
Conditional Deployment Examples
Network (Production only):
module network './network.bicep' = if (envType == 'prod') {
name: 'networkDeployment'
params: { /* ... */ }
}
Storage (Public or Private based on environment):
module storageAccount 'br/public:avm/res/storage/storage-account:0.17.2' = {
name: 'storageAccount'
params: {
name: storageAccountName
allowSharedKeyAccess: false
publicNetworkAccess: envType == 'prod' ? 'Disabled' : 'Enabled'
networkAcls: envType == 'prod' ? {
defaultAction: 'Deny'
virtualNetworkRules: []
bypass: 'AzureServices'
} : {
defaultAction: 'Allow'
virtualNetworkRules: []
bypass: 'AzureServices'
}
// ... other config
}
}
App Service Plan Sizing:
sku: {
name: envType == 'prod' ? 'S1' : 'B2'
tier: envType == 'prod' ? 'Standard' : 'Basic'
}
Enhancing the CI/CD Workflow
The Azure Developer CLI includes a command to bootstrap your CI/CD pipeline:
azd pipeline config
This generates a basic workflow. For the build-once, deploy-everywhere pattern, the authors recommend these enhancements (noting an important update: using GitHub Actions Artifacts instead of local file copies).
Example Workflow Steps
-
Package Once:
- name: Package Application run: | mkdir -p ./dist azd package app --output-path ./dist/app-package.zip echo "✅ Application packaged successfully"
-
Upload Package:
- name: Upload Application Package uses: actions/upload-artifact@v4 with: name: app-package path: ./dist/app-package.zip retention-days: 30
-
Deploy to Dev:
- name: Deploy to Development run: azd deploy app --from-package ./dist/app-package.zip --no-prompt
-
Validation Gate:
- name: Validate Application run: | echo "🔍 Validating application in development environment..." # Add your validation logic here (health checks, tests, etc.) sleep 3 echo "✅ Application validation passed"
-
Download Application Package (for Prod):
- name: Download Application Package uses: actions/download-artifact@v4 with: name: app-package path: ./prod-deploy
-
Set Environment Variables for Prod:
- name: Promote to Production run: | PROD_ENV_NAME="${AZURE_ENV_NAME%-dev}-prod" echo "Production environment name: $PROD_ENV_NAME" export AZURE_ENV_NAME="$PROD_ENV_NAME" export AZURE_ENV_TYPE="prod"
-
Deploy to Prod:
- name: Deploy to Production run: | PACKAGE_PATH="./prod-deploy/app-package.zip" if [ -f "$PACKAGE_PATH" ]; then echo "🚀 Deploying to production using artifact package: $PACKAGE_PATH" azd deploy app --from-package "$PACKAGE_PATH" --no-prompt echo "✅ Production deployment completed successfully" else echo "❌ Package artifact not found - falling back to regular deployment" azd deploy --no-prompt fi
Step-by-Step Setup Guide
-
Initialize the Project:
azd init -t https://github.com/puicchan/azd-dev-prod-appservice-storage
(Downloads templates and enhanced GitHub Actions workflows)
-
Set Up Development Environment:
azd up
When prompted, use a name like
myproj-dev
. The defaultenvType
isdev
, so public access and cost-optimized settings are used. -
Set Up Production Environment:
azd env new myproj-prod azd env set AZURE_ENV_TYPE prod azd up
Provisions with VNet integration, private endpoints, and enhanced security.
-
Switch Environments:
azd env select myproj-dev
Allows further development and testing in the dev environment.
- Make Code Changes:
- Modify your application code (e.g.,
index.html
,app.py
) to test the promotion workflow.
- Modify your application code (e.g.,
-
Configure the CI/CD Pipeline:
azd pipeline config
This configures the GitHub Actions workflow for dev-to-prod promotion with environment variable handling and artifact reuse.
For a complete walkthrough and example implementation, see this repository.
Conclusion
Using Azure Developer CLI in this way allows teams to reliably promote the same build from development to production via a streamlined, automated CI/CD process. Conditional Bicep deployment and artifact management ensure consistency, security, and efficiency throughout the pipeline.
Questions or experiences to share? Join the discussion here.
This post appeared first on “Microsoft DevBlog”. Read the entire article here