Skip to content

Deployment

A push to main on ba-api or ba-document-builder-service runs that repository’s .github/workflows/deploy.yml (it can also be started by hand with Run workflow). The job runs in the GitHub environment ba-staging and:

  1. Signs in to Azure with OIDC (azure/login), then az acr login.
  2. Builds the image and pushes it to acrbastaging as sha-<short commit> and main-latest.
  3. Runs az containerapp update --revision-suffix sha-<short commit> on the app, so the revision name says which commit it runs.
  4. Posts the result to Microsoft Teams. ba-api also runs a Snyk image scan that reports but does not block.

The API runs its database migrations at boot (RUN_MIGRATIONS_ON_START=1). Watch the first minute of the new revision’s console log after a deploy that includes a migration.

To roll back, rerun the workflow on the last good commit, or point the app at the previous tag:

Terminal window
az containerapp update -n ca-ba-staging-api -g rg-ba-staging \
--subscription ba-staging \
--image acrbastaging.azurecr.io/ba-api:sha-<previous> \
--revision-suffix sha-<previous>-rb

Cloudflare Pages builds ba-client from main (production) and from every pull request (preview). There is no GitHub workflow for the deploy; pr-cloudflare-notification.yml only waits for the Pages build and records a GitHub deployment. Environment variables for the build are in wrangler.toml (env.production and env.preview). Previews use the mock-header login against the staging API, which the API refuses in production mode, so the preview’s “Use real authentication” box is the way in.

Every change to ba-infrastructure goes through a pull request:

  1. CI runs lint, validate, Checkov, Trivy and terragrunt run-all plan, and posts a per-layer plan table as a PR comment.
  2. A reviewer approves. The main ruleset requires one review, resolved threads and a squash merge. The PR title is the commit message, in Conventional Commits form (feat(compute): BAM-4 …).
  3. On merge, the apply workflow runs in the infrastructure-production environment.

Needed when a change must be applied before it is merged, or when an import or a hostname bind cannot run in CI. From the repository root, signed in with az login as yourself:

Terminal window
export ARM_USE_OIDC=false ARM_USE_CLI=true
export ARM_TENANT_ID="$(az account show --query tenantId -o tsv)"
export TF_VAR_azure_subscription_id="$(az account show \
--subscription ba-staging --query id -o tsv)"
source <(scripts/fetch-kv-secrets.sh kv-ba-staging-tf)
# The security layer reads the dek-master key and the database layer
# reads mongo-url, so a run-all needs both vault roles active.
scripts/kv-access.sh kv-ba-staging-app --roles both \
--reason "BAM-<n>: apply <what>"
# Only if the aws layer has changes.
aws sso login --profile sii-admin
cd environments/staging
terragrunt run-all init # once per clone; CI does the same
terragrunt run-all apply --terragrunt-exclude-dir aws

Three things that go wrong without the lines above:

  • “state file is encrypted and can not be read”: run-all init was skipped.
  • 403 ForbiddenByRbac on dek-master or mongo-url: the PIM roles are not active.
  • Cloudflare “Authentication error (10000)”: TF_VAR_cloudflare_api_token is empty or stale; re-source the vault script.

Anything already in the Cloudflare zone is an import, not a create. The old record must be imported with terragrunt import before the plan is applied, or the apply fails on it.