This gist contains a sample GitHub Actions workflow for Azure Developer CLI (azd) templates.
Files:
- azure-dev.yml: The GitHub Actions workflow
- export-azd-env-variables.ps1: PowerShell script to export selected azd environment values into job outputs
This gist contains a sample GitHub Actions workflow for Azure Developer CLI (azd) templates.
Files:
| 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 | |
| } | |
| } |