July 21st, 2025
heart1 reaction

Azure Developer CLI: From Dev to Prod with One Click

This post walks through how to implement a “build once, deploy everywhere” pattern using Azure Developer CLI (azd) that provisions environment-specific infrastructure and promotes applications from dev to prod with the same build artifacts. You’ll learn how to use conditional Bicep deployment, environment variable injection, package preservation across environments, and automated CI/CD promotion from development to production.

Environment-Specific Infrastructure

When deploying applications across environments, different requirements emerge:

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

Rather than maintaining separate infrastructure templates or complex configuration files for each environment, we can use a single set of Bicep files that adapt based on an environment variable. This approach eliminates infrastructure drift while keeping deployments simple and consistent.

Set up resources based on an environment variable

To conditionally provision resources based on environment type, azd leverages an environment variable AZURE_ENV_TYPE to make decisions at deployment time.

Behind the scenes, azd passes AZURE_ENV_TYPE to Bicep as the envType parameter:

@description('Environment type - determines networking configuration (dev/test/prod)')
@allowed(['dev', 'test', 'prod'])
param envType string = 'dev'

This parameter then drives conditional resource deployment in Bicep:

For Network Deployment:

// Deploy network infrastructure only for production environments
module network './network.bicep' = if (envType == 'prod') {
  name: 'networkDeployment'
  params: {
    location: location
    tags: tags
    abbrs: abbrs
    resourceToken: resourceToken
  }
}

Similarly for Storage Configuration:

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 configuration
  }
}

Finally for App Service Plan Sizing:

sku: {
  name: envType == 'prod' ? 'S1' : 'B2'
  tier: envType == 'prod' ? 'Standard' : 'Basic'
}

Enhance default CI/CD workflow

azd includes a powerful command to bootstrap CI/CD pipeline:

azd pipeline config

This generates a basic workflow. However, for dev-to-prod promotion, enhance it with these steps:

1. Package Once:

- name: Package Application
  run: |
    mkdir -p ./dist
    azd package app --output-path ./dist/app-package.zip
    # Create a backup copy for production deployment
    cp ./dist/app-package.zip ./app-package-backup.zip
    echo "✅ Application packaged and backup created"

2. Deploy to Dev:

- name: Deploy to Development
  run: azd deploy app --from-package ./dist/app-package.zip --no-prompt

3. Validation Gate:

- name: Validate Application
  run: |
    echo "🔍 Validating application in development environment..."
    # Add your validation logic here:
    # - Health checks and integration tests
    # - Security and compliance scanning
    # - Performance validation
    sleep 3  # Simulate validation time
    echo "✅ Application validation passed"

4. Set environment variables:

- name: Promote to Production
  run: |
    # Create production environment name by replacing -dev with -prod, or adding -prod if no -dev suffix
    PROD_ENV_NAME="${AZURE_ENV_NAME%-dev}-prod"
    echo "Production environment name: $PROD_ENV_NAME"

    # Set environment variables for this step
    export AZURE_ENV_NAME="$PROD_ENV_NAME"
    export AZURE_ENV_TYPE="prod"

5. Deploy to Prod:

# Use the same package created earlier - true "build once, deploy everywhere"
PACKAGE_PATH="./app-package-backup.zip"

if [ -f "$PACKAGE_PATH" ]; then
  echo "🚀 Deploying to production using existing package: $PACKAGE_PATH"
  azd deploy app --from-package "$PACKAGE_PATH" --no-prompt
  echo "✅ Production deployment completed successfully"

6. Clean Up:


  # Clean up the backup package after successful deployment
  rm -f "$PACKAGE_PATH"
  echo "🧹 Cleaned up package backup"
else
  echo "❌ Package backup not found - falling back to regular deployment"
  azd deploy --no-prompt
fi

Try It Out

You can try this approach using the complete implementation here.

Watch the walkthrough: See this entire process in action in the video below that shows the complete setup and deployment flow.

Step-by-Step Setup

Follow these steps to set up both development and production environments, then configure the CI/CD pipeline:

1. Initialize project from the template

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

This downloads the complete implementation with all Bicep files and enhanced GitHub Actions workflow.

2. Set Up Development Environment

azd up

When prompted for the environment name, use myproj-dev (or your preferred naming pattern with -dev suffix).

Note: The default envType is dev, so you don’t need to set the AZURE_ENV_TYPE environment variable for development. The infrastructure will automatically provision with public access and cost-optimized resources.

3. Set Up Production Environment

Create and configure the production environment:

# Create new production environment
azd env new myproj-prod

# Set environment type to production
azd env set AZURE_ENV_TYPE prod

# Deploy production environment
azd up

This provisions production infrastructure with VNet integration, private endpoints, and enhanced security. Note that running azd up deploys the app to the production infrastructure as well. This is for demonstration purpose plus we are still in development stage. In a real-world scenario, you’d probably never deploy your app directly if it is already in production.

4. Switch Back to Development Environment

azd env select myproj-dev

You’re now ready to develop and test in the development environment.

5. Make Code Changes

Edit your application code (e.g., modify app/templates/index.html or app.py) to test the promotion workflow.

6. Configure CI/CD Pipeline

azd pipeline config

The GitHub Actions workflow is enhanced with dev-to-prod promotion logic. Specifically, the pipeline will:

  • Deploy and validate in development (`myproj-dev`)
  • Automatically promote to production (`myproj-prod`) using the same package
  • Handle environment naming conversion automatically

Once configured, every push to the main branch will trigger the automated dev-to-prod promotion pipeline!

Conclusion

In summary, this pattern combines conditional Bicep deployment, package preservation, and smart environment naming to create reliable dev-to-prod promotions with the same build artifacts.

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

0 comments