Skip to content

Split the Terraform CI role: read-only plan credentials on pull requests, write apply credentials on main (tf-lint AVD-AWS-0345) #225

Closed

Description

@cpitzi

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.

Activity

  1. lentago-claude-runner commented on Oct 30, 2026

    @lentago-claude-runner
    Contributor

    🤖 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.

  2. cpitzi commented on Nov 1, 2026

    @cpitzi
    CollaboratorAuthor

    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).

  3. cpitzi commented on Nov 2, 2026

    @cpitzi
    CollaboratorAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew capability or improvement

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions