{"id":2372,"date":"2025-08-13T18:29:24","date_gmt":"2025-08-13T18:29:24","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2025\/08\/13\/azure-developer-cli-from-dev-to-prod-with-azure-devops-pipelines\/"},"modified":"2025-08-13T18:29:24","modified_gmt":"2025-08-13T18:29:24","slug":"azure-developer-cli-from-dev-to-prod-with-azure-devops-pipelines","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2025\/08\/13\/azure-developer-cli-from-dev-to-prod-with-azure-devops-pipelines\/","title":{"rendered":"Azure Developer CLI: From Dev to Prod with Azure DevOps Pipelines"},"content":{"rendered":"<p>Building on our previous <a href=\"https:\/\/devblogs.microsoft.com\/devops\/azure-developer-cli-from-dev-to-prod-with-one-click\/\">post<\/a> about implementing dev-to-prod promotion with GitHub Actions, this follow-up demonstrates the same \u201cbuild once, deploy everywhere\u201d pattern using Azure DevOps Pipelines. You\u2019ll learn how to leverage Azure DevOps YAML pipelines with <a href=\"https:\/\/learn.microsoft.com\/azure\/developer\/azure-developer-cli\/overview\">Azure Developer CLI (azd)<\/a>. This approach ensures consistent, reliable deployments across environments.<\/p>\n<h2>Environment-Specific Infrastructure<\/h2>\n<p>The infrastructure approach is identical to our <a href=\"https:\/\/devblogs.microsoft.com\/devops\/azure-developer-cli-from-dev-to-prod-with-one-click\/\">previous GitHub Actions implementation<\/a>. 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.<\/p>\n<p>For the complete infrastructure setup details, refer to the <a href=\"https:\/\/devblogs.microsoft.com\/devops\/azure-developer-cli-from-dev-to-prod-with-one-click\/#set-up-resources-based-on-an-environment-variable\">GitHub Actions post<\/a>.<\/p>\n<h2>From File Backup to Pipeline Artifacts<\/h2>\n<p>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:<\/p>\n<p><strong>Cross-job compatibility<\/strong>: Artifacts work seamlessly across multiple jobs and stages.<br \/>\n<strong>Automatic cleanup<\/strong>: The platform handles retention policies automatically.<br \/>\n<strong>Better traceability<\/strong>: Artifacts are visible in the platform UI with download history.<br \/>\n<strong>Platform integration<\/strong>: Native features with built-in security and access controls.<\/p>\n<p>This artifact-based approach represents the industry standard for \u201cbuild once, deploy everywhere\u201d 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.<\/p>\n<h2>Azure DevOps Pipeline Enhancement<\/h2>\n<p>Azure DevOps pipelines require a different approach than GitHub Actions. However, they achieve the same outcome. We\u2019ll demonstrate a <strong>multi-stage pipeline<\/strong> 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.<\/p>\n<p>The enhanced pipeline follows a three-stage structure:<\/p>\n<p><strong>1. Pipeline Structure<\/strong><\/p>\n<p>The multi-stage pipeline uses separate stages for build, development deployment, and production promotion:<\/p>\n<p># Run when commits are pushed to main<br \/>\ntrigger:<br \/>\n  &#8211; main<\/p>\n<p>pool:<br \/>\n  vmImage: ubuntu-latest<\/p>\n<p>stages:<br \/>\n&#8211; stage: build_and_test<br \/>\n&#8211; stage: deploy_development<br \/>\n  dependsOn: build_and_test<br \/>\n&#8211; stage: promote_to_Prod<br \/>\n  dependsOn: deploy_development<\/p>\n<p><strong>2. Build and Package Stage<\/strong><\/p>\n<p>The first stage focuses solely on building and packaging the application for deployment:<\/p>\n<p>&#8211; stage: build_and_test<br \/>\n  jobs:<br \/>\n  &#8211; job: buildAndPackage<br \/>\n    pool:<br \/>\n      vmImage: ubuntu-latest<br \/>\n    steps:<br \/>\n    &#8211; task: Bash@3<br \/>\n      displayName: Install azd<br \/>\n      inputs:<br \/>\n        targetType: &#8216;inline&#8217;<br \/>\n        script: |<br \/>\n          curl -fsSL https:\/\/aka.ms\/install-azd.sh | bash<\/p>\n<p>    &#8211; task: PowerShell@2<br \/>\n      displayName: Configure AZD to Use AZ CLI Authentication.<br \/>\n      inputs:<br \/>\n        targetType: inline<br \/>\n        script: |<br \/>\n          azd config set auth.useAzCliAuth &#8220;true&#8221;<br \/>\n        pwsh: true<\/p>\n<p>    &#8211; task: AzureCLI@2<br \/>\n      displayName: Package Application<br \/>\n      inputs:<br \/>\n        azureSubscription: azconnection<br \/>\n        scriptType: bash<br \/>\n        scriptLocation: inlineScript<br \/>\n        keepAzSessionActive: true<br \/>\n        inlineScript: |<br \/>\n          mkdir -p .\/dist<br \/>\n          azd package app &#8211;output-path .\/dist\/app-package.zip  &#8211;no-prompt<br \/>\n          echo &#8220;\u2705 Application packaged successfully&#8221;<\/p>\n<p>    &#8211; task: PublishPipelineArtifact@1<br \/>\n      displayName: Upload Package Artifact<br \/>\n      inputs:<br \/>\n        targetPath: &#8216;.\/dist\/app-package.zip&#8217;<br \/>\n        artifact: &#8216;app-package&#8217;<br \/>\n        publishLocation: &#8216;pipeline&#8217;<\/p>\n<p><strong>3. Deploy to Development Stage<\/strong><\/p>\n<p>The second stage provisions development infrastructure and deploys the packaged application:<\/p>\n<p>&#8211; stage: deploy_development<br \/>\n  dependsOn: build_and_test<br \/>\n  jobs:<br \/>\n  &#8211; job: deployToDevelopment<br \/>\n    pool:<br \/>\n      vmImage: ubuntu-latest<br \/>\n    steps:<br \/>\n    &#8211; task: Bash@3<br \/>\n      displayName: Install azd<br \/>\n      inputs:<br \/>\n        targetType: &#8216;inline&#8217;<br \/>\n        script: |<br \/>\n          curl -fsSL https:\/\/aka.ms\/install-azd.sh | bash<\/p>\n<p>    &#8211; task: PowerShell@2<br \/>\n      displayName: Configure AZD to Use AZ CLI Authentication.<br \/>\n      inputs:<br \/>\n        targetType: inline<br \/>\n        script: |<br \/>\n          azd config set auth.useAzCliAuth &#8220;true&#8221;<br \/>\n        pwsh: true<\/p>\n<p>    &#8211; task: AzureCLI@2<br \/>\n      displayName: Provision DEV Infrastructure<br \/>\n      inputs:<br \/>\n        azureSubscription: azconnection<br \/>\n        scriptType: bash<br \/>\n        scriptLocation: inlineScript<br \/>\n        keepAzSessionActive: true<br \/>\n        inlineScript: |<br \/>\n          azd provision &#8211;no-prompt<\/p>\n<p>    &#8211; task: DownloadPipelineArtifact@2<br \/>\n      displayName: Download Package Artifact<br \/>\n      inputs:<br \/>\n        buildType: &#8216;current&#8217;<br \/>\n        artifactName: &#8216;app-package&#8217;<br \/>\n        targetPath: &#8216;.\/artifacts&#8217;<\/p>\n<p>    &#8211; task: AzureCLI@2<br \/>\n      displayName: Deploy to Development<br \/>\n      inputs:<br \/>\n        azureSubscription: azconnection<br \/>\n        scriptType: bash<br \/>\n        scriptLocation: inlineScript<br \/>\n        keepAzSessionActive: true<br \/>\n        inlineScript: |<br \/>\n          azd deploy app &#8211;from-package .\/artifacts\/app-package.zip &#8211;no-prompt<\/p>\n<p><strong>4. Validation Gate<\/strong><\/p>\n<p>Add validation checks before promotion to production:<\/p>\n<p>    &#8211; task: AzureCLI@2<br \/>\n      displayName: Validate Application<br \/>\n      inputs:<br \/>\n        azureSubscription: azconnection<br \/>\n        scriptType: bash<br \/>\n        scriptLocation: inlineScript<br \/>\n        keepAzSessionActive: true<br \/>\n        inlineScript: |<br \/>\n          echo &#8220;\ud83d\udd0d Validating application in development environment&#8230;&#8221;<br \/>\n          # TODO: Add actual validation here<br \/>\n          # Examples:<br \/>\n          # &#8211; Health checks and integration tests<br \/>\n          # &#8211; Security and compliance scanning<br \/>\n          # &#8211; Performance validation<br \/>\n          sleep 3  # Simulate validation time<br \/>\n          echo &#8220;\u2705 Application validation passed&#8221;<\/p>\n<p><strong>5. Promote to Production Stage<\/strong><\/p>\n<p>The final stage uses environment-specific variables to deploy to production:<\/p>\n<p>&#8211; stage: promote_to_Prod<br \/>\n  dependsOn: deploy_development<br \/>\n  jobs:<br \/>\n  &#8211; job: deployProduction<br \/>\n    # use prod settings to override default environment variables<br \/>\n    # this variables become ENV VARS for all tasks in this job<br \/>\n    variables:<br \/>\n      AZURE_ENV_NAME: $(AZURE_PROD_ENV_NAME)<br \/>\n      AZURE_ENV_TYPE: $(AZURE_PROD_ENV_TYPE)<br \/>\n      AZURE_LOCATION: $(AZURE_PROD_LOCATION)<br \/>\n      AZURE_SUBSCRIPTION_ID: $(AZURE_PROD_SUBSCRIPTION_ID)<br \/>\n    pool:<br \/>\n      vmImage: ubuntu-latest<br \/>\n    steps:<br \/>\n    &#8211; task: Bash@3<br \/>\n      displayName: Install azd<br \/>\n      inputs:<br \/>\n        targetType: &#8216;inline&#8217;<br \/>\n        script: |<br \/>\n          curl -fsSL https:\/\/aka.ms\/install-azd.sh | bash<\/p>\n<p>    &#8211; task: PowerShell@2<br \/>\n      displayName: Configure AZD to Use AZ CLI Authentication.<br \/>\n      inputs:<br \/>\n        targetType: inline<br \/>\n        script: |<br \/>\n          azd config set auth.useAzCliAuth &#8220;true&#8221;<br \/>\n        pwsh: true<\/p>\n<p>    &#8211; task: DownloadPipelineArtifact@2<br \/>\n      displayName: Download Package Artifact<br \/>\n      inputs:<br \/>\n        buildType: &#8216;current&#8217;<br \/>\n        artifactName: &#8216;app-package&#8217;<br \/>\n        targetPath: &#8216;.\/artifacts&#8217;<\/p>\n<p>    &#8211; task: AzureCLI@2<br \/>\n      displayName: Deploy to PROD<br \/>\n      inputs:<br \/>\n        azureSubscription: azconnection<br \/>\n        scriptType: bash<br \/>\n        scriptLocation: inlineScript<br \/>\n        keepAzSessionActive: true<br \/>\n        inlineScript: |<br \/>\n          azd deploy app &#8211;from-package .\/artifacts\/app-package.zip &#8211;no-prompt<\/p>\n<h2>Try It Out<\/h2>\n<p>You can try this approach using the complete implementation <a href=\"https:\/\/github.com\/puicchan\/azd-dev-prod-appservice-storage.\">here<\/a><\/p>\n<p>Watch the walkthrough:<\/p>\n\n<h3>Prerequisites<\/h3>\n<p>You\u2019ll 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 <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/developer\/azure-developer-cli\/pipeline-azure-pipelines\">Microsoft Learn documentation<\/a>.<\/p>\n<h3>Setup Steps<\/h3>\n<p><strong>1. Initialize Project<\/strong><\/p>\n<p>azd init -t https:\/\/github.com\/puicchan\/azd-dev-prod-appservice-storage<\/p>\n<p>Use environment name like projazdo-dev.<\/p>\n<p><strong>2. Edit azure.yaml<\/strong><\/p>\n<p>Make sure you configure Azure DevOps as the CICD tool and add these pipeline variables:<\/p>\n<p>pipeline:<br \/>\n  provider: azdo<br \/>\n  variables:<br \/>\n    &#8211; AZURE_PROD_ENV_NAME<br \/>\n    &#8211; AZURE_PROD_ENV_TYPE<br \/>\n    &#8211; AZURE_PROD_LOCATION<br \/>\n    &#8211; AZURE_PROD_SUBSCRIPTION_ID<\/p>\n<p><strong>3. Set Up Development Environment<\/strong><\/p>\n<p>azd up<\/p>\n<p><strong>4. Set Up Production Environment<\/strong><\/p>\n<p>We will run azd provision and rely on Azure Pipeline to deploy the app to production:<\/p>\n<p>azd env new projazdo-prod<br \/>\nazd env set AZURE_ENV_TYPE prod<br \/>\nazd provision<\/p>\n<p><strong>5. Test the Flow<\/strong><\/p>\n<p>Switch back to Development:<\/p>\n<p>azd env select projazdo-dev<\/p>\n<p>Edit your application code and make sure you run azd env set to configure the following environment variables the pipeline requires:<\/p>\n<p>AZURE_PROD_ENV_NAME<br \/>\nAZURE_PROD_ENV_TYPE<br \/>\nAZURE_PROD_LOCATION<br \/>\nAZURE_PROD_SUBSCRIPTION_ID <\/p>\n<p><strong>6. Configure CI\/CD Pipeline<\/strong><\/p>\n<p>azd pipeline config<\/p>\n<p>Go to your Azure Pipelines Organization and check out the pipeline run.<\/p>\n<h2>Pro Tip: Enhance Your Pipeline with AI<\/h2>\n<p>Need help with Azure DevOps and azd? <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms-azuretools.vscode-azure-github-copilot\">GitHub Copilot for Azure<\/a> with <a href=\"https:\/\/learn.microsoft.com\/azure\/developer\/azure-mcp-server\/overview\">Azure MCP<\/a> can help you enhance your azure-dev.yml file directly in VS Code.<\/p>\n<p>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:<\/p>\n<p><strong>Debug pipeline issues<\/strong>: Analyze YAML syntax errors and configuration problems.<br \/>\n<strong>Add validation steps<\/strong>: Suggest health checks, security scans, or integration tests.<br \/>\n<strong>Optimize deployment strategies<\/strong>: Recommend blue-green deployments or canary releases.<br \/>\n<strong>Configure environment-specific logic<\/strong>: Help set up conditional steps for different environments.<\/p>\n<p>Simply ask GitHub Copilot questions like:<\/p>\n<p>\u201cAdd a health check validation step to my Azure DevOps pipeline\u201d<br \/>\n\u201cHow can I add manual approval gates before production deployment?\u201d<\/p>\n<p>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.<\/p>\n<h2>Conclusion<\/h2>\n<p>This Azure DevOps implementation demonstrates how the \u201cbuild once, deploy everywhere\u201d 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.<\/p>\n<p>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.<\/p>\n<p>Questions about implementation or want to share your Azure DevOps approach? Join the discussion <a href=\"https:\/\/github.com\/Azure\/azure-dev\/discussions\/5447\">here<\/a>.<\/p>\n<p>The post <a href=\"https:\/\/devblogs.microsoft.com\/devops\/azure-developer-cli-from-dev-to-prod-with-azure-devops-pipelines\/\">Azure Developer CLI: From Dev to Prod with Azure DevOps Pipelines<\/a> appeared first on <a href=\"https:\/\/devblogs.microsoft.com\/devops\">Azure DevOps Blog<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>Building on our previous post about implementing dev-to-prod promotion with GitHub Actions, this follow-up demonstrates the same \u201cbuild once, deploy [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":94,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[3],"tags":[],"class_list":["post-2372","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/2372","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=2372"}],"version-history":[{"count":0,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/2372\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media\/94"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=2372"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=2372"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=2372"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}