{"id":10112,"date":"2025-12-26T00:52:24","date_gmt":"2025-12-26T00:52:24","guid":{"rendered":"https:\/\/techtrendfeed.com\/?p=10112"},"modified":"2025-12-26T00:52:25","modified_gmt":"2025-12-26T00:52:25","slug":"decoupling-azure-releases-with-github-actions","status":"publish","type":"post","link":"https:\/\/techtrendfeed.com\/?p=10112","title":{"rendered":"Decoupling Azure Releases With GitHub Actions"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div>\n<p>Cloud deployments typically fail as a result of surroundings configurations are hardcoded into the construct course of. Here&#8217;s a sample to decouple your Construct Artifacts out of your Deployment Logic utilizing GitHub Actions and a versatile JSON Configuration map.<\/p>\n<p>On the planet of <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/dzone.com\/refcardz\/getting-started-kubernetes\">Kubernetes<\/a>, we&#8217;re used to the separation of considerations: Docker builds the picture, and Helm\/Kustomize handles the surroundings configuration. Nonetheless, when working with serverless (Azure Features) or PaaS (App Service), builders typically fall into the entice of monolithic pipelines. They construct a package deal that solely works in DEV, after which rebuild it for PROD.<\/p>\n<p>This results in &#8220;Artifact Drift,&#8221;\u00a0the place the binary operating in Manufacturing just isn&#8217;t arguably the identical binary that handed testing in Staging.<\/p>\n<p>A latest implementation by Fujitsu\u2019s International Gateway Division tackles this drawback head-on. By shifting away from handbook Azure CLI deployments to a strict GitHub Actions workflow, they lowered launch time by 75% (from 2 hours to half-hour) and eradicated handbook configuration errors.<\/p>\n<p>Right here is learn how to implement their &#8220;Atmosphere Configuration Map&#8221; sample to realize protected, automated deployments throughout Azure environments.\u00a0<\/p>\n<h2>The Downside: The &#8220;Config Matrix&#8221; Hell<\/h2>\n<p>In an Agile surroundings, particularly throughout <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/dzone.com\/articles\/what-is-the-purpose-of-a-proof-of-concept-poc\">Proof-of-Idea<\/a> (PoC) phases, property are changed continuously. You may need:<\/p>\n<ol>\n<li>Azure Features (API logic)<\/li>\n<li>Cosmos DB (Knowledge persistence)<\/li>\n<li>Digital machines (Legacy processing)<\/li>\n<\/ol>\n<p>The problem is that DEV, STAGING, and PROD have utterly completely different Useful resource Group names, Subscription IDs, and tier settings (e.g., Use a less expensive SKU for Dev).<\/p>\n<p>When you hardcode these into your pipeline YAML, your pipeline turns into brittle. When you attempt to handle them manually, you danger human error.<\/p>\n<h2>The Resolution: The &#8220;Construct-As soon as, Deploy-Many&#8221; Structure<\/h2>\n<p>The core philosophy of this sample is straightforward:\u00a0Construct the binary as soon as, model it, after which inject configuration solely in the mean time of deployment.<\/p>\n<p>The workflow consists of three distinct phases:<\/p>\n<ol>\n<li><strong>Construct part<\/strong>: Compile code and generate a .zip artifact.<\/li>\n<li><strong>Launch part<\/strong>: Retailer the artifact in GitHub Releases (immutable versioning).<\/li>\n<li><strong>Deploy part<\/strong>: A workflow reads a Config JSON, pulls the artifact, and pushes it to Azure.<\/li>\n<\/ol>\n<p>A pattern workflow to know launch move utilizing GitHub Actions (handbook versus automated execution):<\/p>\n<p><img decoding=\"async\" data-fr-image-pasted=\"true\" alt=\"article image\" class=\"fr-fic fr-dib lazyload\" src=\"https:\/\/dz2cdn1.dzone.com\/storage\/temp\/18773851-workflow-using-gitactions.png\" data-url=\"https:\/\/dz2cdn1.dzone.com\/storage\/temp\/18773851-workflow-using-gitactions.png\"\/><\/p>\n<h3>1. The Atmosphere Configuration Map (config.json)<\/h3>\n<p>As a substitute of scattering variables throughout GitHub Secrets and techniques or Azure App Settings, we centralize the surroundings definition in a JSON file dedicated to the repository. This acts as our &#8220;Supply of Reality.&#8221;<\/p>\n<p>File: .github\/config\/config.json<\/p>\n<div class=\"codeMirror-wrapper\" contenteditable=\"false\">\n<div contenteditable=\"false\">\n<div class=\"codeMirror-code--wrapper\" data-code=\"{&#10;  &quot;dev&quot;: {&#10;    &quot;subscriptionId&quot;: &quot;sub-id-dev-001&quot;,&#10;    &quot;resourceGroup&quot;: &quot;rg-app-dev&quot;,&#10;    &quot;resources&quot;: [&#10;      {&#10;        &quot;type&quot;: &quot;function&quot;,&#10;        &quot;name&quot;: &quot;func-api-dev&quot;,&#10;        &quot;slot&quot;: &quot;staging&quot; &#10;      }&#10;    ]&#10;  },&#10;  &quot;prod&quot;: {&#10;    &quot;subscriptionId&quot;: &quot;sub-id-prod-999&quot;,&#10;    &quot;resourceGroup&quot;: &quot;rg-app-prod&quot;,&#10;    &quot;resources&quot;: [&#10;      {&#10;        &quot;type&quot;: &quot;function&quot;,&#10;        &quot;name&quot;: &quot;func-api-prod&quot;,&#10;        &quot;slot&quot;: &quot;production&quot;&#10;      },&#10;      {&#10;        &quot;type&quot;: &quot;cosmosdb&quot;,&#10;        &quot;name&quot;: &quot;cosmos-core-prod&quot;,&#10;        &quot;partitionKey&quot;: &quot;\/userId&quot;&#10;      }&#10;    ]&#10;  }&#10;}\" data-lang=\"application\/json\">\n<pre><code lang=\"application\/json\">{\n  \"dev\": {\n    \"subscriptionId\": \"sub-id-dev-001\",\n    \"resourceGroup\": \"rg-app-dev\",\n    \"sources\": [\n      {\n        \"type\": \"function\",\n        \"name\": \"func-api-dev\",\n        \"slot\": \"staging\" \n      }\n    ]\n  },\n  \"prod\": {\n    \"subscriptionId\": \"sub-id-prod-999\",\n    \"resourceGroup\": \"rg-app-prod\",\n    \"sources\": [\n      {\n        \"type\": \"function\",\n        \"name\": \"func-api-prod\",\n        \"slot\": \"production\"\n      },\n      {\n        \"type\": \"cosmosdb\",\n        \"name\": \"cosmos-core-prod\",\n        \"partitionKey\": \"\/userId\"\n      }\n    ]\n  }\n}<\/code><\/pre>\n<\/p><\/div><\/div>\n<\/div>\n<p>Key perception: Discover that dev would possibly deploy to a staging slot, whereas prod deploys to manufacturing. This logic is abstracted away from the construct script.\u00a0<\/p>\n<h3>2. The Deployment &#8220;Operator&#8221; (Shell + Azure CLI)<\/h3>\n<p>As a substitute of relying solely on inflexible GitHub Actions plugins, this sample makes use of a Shell script to parse the JSON and execute the logic. This makes the deployment transportable \u2014 you&#8217;ll be able to run it domestically or in CI.<\/p>\n<p>File: scripts\/deploy.sh<\/p>\n<div class=\"codeMirror-wrapper\" contenteditable=\"false\">\n<div contenteditable=\"false\">\n<div class=\"codeMirror-code--wrapper\" data-code=\"#!\/bin\/bash&#10;ENV_FLAVOR=$1  # e.g., &quot;prod&quot;&#10;TAG_VERSION=$2 # e.g., &quot;v1.0.2&quot;&#10;&#10;# 1. Read Config using jq&#10;CONFIG=$(cat .github\/config\/config.json | jq -r --arg env &quot;$ENV_FLAVOR&quot; '.[$env]')&#10;RG_NAME=$(echo $CONFIG | jq -r '.resourceGroup')&#10;SUB_ID=$(echo $CONFIG | jq -r '.subscriptionId')&#10;&#10;# 2. Login to Azure&#10;az account set --subscription $SUB_ID&#10;&#10;# 3. Download the Immutable Artifact from GitHub Releases&#10;wget https:\/\/github.com\/my-org\/my-repo\/releases\/download\/$TAG_VERSION\/build-artifact.zip&#10;&#10;# 4. Iterate through defined resources and deploy&#10;echo $CONFIG | jq -c '.resources[]' | while read resource; do&#10;    TYPE=$(echo $resource | jq -r '.type')&#10;    NAME=$(echo $resource | jq -r '.name')&#10;    &#10;    if [ &quot;$TYPE&quot; == &quot;function&quot; ]; then&#10;        echo &quot;Deploying $TAG_VERSION to Azure Function: $NAME...&quot;&#10;        az functionapp deployment source config-zip &#10;            --resource-group $RG_NAME &#10;            --name $NAME &#10;            --src build-artifact.zip&#10;    fi&#10;done\" data-lang=\"text\/x-sh\">\n<pre><code lang=\"text\/x-sh\">#!\/bin\/bash\nENV_FLAVOR=$1  # e.g., \"prod\"\nTAG_VERSION=$2 # e.g., \"v1.0.2\"\n\n# 1. Learn Config utilizing jq\nCONFIG=$(cat .github\/config\/config.json | jq -r --arg env \"$ENV_FLAVOR\" '.[$env]')\nRG_NAME=$(echo $CONFIG | jq -r '.resourceGroup')\nSUB_ID=$(echo $CONFIG | jq -r '.subscriptionId')\n\n# 2. Login to Azure\naz account set --subscription $SUB_ID\n\n# 3. Obtain the Immutable Artifact from GitHub Releases\nwget https:\/\/github.com\/my-org\/my-repo\/releases\/obtain\/$TAG_VERSION\/build-artifact.zip\n\n# 4. Iterate by means of outlined sources and deploy\necho $CONFIG | jq -c '.sources[]' | whereas learn useful resource; do\n    TYPE=$(echo $useful resource | jq -r '.kind')\n    NAME=$(echo $useful resource | jq -r '.identify')\n    \n    if [ \"$TYPE\" == \"function\" ]; then\n        echo \"Deploying $TAG_VERSION to Azure Perform: $NAME...\"\n        az functionapp deployment supply config-zip \n            --resource-group $RG_NAME \n            --name $NAME \n            --src build-artifact.zip\n    fi\nfinished<\/code><\/pre>\n<\/p><\/div><\/div>\n<\/div>\n<h3>3. The GitHub Actions Workflow<\/h3>\n<p>Lastly, we tie it along with a Workflow that requires\u00a0handbook approval for Manufacturing environments.<\/p>\n<p>File: .github\/workflows\/deploy.yaml<\/p>\n<div class=\"codeMirror-wrapper newest\" contenteditable=\"false\">\n<div contenteditable=\"false\">\n<div class=\"codeMirror-code--wrapper\" data-code=\"name: Deploy to Azure&#10;&#10;on:&#10;  release:&#10;    types: [published]&#10;  workflow_dispatch:&#10;    inputs:&#10;      environment:&#10;        description: 'Target Environment'&#10;        required: true&#10;        default: 'dev'&#10;        type: choice&#10;        options:&#10;        - dev&#10;        - prod&#10;&#10;jobs:&#10;  deploy:&#10;    runs-on: ubuntu-latest&#10;    environment: ${{ github.event.inputs.environment }} # Uses GitHub Environments for approvals&#10;    steps:&#10;      - name: Checkout Code&#10;        uses: actions\/checkout@v3&#10;&#10;      - name: Azure Login&#10;        uses: azure\/login@v1&#10;        with:&#10;          creds: ${{ secrets.AZURE_CREDENTIALS }}&#10;&#10;      - name: Run Deployment Operator&#10;        run: |&#10;          chmod +x scripts\/deploy.sh&#10;          .\/scripts\/deploy.sh ${{ github.event.inputs.environment }} ${{ github.event.release.tag_name }}\" data-lang=\"text\/x-yaml\">\n<pre><code lang=\"text\/x-yaml\">identify: Deploy to Azure\n\non:\n  launch:\n    varieties: [published]\n  workflow_dispatch:\n    inputs:\n      surroundings:\n        description: 'Goal Atmosphere'\n        required: true\n        default: 'dev'\n        kind: selection\n        choices:\n        - dev\n        - prod\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    surroundings: ${{ github.occasion.inputs.surroundings }} # Makes use of GitHub Environments for approvals\n    steps:\n      - identify: Checkout Code\n        makes use of: actions\/checkout@v3\n\n      - identify: Azure Login\n        makes use of: azure\/login@v1\n        with:\n          creds: ${{ secrets and techniques.AZURE_CREDENTIALS }}\n\n      - identify: Run Deployment Operator\n        run: |\n          chmod +x scripts\/deploy.sh\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .\/scripts\/deploy.sh ${{ github.occasion.inputs.surroundings }} ${{ github.occasion.launch.tag_name }}<\/code><\/pre>\n<\/p><\/div><\/div>\n<\/div>\n<h2>The Structure Visualized<\/h2>\n<p>This method separates the lifecycle of the code from the lifecycle of the surroundings.<\/p>\n<p><img decoding=\"async\" data-fr-image-pasted=\"true\" alt=\"Architecture\" class=\"fr-fic fr-dib lazyload\" style=\"width: 472px;\" src=\"https:\/\/dz2cdn1.dzone.com\/storage\/temp\/18773855-lifecycle-of-code-in-different-environments.png\" data-url=\"https:\/\/dz2cdn1.dzone.com\/storage\/temp\/18773855-lifecycle-of-code-in-different-environments.png\"\/><\/p>\n<h2>Why This Sample Works (The Outcomes)<\/h2>\n<p>Within the Fujitsu case research, adopting this sample solved three essential points:<\/p>\n<ol>\n<li><strong>Prompt rollbacks<\/strong>: As a result of artifacts are saved in GitHub Releases, &#8220;rolling again&#8221; is simply re-running the deployment job with the earlier Model Tag (e.g., v1.0.1 as a substitute of v1.0.2). No rebuilding vital.<\/li>\n<li><strong>Useful resource isolation<\/strong>: The config.json permits granular management. You&#8217;ll be able to outline particular permissions or exclusion guidelines (e.g., ignore: [&#8220;iam&#8221;]) for Dev environments to forestall unintended permission overwrites.<\/li>\n<li><strong>Eliminating &#8220;VM Lock&#8221;<\/strong>: Beforehand, deployments required unique entry to VMs, blocking different builders. By shifting to Azure Features and asynchronous Actions, the pipeline turned non-blocking.<\/li>\n<\/ol>\n<h2>Conclusion<\/h2>\n<p>Instruments like Terraform and Bicep are glorious for <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/dzone.com\/articles\/what-is-infrastructure-as-code\">Infrastructure as Code<\/a> (creating the sources). Nonetheless, for Deployment as Code (shifting the bits to the sources), a light-weight, configuration-driven method utilizing GitHub Actions and JSON maps gives the pliability wanted for high-velocity groups.<\/p>\n<p>By decoupling the &#8220;What&#8221; (the construct artifact) from the &#8220;The place&#8221; (the surroundings config), you flip a fragile handbook launch course of into a sturdy, repeatable engine.<\/p>\n<\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>Cloud deployments typically fail as a result of surroundings configurations are hardcoded into the construct course of. Here&#8217;s a sample to decouple your Construct Artifacts out of your Deployment Logic utilizing GitHub Actions and a versatile JSON Configuration map. On the planet of Kubernetes, we&#8217;re used to the separation of considerations: Docker builds the picture, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":10114,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[56],"tags":[2469,1126,7112,933,99],"class_list":["post-10112","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software","tag-actions","tag-azure","tag-decoupling","tag-github","tag-releases"],"_links":{"self":[{"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/posts\/10112","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10112"}],"version-history":[{"count":1,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/posts\/10112\/revisions"}],"predecessor-version":[{"id":10113,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/posts\/10112\/revisions\/10113"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=\/wp\/v2\/media\/10114"}],"wp:attachment":[{"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10112"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10112"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/techtrendfeed.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10112"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}<!-- This website is optimized by Airlift. Learn more: https://airlift.net. Template:. Learn more: https://airlift.net. Template: 69d9690a190636c2e0989534. Config Timestamp: 2026-04-10 21:18:02 UTC, Cached Timestamp: 2026-05-14 21:45:57 UTC -->