Skip to content

Deploy Landing Zone (Bicep) #29

Deploy Landing Zone (Bicep)

Deploy Landing Zone (Bicep) #29

Workflow file for this run

# OIDC tokens from azure/login@v2 are valid for ~1 hour. Each job performs its
# own fresh login, so token expiry is not a concern for typical deployments.
# If a single deployment exceeds 1 hour, split it into smaller deployments or
# add a second azure/login step before the long-running command.
name: Deploy Landing Zone (Bicep)
on:
push:
branches: [main]
paths:
- 'infra/bicep/**'
pull_request:
branches: [main]
paths:
- 'infra/bicep/**'
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy'
required: true
type: choice
options:
- prod
- nonprod
permissions:
id-token: write
contents: read
pull-requests: write
concurrency:
group: deploy-bicep-${{ github.ref }}
cancel-in-progress: false
env:
BICEP_DIR: infra/bicep
jobs:
validate:
name: Validate Bicep (${{ matrix.environment }})
runs-on: ubuntu-latest
strategy:
matrix:
environment: ${{ github.event_name == 'workflow_dispatch' && fromJSON(format('["{0}"]', github.event.inputs.environment)) || fromJSON('["prod","nonprod"]') }}
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ matrix.environment == 'prod' && secrets.AZURE_SUBSCRIPTION_ID_PROD || secrets.AZURE_SUBSCRIPTION_ID_NONPROD }}
- name: Validate Bicep
env:
AZURE_LOCATION: ${{ vars.AZURE_LOCATION || 'eastus2' }}
run: |
az deployment sub validate \
--location "$AZURE_LOCATION" \
--template-file ${{ env.BICEP_DIR }}/main.bicep \
--parameters ${{ env.BICEP_DIR }}/parameters/${{ matrix.environment }}.bicepparam \
--name "sslz-validate-${{ matrix.environment }}"
what-if:
name: What-If (${{ matrix.environment }})
runs-on: ubuntu-latest
needs: validate
if: github.event_name == 'pull_request'
strategy:
matrix:
environment: ${{ github.event_name == 'workflow_dispatch' && fromJSON(format('["{0}"]', github.event.inputs.environment)) || fromJSON('["prod","nonprod"]') }}
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ matrix.environment == 'prod' && secrets.AZURE_SUBSCRIPTION_ID_PROD || secrets.AZURE_SUBSCRIPTION_ID_NONPROD }}
- name: What-If
id: whatif
env:
AZURE_LOCATION: ${{ vars.AZURE_LOCATION || 'eastus2' }}
run: |
az deployment sub what-if \
--location "$AZURE_LOCATION" \
--template-file ${{ env.BICEP_DIR }}/main.bicep \
--parameters ${{ env.BICEP_DIR }}/parameters/${{ matrix.environment }}.bicepparam \
--name "sslz-whatif-${{ matrix.environment }}" \
2>&1 | tee whatif-output.txt
- name: Post What-If to PR
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const whatif = fs.readFileSync('whatif-output.txt', 'utf8');
const truncated = whatif.length > 60000 ? whatif.substring(0, 60000) + '\n... (truncated)' : whatif;
// Count resource changes from what-if output
const createMatch = whatif.match(/Create:\s*(\d+)/);
const modifyMatch = whatif.match(/Modify:\s*(\d+)/);
const deleteMatch = whatif.match(/Delete:\s*(\d+)/);
const noChanges = whatif.includes('no change');
let badge;
if (noChanges && !createMatch && !modifyMatch && !deleteMatch) {
badge = '`No changes`';
} else {
const parts = [];
if (createMatch) parts.push(`${createMatch[1]} to create`);
if (modifyMatch) parts.push(`${modifyMatch[1]} to modify`);
if (deleteMatch) parts.push(`${deleteMatch[1]} to delete`);
badge = parts.length > 0 ? `\`${parts.join(', ')}\`` : '`See details`';
}
const body = [
`### Bicep What-If — \`${{ matrix.environment }}\` ${badge}`,
'',
'<details>',
'<summary>Show What-If Output</summary>',
'',
'```',
truncated,
'```',
'',
'</details>'
].join('\n');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
deploy-nonprod:
name: Deploy Non-Prod
runs-on: ubuntu-latest
needs: validate
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || github.event.inputs.environment == 'nonprod')
environment: nonprod
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID_NONPROD }}
- name: Deploy
env:
AZURE_LOCATION: ${{ vars.AZURE_LOCATION || 'eastus2' }}
run: |
az deployment sub create \
--location "$AZURE_LOCATION" \
--template-file ${{ env.BICEP_DIR }}/main.bicep \
--parameters ${{ env.BICEP_DIR }}/parameters/nonprod.bicepparam \
--name "lz-nonprod-$(date +%Y%m%d-%H%M%S)"
deploy-prod:
name: Deploy Prod
runs-on: ubuntu-latest
needs: validate
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || github.event.inputs.environment == 'prod')
environment: prod
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID_PROD }}
- name: Deploy
env:
AZURE_LOCATION: ${{ vars.AZURE_LOCATION || 'eastus2' }}
run: |
az deployment sub create \
--location "$AZURE_LOCATION" \
--template-file ${{ env.BICEP_DIR }}/main.bicep \
--parameters ${{ env.BICEP_DIR }}/parameters/prod.bicepparam \
--name "lz-prod-$(date +%Y%m%d-%H%M%S)"