You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Split the Terraform CI role: read-only plan credentials on pull requests, write apply credentials on main (tf-lint AVD-AWS-0345) #225
Item 2 of #180, accepted 2026-10-30 with this target posture: split, not per-resource least privilege. A read-only plan role for pull_request runs and a write apply role for pushes to main. Per-provider-resource permission diffs are not the goal.
Current state
One role, solidago-dev-github-actions-terraform (modules/iam), is assumed by both the plan job (on pull_request) and the apply job (on push to main) in .github/workflows/terraform.yml, with <service>:* on ~20 services.
modules/iam/main.tf already carries the intent in a comment ("separate roles for terraform plan (read-only) vs terraform apply (write)").
Shape
New role …-github-actions-terraform-plan: trust restricted to this repo's pull_request subject; policy = ReadOnlyAccess-class permissions plus the writes a plan needs (state object read, lock-table item get/put/delete), enumerated from the backend config.
The existing role keeps apply; its trust narrows to ref:refs/heads/main (plus workflow_dispatch if the workflow allows it).
terraform.yml: the plan job assumes the plan role, the apply job the apply role.
Bootstrap note in the README: the roles are themselves Terraform-managed, so the apply that creates the plan role runs under the existing role; order the PR so plan on the PR itself still succeeds before any trust narrows.
The trivy:ignore:AVD-AWS-0345 stays unless the finding disappears on the apply role; its comment then points at this issue as the resolution (the split is the deliberate posture; s3:* on the apply role is by design).
Acceptance
A PR plan runs under the plan role and succeeds; an apply attempted under it is denied.
The apply on merge runs under the apply role and succeeds.
aws sts get-caller-identity in each job log names the expected role.
🤖 claytonia run 20261030T202101Z-solidago-225-split-terraform-ci-role on claude-runner-5 (opus), started 2026-10-30 20:21:04 UTC — ✅ finished; opened #227.
Priority raised on 2026-11-01 (lentago/.github#304). An outside review flagged the shared plan/apply role; a live check the same day found the terraform GitHub environment has no protection rules and no deployment branch policy, and the role's policy carries iam:*, s3:*, kms:*, secretsmanager:* and lambda:* on all resources. The OIDC trust is environment:terraform, so any workflow run in this repository that declares that environment, including one on a pull-request branch opened by a bullpen worker, receives the full-write role. solidago#156 is the companion (split environments so the apply environment can carry a main-only branch policy).
Decision (maintainer, 2026-11-01), also for #156: option C. Environment-scoped trust: the plan role trusts environment:terraform-plan (new environment, no rules); the apply role keeps environment:terraform, which receives a main-only deployment branch policy, set live as soon as plans are off that environment. The live setting is codified in the .github meta-repo the same session: lentago/.github#311. #226 and #227 are reworked to these subjects.
Activity
lentago-claude-runner commentedon Oct 30, 2026
🤖 claytonia run
20261030T202101Z-solidago-225-split-terraform-ci-roleon claude-runner-5 (opus), started 2026-10-30 20:21:04 UTC — ✅ finished; opened #227.cpitzi commentedon Nov 1, 2026
Priority raised on 2026-11-01 (lentago/.github#304). An outside review flagged the shared plan/apply role; a live check the same day found the
terraformGitHub environment has no protection rules and no deployment branch policy, and the role's policy carriesiam:*,s3:*,kms:*,secretsmanager:*andlambda:*on all resources. The OIDC trust isenvironment:terraform, so any workflow run in this repository that declares that environment, including one on a pull-request branch opened by a bullpen worker, receives the full-write role. solidago#156 is the companion (split environments so the apply environment can carry amain-only branch policy).cpitzi commentedon Nov 2, 2026
Decision (maintainer, 2026-11-01), also for #156: option C. Environment-scoped trust: the plan role trusts
environment:terraform-plan(new environment, no rules); the apply role keepsenvironment:terraform, which receives a main-only deployment branch policy, set live as soon as plans are off that environment. The live setting is codified in the.githubmeta-repo the same session: lentago/.github#311. #226 and #227 are reworked to these subjects.iam: read-only Terraform plan role on the terraform-plan environment (#…
ci(terraform): plan in the terraform-plan environment on the read-onl…
ci(terraform): plan in the terraform-plan environment on the read-onl…
feat(iam): read-only Terraform plan role on the terraform-plan enviro…
Merge main (step 1 landed as 4ba8e84) into #225 step 2
ci(terraform): plan in the terraform-plan environment on the read-onl…