Production-ready CI/CD workflows for deploying applications to Azure. Includes workflows for Static Web Apps, Azure Functions, and container deployments.
| Workflow | Purpose |
|---|---|
azure-static-web-app.yml |
Deploy React/Vue/Angular apps to Azure Static Web Apps |
azure-functions-node.yml |
Deploy Node.js/TypeScript Azure Functions |
azure-functions-python.yml |
Deploy Python Azure Functions |
pr-validation.yml |
Run linting, type checking, and tests on pull requests |
infrastructure-deploy.yml |
Deploy Bicep/ARM infrastructure templates |
Copy the workflow file you need to your repository's .github/workflows/ directory and configure the required secrets.
Deploys a frontend application (React, Vue, Angular, etc.) to Azure Static Web Apps with automatic preview environments for pull requests.
File: azure-static-web-app.yml
name: Deploy Static Web App
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened, closed]
branches: [main]
env:
APP_LOCATION: "src"
API_LOCATION: "api"
OUTPUT_LOCATION: "dist"
jobs:
build_and_deploy:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: '${{ env.APP_LOCATION }}/package-lock.json'
- name: Install dependencies
run: npm ci
working-directory: ${{ env.APP_LOCATION }}
- name: Build
run: npm run build
working-directory: ${{ env.APP_LOCATION }}
env:
VITE_API_URL: ${{ vars.API_URL }}
- name: Deploy to Azure Static Web Apps
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: "upload"
app_location: ${{ env.APP_LOCATION }}
api_location: ${{ env.API_LOCATION }}
output_location: ${{ env.OUTPUT_LOCATION }}
close_pull_request:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: ubuntu-latest
name: Close PR Preview
steps:
- name: Close Pull Request
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
action: "close"Required Secrets:
AZURE_STATIC_WEB_APPS_API_TOKEN- Deployment token from Azure portal
Optional Variables:
API_URL- API endpoint for the frontend to call
Deploys a Node.js or TypeScript Azure Functions app with staging slot deployment and swap.
File: azure-functions-node.yml
name: Deploy Azure Functions
on:
push:
branches: [main, develop]
workflow_dispatch:
env:
AZURE_FUNCTIONAPP_NAME: 'your-function-app'
AZURE_FUNCTIONAPP_PACKAGE_PATH: 'functions'
NODE_VERSION: '20.x'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/package-lock.json'
- name: Install dependencies
run: npm ci
working-directory: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}
- name: Build
run: npm run build
working-directory: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}
- name: Run tests
run: npm test
working-directory: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: functions
path: |
${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/dist
${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/host.json
${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/package.json
${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/package-lock.json
deploy-staging:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: staging
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: functions
path: ./deploy
- name: Install production dependencies
run: npm ci --production
working-directory: ./deploy
- name: Login to Azure
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy to staging slot
uses: Azure/functions-action@v1
with:
app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
package: './deploy'
slot-name: 'staging'
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- name: Login to Azure
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Swap staging to production
run: |
az functionapp deployment slot swap \
--resource-group ${{ vars.RESOURCE_GROUP }} \
--name ${{ env.AZURE_FUNCTIONAPP_NAME }} \
--slot staging \
--target-slot production
deploy-develop:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
environment: development
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: functions
path: ./deploy
- name: Install production dependencies
run: npm ci --production
working-directory: ./deploy
- name: Login to Azure
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy to dev
uses: Azure/functions-action@v1
with:
app-name: '${{ env.AZURE_FUNCTIONAPP_NAME }}-dev'
package: './deploy'Required Secrets:
AZURE_CREDENTIALS- Service principal credentials (JSON format)
Required Variables:
RESOURCE_GROUP- Azure resource group name
Runs linting, type checking, and tests on every pull request. Blocks merge if checks fail.
File: pr-validation.yml
name: PR Validation
on:
pull_request:
branches: [main, develop]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Test
run: npm test -- --coverage
- name: Build
run: npm run build
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: falseDeploys Bicep or ARM templates to Azure.
File: infrastructure-deploy.yml
name: Deploy Infrastructure
on:
push:
branches: [main]
paths:
- 'infrastructure/**'
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy'
required: true
default: 'dev'
type: choice
options:
- dev
- staging
- prod
env:
TEMPLATE_FILE: 'infrastructure/main.bicep'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to Azure
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Validate template
run: |
az deployment group validate \
--resource-group ${{ vars.RESOURCE_GROUP }} \
--template-file ${{ env.TEMPLATE_FILE }} \
--parameters infrastructure/parameters.${{ inputs.environment || 'dev' }}.json
deploy:
needs: validate
runs-on: ubuntu-latest
environment: ${{ inputs.environment || 'dev' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to Azure
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy infrastructure
run: |
az deployment group create \
--resource-group ${{ vars.RESOURCE_GROUP }} \
--template-file ${{ env.TEMPLATE_FILE }} \
--parameters infrastructure/parameters.${{ inputs.environment || 'dev' }}.jsonCreate a service principal for GitHub Actions:
az ad sp create-for-rbac \
--name "github-actions-sp" \
--role contributor \
--scopes /subscriptions/<subscription-id>/resourceGroups/<resource-group> \
--sdk-authCopy the JSON output and add it as a repository secret named AZURE_CREDENTIALS.
For Static Web Apps, get the deployment token from: Azure Portal > Static Web App > Manage deployment token
These workflows assume the following branch strategy:
main (production)
└── staging slot deployment, then swap to production
develop (development)
└── direct deployment to dev environment
feature/* (feature branches)
└── PR validation only, preview environments for Static Web Apps
Modify the branch triggers in each workflow to match your strategy.
- Azure AI Registry - Uses these workflows
- Azure React Dashboard - Uses these workflows
- Azure Document Pipeline - Uses these workflows