Skip to content

Instantly share code, notes, and snippets.

@ronaldbosma
Last active April 17, 2026 10:04
Show Gist options
  • Select an option

  • Save ronaldbosma/c033a33483f67cfe9eb4752d1f52a7fa to your computer and use it in GitHub Desktop.

Select an option

Save ronaldbosma/c033a33483f67cfe9eb4752d1f52a7fa to your computer and use it in GitHub Desktop.
GitHub Actions Workflow for Azure Developer CLI (azd) Templates
name: azure-dev
on:
workflow_dispatch:
inputs:
cleanup-resources:
description: 'Clean up resources after deployment'
required: false
default: true
type: boolean
pull_request:
# Also trigger on ready_for_review to catch the transition from draft to ready state.
types: [opened, synchronize, reopened, ready_for_review]
branches:
- main
paths:
- '.github/workflows/azure-dev.yml'
- '.github/workflows/scripts/**'
- 'hooks/**'
- 'infra/**'
- 'src/**'
- 'tests/IntegrationTests/**'
- 'azure.yaml'
- 'bicepconfig.json'
defaults:
run:
shell: pwsh # Use PowerShell Core for all scripts (the azd hooks are written in PowerShell)
env:
# Add a unique suffix to the environment name for pull requests to avoid name conflicts
AZURE_ENV_NAME: ${{ github.event.pull_request.number && format('{0}-pr{1}', vars.AZURE_ENV_NAME, github.event.pull_request.number) || vars.AZURE_ENV_NAME }}
AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID || vars.AZURE_SUBSCRIPTION_ID }}
# Set additional configuration settings
# SAMPLE_ENVIRONMENT_VARIABLE: ${{ vars.SAMPLE_ENVIRONMENT_VARIABLE }}
jobs:
# ------------------------------------------------------------
# Build, Verify and Package
# ------------------------------------------------------------
build-verify-package:
name: Build, Verify and Package
runs-on: ubuntu-latest
permissions:
id-token: write # Required to fetch an OIDC token for Azure authentication
contents: read # Required to checkout code if needed
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup azd
uses: Azure/setup-azd@v2
- name: Setup .NET 10
uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
# This template uses a workaround to deploy the Logic App workflow, which requires the npm CLI.
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 'latest'
- name: Print Tool Versions
run: |
az version
az bicep version
azd version
Write-Host ".NET SDK Version: $(dotnet --version)"
Write-Host "Node.js Version: $(node --version)"
Write-Host "npm Version: $(npm --version)"
# Use Azure CLI authentication with azd commands so credentials are shared between azd commands and az (Azure CLI) commands used in hooks.
- name: Configure azd to use Azure CLI Authentication
run: |
azd config set auth.useAzCliAuth "true"
# Login to the Azure CLI with OpenID Connect (OIDC) using federated identity credentials.
- name: Azure CLI Login
uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID || vars.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID || vars.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID || vars.AZURE_SUBSCRIPTION_ID }}
- name: Bicep Lint
run: |
az bicep lint --file ./infra/main.bicep
- name: Validate Template
run: |
az deployment sub validate `
--template-file './infra/main.bicep' `
--location $env:AZURE_LOCATION `
--parameters environmentName=$env:AZURE_ENV_NAME `
location=$env:AZURE_LOCATION
- name: Run Unit Tests for Function App
run: |
dotnet run --report-trx --results-directory "${{ github.workspace }}/artifacts/TestResults/functionApp"
working-directory: ./src/functionApp/FunctionApp.Tests
- name: Run Unit Tests for Logic App Functions
run: |
dotnet run --report-trx --results-directory "${{ github.workspace }}/artifacts/TestResults/logicApp"
working-directory: ./src/logicApp/Functions.Tests
- name: Run Unit Tests for Logic App Workflows
run: |
dotnet run --report-trx --results-directory "${{ github.workspace }}/artifacts/TestResults/logicApp"
working-directory: ./src/logicApp/Workflows.Tests
- name: Upload Unit Test Results
if: always()
uses: actions/upload-artifact@v7
with:
name: unit-test-results
path: ./artifacts/TestResults/
retention-days: 1
- name: Create artifacts folder
run: |
mkdir -p ./artifacts
- name: Package Function App
run: |
azd package functionApp --output-path ./artifacts/functionapp-package.zip --no-prompt
- name: Package Logic App
run: |
azd package logicApp --output-path ./artifacts/logicapp-package.zip --no-prompt
- name: Build Integration Tests
run: |
dotnet build ./tests/IntegrationTests/IntegrationTests.csproj --configuration Release --output ./artifacts/integration-tests
- name: Upload Function App Package
uses: actions/upload-artifact@v7
with:
name: functionapp-package
path: ./artifacts/functionapp-package.zip
retention-days: 1
- name: Upload Logic App Package
uses: actions/upload-artifact@v7
with:
name: logicapp-package
path: ./artifacts/logicapp-package.zip
retention-days: 1
- name: Upload Integration Tests Package
uses: actions/upload-artifact@v7
with:
name: integration-tests-package
path: ./artifacts/integration-tests/
retention-days: 1
# ------------------------------------------------------------
# Deploy to Azure
# ------------------------------------------------------------
deploy:
name: Deploy to Azure
needs: build-verify-package
# Only deploy if it's not a pull request or if the pull request is ready for review (not a draft) to avoid deploying from work-in-progress branches.
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
id-token: write # Required to fetch an OIDC token for Azure authentication
contents: read # Required to checkout code if needed
outputs:
AZURE_RESOURCE_GROUP: ${{ steps.get-outputs.outputs.AZURE_RESOURCE_GROUP }}
AZURE_ENV_ID: ${{ steps.get-outputs.outputs.AZURE_ENV_ID }}
AZURE_API_MANAGEMENT_GATEWAY_URL: ${{ steps.get-outputs.outputs.AZURE_API_MANAGEMENT_GATEWAY_URL }}
AZURE_APPLICATION_INSIGHTS_NAME: ${{ steps.get-outputs.outputs.AZURE_APPLICATION_INSIGHTS_NAME }}
AZURE_KEY_VAULT_URI: ${{ steps.get-outputs.outputs.AZURE_KEY_VAULT_URI }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup azd
uses: Azure/setup-azd@v2
# Use Azure CLI authentication with azd commands so credentials are shared between azd commands and az (Azure CLI) commands used in hooks.
- name: Configure azd to use Azure CLI Authentication
run: |
azd config set auth.useAzCliAuth "true"
# Login to the Azure CLI with OpenID Connect (OIDC) using federated identity credentials.
- name: Azure CLI Login
uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID || vars.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID || vars.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID || vars.AZURE_SUBSCRIPTION_ID }}
- name: Provision Infrastructure
run: |
azd provision --no-prompt
- name: Download Function App Package
uses: actions/download-artifact@v8
with:
name: functionapp-package
path: ./artifacts
- name: Deploy Function App
run: |
azd deploy functionApp --from-package ./artifacts/functionapp-package.zip --no-prompt
- name: Download Logic App Package
uses: actions/download-artifact@v8
with:
name: logicapp-package
path: ./artifacts
- name: Deploy Logic App
run: |
azd deploy logicApp --from-package ./artifacts/logicapp-package.zip --no-prompt
# Extract relevant azd environment variables and make them available as job outputs for subsequent jobs.
- name: Get Output Variables
id: get-outputs
run: |
$variableNames = @(
"AZURE_RESOURCE_GROUP",
"AZURE_ENV_ID",
"AZURE_API_MANAGEMENT_GATEWAY_URL",
"AZURE_APPLICATION_INSIGHTS_NAME",
"AZURE_KEY_VAULT_URI"
)
.\.github\workflows\scripts\export-azd-env-variables.ps1 -VariableNames $variableNames
# ------------------------------------------------------------
# Verify Deployment
# ------------------------------------------------------------
verify-deployment:
name: Verify Deployment
needs: deploy
runs-on: ubuntu-latest
permissions:
id-token: write # Required to fetch an OIDC token for Azure authentication
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup .NET 10
uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
- name: Download Integration Tests Package
uses: actions/download-artifact@v8
with:
name: integration-tests-package
path: ./artifacts/integration-tests
# Login to the Azure CLI with OpenID Connect (OIDC) using federated identity credentials.
# This is necessary for the integration test and verification scripts to access Azure resources.
- name: Azure CLI Login
uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID || vars.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID || vars.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID || vars.AZURE_SUBSCRIPTION_ID }}
- name: Run Integration Tests
run: |
dotnet ./artifacts/integration-tests/IntegrationTests.dll --report-trx --results-directory ./artifacts/integration-tests/TestResults
working-directory: ./
env:
# Pass the necessary deployed resource properties as environment variables so the integration tests can access them.
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID || vars.AZURE_TENANT_ID }}
AZURE_RESOURCE_GROUP: ${{ needs.deploy.outputs.AZURE_RESOURCE_GROUP }}
AZURE_API_MANAGEMENT_GATEWAY_URL: ${{ needs.deploy.outputs.AZURE_API_MANAGEMENT_GATEWAY_URL }}
AZURE_KEY_VAULT_URI: ${{ needs.deploy.outputs.AZURE_KEY_VAULT_URI }}
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v7
with:
name: integration-test-results
path: ./artifacts/integration-tests/TestResults/
retention-days: 1
- name: Verify Monitoring
run: |
.\.github\workflows\scripts\verify-monitoring.ps1 `
-ResourceGroupName "${{ needs.deploy.outputs.AZURE_RESOURCE_GROUP }}" `
-AppInsightsName "${{ needs.deploy.outputs.AZURE_APPLICATION_INSIGHTS_NAME }}"
# ------------------------------------------------------------
# Clean Up Resources
# ------------------------------------------------------------
cleanup:
name: Clean Up Resources
needs: [ deploy, verify-deployment ]
if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.cleanup-resources == 'true') }}
runs-on: ubuntu-latest
permissions:
id-token: write # Required to fetch an OIDC token for Azure authentication
contents: read # Required to checkout code if needed
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup azd
uses: Azure/setup-azd@v2
# Use Azure CLI authentication with azd commands so credentials are shared between azd commands and az (Azure CLI) commands used in hooks.
- name: Configure azd to use Azure CLI Authentication
run: |
azd config set auth.useAzCliAuth "true"
# Login to the Azure CLI with OpenID Connect (OIDC) using federated identity credentials.
- name: Azure CLI Login
uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID || vars.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID || vars.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID || vars.AZURE_SUBSCRIPTION_ID }}
# Clean up using the --purge flag to ensure all resources are permanently deleted, like API Management.
- name: Cleanup Resources
run: |
azd down --purge --force --no-prompt
env:
# Pass the deployed resource identifiers as environment variables so azd hooks can access them
# during cleanup operations (e.g., for custom resource deletion or additional cleanup tasks).
AZURE_ENV_ID: ${{ needs.deploy.outputs.AZURE_ENV_ID }}
<#
Exports specified Azure Developer CLI (azd) environment variables as GitHub Actions output.
Usage samples:
.\export-azd-env-variables.ps1 -VariableNames "AZURE_RESOURCE_GROUP"
.\export-azd-env-variables.ps1 -VariableNames @("AZURE_RESOURCE_GROUP", "AZURE_LOCATION")
#>
param(
[Parameter(Mandatory = $false)]
[string[]]$VariableNames = @()
)
# Export specific variables
foreach ($varName in $VariableNames) {
$value = azd env get-value $varName
if ($value) {
Write-Host "Exporting $varName"
"$varName=$value" >> $env:GITHUB_OUTPUT
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment