Skip to content

Deployment Pipeline Setup Guide

This guide provides step-by-step instructions for setting up the deployment pipeline for LBS Foundry applications, including GitHub secrets configuration and Azure role assignments.

Overview

The deployment pipeline (Deploy-Foundry-API.yml, workflow name "Deploy Foundry Applications") enables automated deployment of LBS Foundry applications. The containerized services deploy to Azure Container Apps, while the web frontend deploys to Cloudflare Workers. It supports:

  • Environments: UAT and Production
  • Applications:
  • LBS-Foundry-API
  • Ballr-WebAPI
  • Notification-Service
  • Foundry-Web (deploys to Cloudflare Workers)
  • All (deploy all services)

Prerequisites

Before setting up the deployment pipeline, ensure you have:

  1. Azure Subscription Access
  2. Access to UAT and Production Azure subscriptions
  3. Permissions to create service principals and assign roles
  4. Access to Azure Container Registry (ACR)
  5. Access to Azure Container Apps

  6. GitHub Repository Access

  7. Admin or maintainer access to the repository
  8. Ability to create secrets and environment variables

  9. Azure Resources Created

  10. Azure Container Registry (ACR)
  11. Azure Container Apps for each service
  12. Resource Groups for each environment

Step 1: Configure GitHub Secrets

GitHub secrets are stored at the repository level and environment level. Some secrets are shared across environments, while others are environment-specific.

Repository-Level Secrets

These secrets are shared across all environments and are used by the build pipeline as well.

1.1 Azure Container Registry (ACR) Secrets

Location: Repository Settings → Secrets and variables → Actions → Repository secrets

Secret Name Description How to Get
ACR_REGISTRY Full URL of your Azure Container Registry Azure Portal → Container Registries → Your ACR → Login server
ACR_USERNAME ACR admin username Azure Portal → Container Registries → Your ACR → Access keys → Admin username
ACR_PASSWORD ACR admin password Azure Portal → Container Registries → Your ACR → Access keys → Admin password (or enable admin user)

Example:

ACR_REGISTRY: myregistry.azurecr.io
ACR_USERNAME: myregistry
ACR_PASSWORD: <your-acr-password>

1.2 Azure Service Principal Credentials

A service principal is required for GitHub Actions to authenticate with Azure.

Option A: Create Service Principal via Azure Portal

  1. Go to Azure Portal → Azure Active Directory → App registrations
  2. Click "New registration"
  3. Name: github-actions-deployment (or similar)
  4. Click "Register"
  5. Note the Application (client) ID and Directory (tenant) ID
  6. Go to "Certificates & secrets" → "New client secret"
  7. Create a secret, note the value (you won't see it again)
  8. Go to "API permissions" → "Add a permission" → "Azure Service Management" → "User Impersonation" → "Add permissions"
  9. Grant admin consent if required

Option B: Create Service Principal via Azure CLI

# Login to Azure
az login

# Set your subscription
az account set --subscription <SUBSCRIPTION_ID>

# Create service principal
az ad sp create-for-rbac \
  --name "github-actions-deployment" \
  --role contributor \
  --scopes /subscriptions/<SUBSCRIPTION_ID> \
  --sdk-auth

The output will be a JSON object. You'll need to format it as a GitHub secret.

Add to GitHub Secrets:

Secret Name Description Value
AZURE_CREDENTIALS Service principal credentials (JSON) Full JSON output from az ad sp create-for-rbac

Example JSON format:

{
  "clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "clientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "subscriptionId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
  "resourceManagerEndpointUrl": "https://management.azure.com/",
  "activeDirectoryGraphResourceId": "https://graph.windows.net/",
  "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
  "galleryEndpointUrl": "https://gallery.azure.com/",
  "managementEndpointUrl": "https://management.core.windows.net/"
}

1.3 Cloudflare Secrets (Foundry-Web)

The Foundry-Web application is built with pnpm web:build and deployed to Cloudflare Workers via wrangler (UAT uses the staging Wrangler environment, Production uses production). These secrets are read by the deploy-foundry-web job.

Location: Repository Settings → Secrets and variables → Actions → Repository secrets

Secret Name Description How to Get
CLOUDFLARE_API_TOKEN API token used by Wrangler to deploy the Worker Cloudflare Dashboard → My Profile → API Tokens → Create Token (Workers deploy permissions)
CLOUDFLARE_ACCOUNT_ID Cloudflare account ID that owns the Worker Cloudflare Dashboard → Workers & Pages → Account ID

Environment-Level Secrets

These secrets are specific to each environment (UAT and Production) and stored in GitHub Environments.

Location: Repository Settings → Environments → [Environment Name] → Environment secrets

1.4 UAT Environment Secrets

Create a "UAT" environment in GitHub (if not exists): 1. Go to Repository Settings → Environments 2. Click "New environment" 3. Name: UAT 4. Click "Configure environment"

Add the following secrets:

Secret Name Description How to Get
UAT_SUBSCRIPTION_ID Azure subscription ID for UAT Azure Portal → Subscriptions → UAT subscription → Subscription ID
AZURE_RESOURCE_GROUP Resource group name for UAT Azure Portal → Resource groups → Your UAT resource group → Name
AZURE_FOUNDRY_CONTAINERAPP_NAME Container App name for Foundry API (UAT) Azure Portal → Container Apps → Your Foundry API app → Name
AZURE_BALLR_CONTAINERAPP_NAME Container App name for Ballr WebAPI (UAT) Azure Portal → Container Apps → Your Ballr WebAPI app → Name
AZURE_BALLR_RESOURCE_GROUP Resource group name for Ballr (UAT) Azure Portal → Resource groups → Your Ballr UAT resource group → Name
AZURE_NOTIFICATION_SERVICE_CONTAINERAPP_NAME Container App name for Notification Service (UAT) Azure Portal → Container Apps → Your Notification Service app → Name

1.5 Production Environment Secrets

Create a "Production" environment in GitHub (if not exists): 1. Go to Repository Settings → Environments 2. Click "New environment" 3. Name: Production 4. Click "Configure environment" 5. Important: Consider adding deployment protection rules (approvals, branches, etc.)

Add the following secrets (same as UAT, but with Production values):

Secret Name Description How to Get
PROD_SUBSCRIPTION_ID Azure subscription ID for Production Azure Portal → Subscriptions → Production subscription → Subscription ID
AZURE_RESOURCE_GROUP Resource group name for Production Azure Portal → Resource groups → Your Production resource group → Name
AZURE_FOUNDRY_CONTAINERAPP_NAME Container App name for Foundry API (Production) Azure Portal → Container Apps → Your Foundry API app → Name
AZURE_BALLR_CONTAINERAPP_NAME Container App name for Ballr WebAPI (Production) Azure Portal → Container Apps → Your Ballr WebAPI app → Name
AZURE_BALLR_RESOURCE_GROUP Resource group name for Ballr (Production) Azure Portal → Resource groups → Your Ballr Production resource group → Name
AZURE_NOTIFICATION_SERVICE_CONTAINERAPP_NAME Container App name for Notification Service (Production) Azure Portal → Container Apps → Your Notification Service app → Name

Secret Summary Table

Repository Secrets (shared): - ACR_REGISTRY - ACR_USERNAME - ACR_PASSWORD - AZURE_CREDENTIALS - CLOUDFLARE_API_TOKEN (for Foundry-Web) - CLOUDFLARE_ACCOUNT_ID (for Foundry-Web) - NUGET_PUBLISH_TOKEN (for build pipeline) - GITHUB_TOKEN (automatically provided)

UAT Environment Secrets: - UAT_SUBSCRIPTION_ID - AZURE_RESOURCE_GROUP - AZURE_FOUNDRY_CONTAINERAPP_NAME - AZURE_BALLR_CONTAINERAPP_NAME - AZURE_BALLR_RESOURCE_GROUP - AZURE_NOTIFICATION_SERVICE_CONTAINERAPP_NAME

Production Environment Secrets: - PROD_SUBSCRIPTION_ID - AZURE_RESOURCE_GROUP - AZURE_FOUNDRY_CONTAINERAPP_NAME - AZURE_BALLR_CONTAINERAPP_NAME - AZURE_BALLR_RESOURCE_GROUP - AZURE_NOTIFICATION_SERVICE_CONTAINERAPP_NAME

Step 2: Configure Azure Role Assignments

The service principal needs appropriate permissions in Azure to deploy container apps. Follow these steps for each environment.

2.1 Required Azure Roles

The service principal needs the following roles at the subscription or resource group level:

Role Scope Purpose
Contributor Subscription or Resource Group Deploy and update container apps
AcrPull Container Registry Pull container images from ACR

2.2 Assign Roles via Azure Portal

  1. Go to Azure Portal → Resource Groups
  2. Select your UAT resource group
  3. Click "Access control (IAM)" in the left menu
  4. Click "+ Add" → "Add role assignment"
  5. Role: Select "Contributor"
  6. Assign access to: Select "User, group, or service principal"
  7. Select: Search for your service principal name (e.g., github-actions-deployment)
  8. Click "Save"
  9. Repeat for Production resource group

Step 2: Assign AcrPull Role to Container Registry

  1. Go to Azure Portal → Container Registries
  2. Select your container registry
  3. Click "Access control (IAM)" in the left menu
  4. Click "+ Add" → "Add role assignment"
  5. Role: Select "AcrPull"
  6. Assign access to: Select "User, group, or service principal"
  7. Select: Search for your service principal name
  8. Click "Save"

Alternative: Assign at Subscription Level

If you prefer to assign roles at the subscription level:

  1. Go to Azure Portal → Subscriptions
  2. Select your subscription (UAT or Production)
  3. Click "Access control (IAM)"
  4. Click "+ Add" → "Add role assignment"
  5. Role: Select "Contributor"
  6. Assign access to: Select "User, group, or service principal"
  7. Select: Search for your service principal name
  8. Click "Save"
  9. Repeat for the other subscription

2.3 Assign Roles via Azure CLI

Alternatively, you can assign roles using Azure CLI:

# Set variables
SP_NAME="github-actions-deployment"
RESOURCE_GROUP="your-resource-group-name"
ACR_NAME="your-acr-name"
SUBSCRIPTION_ID="your-subscription-id"

# Get service principal object ID
SP_OBJECT_ID=$(az ad sp list --display-name "$SP_NAME" --query "[0].id" -o tsv)

# Assign Contributor role at resource group level
az role assignment create \
  --assignee "$SP_OBJECT_ID" \
  --role "Contributor" \
  --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP"

# Assign AcrPull role to container registry
az role assignment create \
  --assignee "$SP_OBJECT_ID" \
  --role "AcrPull" \
  --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.ContainerRegistry/registries/$ACR_NAME"

2.4 Verify Role Assignments

Verify that roles are assigned correctly:

# Check role assignments for resource group
az role assignment list \
  --resource-group "$RESOURCE_GROUP" \
  --assignee "$SP_OBJECT_ID" \
  --output table

# Check role assignments for ACR
az role assignment list \
  --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.ContainerRegistry/registries/$ACR_NAME" \
  --assignee "$SP_OBJECT_ID" \
  --output table

Step 3: Test the Deployment Pipeline

3.1 Manual Deployment Workflow

  1. Go to your GitHub repository
  2. Navigate to Actions tab
  3. Select "Deploy Foundry Applications" workflow
  4. Click "Run workflow" button
  5. Fill in the form:
  6. Branch: Select the branch (e.g., main for Production)
  7. Environment: Select UAT or Production
  8. Application: Select which application to deploy (e.g., Notification-Service)
  9. ref (optional): Leave empty to use latest tag, or specify a version tag (e.g., 2025.1015.0308.577)
  10. Click "Run workflow"

3.2 Workflow Execution

The workflow will:

  1. Validate: Check branch and environment combination
  2. Production deployments only allowed from main branch
  3. UAT deployments allowed from any branch

  4. Get Version:

  5. If ref is provided, uses that version tag
  6. Otherwise, finds the latest tag for the branch

  7. Pull Images: Pulls the container image from ACR

  8. Deploy: Updates the Azure Container App with the new image

Note: The Get Version, Pull Images, and ACR deploy steps apply only to the container apps (LBS-Foundry-API, Ballr-WebAPI, Notification-Service). Foundry-Web runs a separate job that builds the frontend with pnpm web:build and deploys to Cloudflare Workers via wrangler (no ACR image involved). Selecting All runs both paths.

3.3 Monitor Deployment

  • Watch the workflow execution in the Actions tab
  • Check Azure Portal → Container Apps → Your app → Revisions to see the new revision
  • Monitor logs in Azure Portal → Container Apps → Your app → Log stream

Step 4: Deployment Best Practices

4.1 Environment Protection Rules

For Production environment, consider adding protection rules:

  1. Go to Repository Settings → Environments → Production
  2. Enable "Required reviewers": Add team members who must approve deployments
  3. Enable "Wait timer": Add a delay before deployment starts
  4. Enable "Deployment branches": Restrict to main branch only

4.2 Version Tagging

The deployment pipeline uses version tags created by the build pipeline:

  • Production tags: v2025.1015.0308.577 (no -pr suffix)
  • PR/Preview tags: v2025.1015.0308.577-pr (with -pr suffix)

4.3 Deployment Strategy

  • Blue-Green Deployment: Azure Container Apps automatically creates new revisions
  • Rollback: If deployment fails, you can rollback to a previous revision in Azure Portal
  • Testing: Always deploy to UAT first, test thoroughly, then deploy to Production

4.4 Monitoring

After deployment:

  1. Check Health: Verify the app is healthy in Azure Portal
  2. Check Logs: Review container logs for errors
  3. Test Endpoints: Verify API endpoints are responding
  4. Monitor Metrics: Check performance metrics in Azure Portal

Troubleshooting

Common Issues

Issue: "Authentication failed" or "Service principal not found"

Solution: - Verify AZURE_CREDENTIALS secret is correctly formatted JSON - Ensure service principal exists in Azure AD - Check that client secret hasn't expired

Issue: "Insufficient permissions" or "Forbidden"

Solution: - Verify Contributor role is assigned at resource group or subscription level - Verify AcrPull role is assigned to the container registry - Check role assignments: az role assignment list --assignee <SP_OBJECT_ID>

Issue: "Container App not found"

Solution: - Verify AZURE_*_CONTAINERAPP_NAME secrets match actual container app names - Check that container apps exist in the specified resource group - Ensure you're using the correct subscription ID

Issue: "Image not found in ACR"

Solution: - Verify the image was built and pushed by the build pipeline - Check that the version tag exists in ACR - Ensure ACR credentials (ACR_REGISTRY, ACR_USERNAME, ACR_PASSWORD) are correct

Issue: "Production deployment blocked - not on main branch"

Solution: - This is by design - Production deployments only allowed from main branch - Deploy to UAT first, or merge your changes to main

Issue: "Tag not found" or "Version not found"

Solution: - Ensure the build pipeline has completed successfully - Check that version tags exist: git tag -l "v*" - If specifying a version manually, ensure the tag exists and is reachable from the current branch

Debugging Steps

  1. Check Workflow Logs: Go to Actions → Workflow run → View logs
  2. Verify Secrets: Go to Repository Settings → Secrets and verify all required secrets exist
  3. Test Azure CLI Commands: Run the deployment commands manually in Azure Cloud Shell
  4. Check Azure Portal: Verify container apps and resource groups exist
  5. Validate JSON: If using AZURE_CREDENTIALS, ensure it's valid JSON

Getting Help

If you encounter issues:

  1. Check the workflow logs in GitHub Actions
  2. Review Azure Portal logs for container apps
  3. Verify all secrets and role assignments are correct
  4. Consult Azure documentation for Container Apps
  5. Contact your Azure administrator for permission issues

Quick Reference

Required GitHub Secrets Checklist

Repository Secrets: - [ ] ACR_REGISTRY - [ ] ACR_USERNAME - [ ] ACR_PASSWORD - [ ] AZURE_CREDENTIALS - [ ] CLOUDFLARE_API_TOKEN (Foundry-Web) - [ ] CLOUDFLARE_ACCOUNT_ID (Foundry-Web)

UAT Environment Secrets: - [ ] UAT_SUBSCRIPTION_ID - [ ] AZURE_RESOURCE_GROUP - [ ] AZURE_FOUNDRY_CONTAINERAPP_NAME - [ ] AZURE_BALLR_CONTAINERAPP_NAME - [ ] AZURE_BALLR_RESOURCE_GROUP - [ ] AZURE_NOTIFICATION_SERVICE_CONTAINERAPP_NAME

Production Environment Secrets: - [ ] PROD_SUBSCRIPTION_ID - [ ] AZURE_RESOURCE_GROUP - [ ] AZURE_FOUNDRY_CONTAINERAPP_NAME - [ ] AZURE_BALLR_CONTAINERAPP_NAME - [ ] AZURE_BALLR_RESOURCE_GROUP - [ ] AZURE_NOTIFICATION_SERVICE_CONTAINERAPP_NAME

Required Azure Role Assignments Checklist

  • [ ] Contributor role assigned to service principal at resource group level (UAT)
  • [ ] Contributor role assigned to service principal at resource group level (Production)
  • [ ] AcrPull role assigned to service principal for Container Registry
  • [ ] Service principal has access to both UAT and Production subscriptions