From 5a1749f01be6a454611303e8f4f4f2550b662461 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Mon, 28 Sep 2026 02:21:34 +0200 Subject: [PATCH 1/2] docs(ci): regenerate the environments list from actual environment: bindings .github/workflows/README.md's "Set Up Environments" section listed aws-lambda-{dev,staging,prod} and azure-{dev,staging,prod} / gcp-{dev,staging,prod}, none of which match any current environment: binding: deploy-aws-lambda.yml, deploy-gcp.yml and deploy-azure.yml bind plain dev/staging/prod, rollback.yml's four jobs do too (#139), and database-migration.yml binds aws-db-/gcp-db-/ azure-db-, which the README omitted entirely. An operator following this guidance would configure required reviewers on environments nothing binds to while the credentialed jobs kept auto-creating theirs bare with no protection -- audit finding A14-007 on #141. Regenerate the list from the actual environment: expressions in .github/workflows/*.yml, note that cleanup-staging.yml and destroy-fargate-dev.yml reuse staging/dev rather than having their own, and add a note that each cloud's OIDC trust policy must also allowlist the environment: subject for every name listed or the binding alone does not let the job authenticate. Documents the correct end state once #139 and #140 also land (deploy-gcp.yml, deploy-azure.yml and rollback.yml still bind compound/missing names until those merge); the aws-lambda, aws-fargate and database-migration entries are already accurate on main today. Part of #141 (declaring the environments themselves with protection rules is a repo-settings/bootstrap change outside what this PR does; see the issue comment). Co-Authored-By: claude-flow --- .github/workflows/README.md | 39 +++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index bf781e357..3f19bdbb3 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -514,20 +514,39 @@ gh variable set API_URL -b"https://api.cudly.example.com" ### 3. Set Up Environments -GitHub Environments provide deployment protection and environment-specific secrets: +GitHub Environments provide deployment protection and environment-specific secrets. +GitHub **auto-creates** a referenced environment on first use with no protection rules and no +branch policy, so relying on that instead of provisioning the list below produces exactly the gap +this section exists to prevent (see #141). The list below is generated from the actual +`environment:` expressions each workflow binds, not from what the environment happens to be named +-- keep the two in sync when a workflow's binding changes. 1. Go to **Settings** → **Environments** -2. Create environments: - - `aws-lambda-dev`, `aws-lambda-staging`, `aws-lambda-prod` - - `aws-fargate-dev`, `aws-fargate-staging`, `aws-fargate-prod` - - `gcp-dev`, `gcp-staging`, `gcp-prod` - - `azure-dev`, `azure-staging`, `azure-prod` - - `frontend-aws-dev`, etc. +2. Create environments matching every `environment:` binding in `.github/workflows/*.yml`: + - `dev`, `staging`, `prod` -- bound by `deploy-aws-lambda.yml`, `deploy-aws-fargate`'s sibling + compute platforms use their own names (below), `deploy-gcp.yml`, `deploy-azure.yml`, and all + four `rollback.yml` jobs (rollback intentionally reuses the deploy environments rather than + having its own, see #139) + - `aws-fargate-dev`, `aws-fargate-staging`, `aws-fargate-prod` -- `deploy-aws-fargate.yml` + - `aws-db-dev`, `aws-db-staging`, `aws-db-prod` -- `database-migration.yml` (AWS) + - `gcp-db-dev`, `gcp-db-staging`, `gcp-db-prod` -- `database-migration.yml` (GCP) + - `azure-db-dev`, `azure-db-staging`, `azure-db-prod` -- `database-migration.yml` (Azure) + - `staging` -- also bound by `cleanup-staging.yml`'s destroy jobs (same `staging` environment as + above, not a separate one) + - `dev` -- also bound by `destroy-fargate-dev.yml` (same `dev` environment as above) + - `frontend-aws-dev`, etc. -- if/when frontend deploy workflows gain an `environment:` binding 3. Configure protection rules: - - **Production**: Require approvals, restrict to main branch - - **Staging**: Optional approvals - - **Dev**: No restrictions + - **Production** (`prod`, `aws-fargate-prod`, `aws-db-prod`, `gcp-db-prod`, `azure-db-prod`): + require approvals, restrict `deployment_branch_policy` to `main` + - **Staging** (`staging`, `aws-fargate-staging`, `aws-db-staging`, `gcp-db-staging`, + `azure-db-staging`): optional approvals, restrict to `main` + - **Dev** (`dev`, `aws-fargate-dev`, `aws-db-dev`, `gcp-db-dev`, `azure-db-dev`): no restrictions + + Each cloud's OIDC trust policy must also allowlist the `environment:` subject for every + name above (`terraform/environments/{aws,gcp,azure}/ci-cd-permissions/`) or the bound job's + cloud login step fails with `AssumeRoleWithWebIdentity`/`AADSTS70021` regardless of what's + configured here -- see the `github_environments` variable in each module. --- From a063e91d759be39ce33aace63ab36bbeb09a104f Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Mon, 28 Sep 2026 05:40:25 +0200 Subject: [PATCH 2/2] docs(ci): fix garbled environment list and per-cloud trust note after review Independent review caught two problems left over from #367's final shape: 1. The dev/staging/prod bullet had a garbled mid-sentence insertion ("deploy-aws-fargate's sibling compute platforms use their own names (below)") and, now that #367 rebinds rollback-aws-fargate to aws-fargate- instead of plain dev/staging/prod, the "all four rollback.yml jobs" claim was also wrong -- only three of the four do. Rewrote the bullet to name the three rollback jobs that share this group and call out that rollback-aws-fargate pairs with deploy-aws-fargate.yml's own aws-fargate- group instead. 2. The trust-allowlist note implied all three clouds gate environment names the same way via a "github_environments variable". Only Azure has one; AWS's trust list is a literal sub array inlined in role.tf, not a variable; GCP doesn't gate by environment/sub at all (ref-based only, per #141). Replaced the generic note with the three distinct mechanisms and where each one lives. Verified: markdownlint clean. Co-Authored-By: claude-flow --- .github/workflows/README.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 3f19bdbb3..05cce83d8 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -523,10 +523,11 @@ this section exists to prevent (see #141). The list below is generated from the 1. Go to **Settings** → **Environments** 2. Create environments matching every `environment:` binding in `.github/workflows/*.yml`: - - `dev`, `staging`, `prod` -- bound by `deploy-aws-lambda.yml`, `deploy-aws-fargate`'s sibling - compute platforms use their own names (below), `deploy-gcp.yml`, `deploy-azure.yml`, and all - four `rollback.yml` jobs (rollback intentionally reuses the deploy environments rather than - having its own, see #139) + - `dev`, `staging`, `prod` -- bound by `deploy-aws-lambda.yml`, `deploy-gcp.yml`, + `deploy-azure.yml`, and three of `rollback.yml`'s four jobs (`rollback-aws-lambda`, + `rollback-gcp`, `rollback-azure` -- reusing the deploy environments rather than having their + own, see #139). `deploy-aws-fargate.yml` and `rollback.yml`'s `rollback-aws-fargate` job both + use `aws-fargate-` instead (below), matching each other rather than this group. - `aws-fargate-dev`, `aws-fargate-staging`, `aws-fargate-prod` -- `deploy-aws-fargate.yml` - `aws-db-dev`, `aws-db-staging`, `aws-db-prod` -- `database-migration.yml` (AWS) - `gcp-db-dev`, `gcp-db-staging`, `gcp-db-prod` -- `database-migration.yml` (GCP) @@ -543,10 +544,19 @@ this section exists to prevent (see #141). The list below is generated from the `azure-db-staging`): optional approvals, restrict to `main` - **Dev** (`dev`, `aws-fargate-dev`, `aws-db-dev`, `gcp-db-dev`, `azure-db-dev`): no restrictions - Each cloud's OIDC trust policy must also allowlist the `environment:` subject for every - name above (`terraform/environments/{aws,gcp,azure}/ci-cd-permissions/`) or the bound job's - cloud login step fails with `AssumeRoleWithWebIdentity`/`AADSTS70021` regardless of what's - configured here -- see the `github_environments` variable in each module. + Configuring protection rules here is necessary but not sufficient on its own: each cloud's OIDC + trust must independently allowlist the `environment:` subject for every name above, and + the three clouds do this three different ways: + - **Azure** (`terraform/environments/azure/ci-cd-permissions/`): the `github_environments` + Terraform variable -- add the name there and re-apply, or `azure/login` fails with + `AADSTS70021`. + - **AWS** (`terraform/environments/aws/ci-cd-permissions/role.tf`): a literal `sub` list inlined + in the role's assume-role policy, not a variable -- add the subject there and re-apply, or + `configure-aws-credentials` fails with `AssumeRoleWithWebIdentity` denied. + - **GCP** (`terraform/environments/gcp/ci-cd-permissions/github_oidc.tf`): ref-based, not + environment-based -- its `attribute_condition` checks `repository`/`ref` only, never `sub`, so + no GCP-side change is needed for a new environment name (see #141 for why that's its own, + separate gap). ---