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:
- Azure Subscription Access
- Access to UAT and Production Azure subscriptions
- Permissions to create service principals and assign roles
- Access to Azure Container Registry (ACR)
-
Access to Azure Container Apps
-
GitHub Repository Access
- Admin or maintainer access to the repository
-
Ability to create secrets and environment variables
-
Azure Resources Created
- Azure Container Registry (ACR)
- Azure Container Apps for each service
- 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:
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
- Go to Azure Portal → Azure Active Directory → App registrations
- Click "New registration"
- Name:
github-actions-deployment(or similar) - Click "Register"
- Note the Application (client) ID and Directory (tenant) ID
- Go to "Certificates & secrets" → "New client secret"
- Create a secret, note the value (you won't see it again)
- Go to "API permissions" → "Add a permission" → "Azure Service Management" → "User Impersonation" → "Add permissions"
- 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¶
Step 1: Assign Contributor Role at Resource Group Level (Recommended)¶
- Go to Azure Portal → Resource Groups
- Select your UAT resource group
- Click "Access control (IAM)" in the left menu
- Click "+ Add" → "Add role assignment"
- Role: Select "Contributor"
- Assign access to: Select "User, group, or service principal"
- Select: Search for your service principal name (e.g.,
github-actions-deployment) - Click "Save"
- Repeat for Production resource group
Step 2: Assign AcrPull Role to Container Registry¶
- Go to Azure Portal → Container Registries
- Select your container registry
- Click "Access control (IAM)" in the left menu
- Click "+ Add" → "Add role assignment"
- Role: Select "AcrPull"
- Assign access to: Select "User, group, or service principal"
- Select: Search for your service principal name
- Click "Save"
Alternative: Assign at Subscription Level¶
If you prefer to assign roles at the subscription level:
- Go to Azure Portal → Subscriptions
- Select your subscription (UAT or Production)
- Click "Access control (IAM)"
- Click "+ Add" → "Add role assignment"
- Role: Select "Contributor"
- Assign access to: Select "User, group, or service principal"
- Select: Search for your service principal name
- Click "Save"
- 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¶
- Go to your GitHub repository
- Navigate to Actions tab
- Select "Deploy Foundry Applications" workflow
- Click "Run workflow" button
- Fill in the form:
- Branch: Select the branch (e.g.,
mainfor Production) - Environment: Select
UATorProduction - Application: Select which application to deploy (e.g.,
Notification-Service) - ref (optional): Leave empty to use latest tag, or specify a version tag (e.g.,
2025.1015.0308.577) - Click "Run workflow"
3.2 Workflow Execution¶
The workflow will:
- Validate: Check branch and environment combination
- Production deployments only allowed from
mainbranch -
UAT deployments allowed from any branch
-
Get Version:
- If
refis provided, uses that version tag -
Otherwise, finds the latest tag for the branch
-
Pull Images: Pulls the container image from ACR
-
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-Webruns a separate job that builds the frontend withpnpm web:buildand deploys to Cloudflare Workers viawrangler(no ACR image involved). SelectingAllruns 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:
- Go to Repository Settings → Environments → Production
- Enable "Required reviewers": Add team members who must approve deployments
- Enable "Wait timer": Add a delay before deployment starts
- Enable "Deployment branches": Restrict to
mainbranch only
4.2 Version Tagging¶
The deployment pipeline uses version tags created by the build pipeline:
- Production tags:
v2025.1015.0308.577(no-prsuffix) - PR/Preview tags:
v2025.1015.0308.577-pr(with-prsuffix)
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:
- Check Health: Verify the app is healthy in Azure Portal
- Check Logs: Review container logs for errors
- Test Endpoints: Verify API endpoints are responding
- 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¶
- Check Workflow Logs: Go to Actions → Workflow run → View logs
- Verify Secrets: Go to Repository Settings → Secrets and verify all required secrets exist
- Test Azure CLI Commands: Run the deployment commands manually in Azure Cloud Shell
- Check Azure Portal: Verify container apps and resource groups exist
- Validate JSON: If using
AZURE_CREDENTIALS, ensure it's valid JSON
Getting Help¶
If you encounter issues:
- Check the workflow logs in GitHub Actions
- Review Azure Portal logs for container apps
- Verify all secrets and role assignments are correct
- Consult Azure documentation for Container Apps
- 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
Related Documentation¶
- CI/CD Pipeline - Build and test pipeline documentation
- Common Tasks - Common development tasks
- Azure Container Apps Documentation