Hey folks :wave: I'm working on an interesting pr...
# terraform
g
Hey folks 👋 I'm working on an interesting problem and I feel like someone out there must have run into this before: Problem/Background • I'm currently assuming an aws role to access an s3 bucket for backend state as follows:
Copy code
terraform {
  backend "s3" {
    ...
    assume_role = {
      role_arn = var.backend_assume_role_arn
    }
    ...
  }
}
• This role has permissions to read and write to the s3 bucket, and I'd like to scope down permissions so that I use a read-only role during
init
and
plan
phases, and switch to a read-write role for the
apply
phase. Where I'm stuck • I can swap in the read-write role arn before running the
apply
phase, but the problem I run into is that since the backend config changes, a
init -reconfigure
is required here. • If I run
tofu init -reconfigure
before the
apply
, this leads to
Error: Inconsistent dependency lock file
• I've also attempted
tofu init -reconfigure -lockfile=readonly
, but that leads to
Error: Provider dependency changes detected
• At this point, my only option is to run another
tofu plan
, which is out of the question. An alternative • I can remove the
assume_role
out of the
backend
block entirely. • This allows me to assume a read-only role to begin with (in my terminal, pipeline, etc), and I can
init
and
plan
with it • Then I can assume a read-write role before running the
apply
• This does work, but I'm reaching out for other ideas here. Ideally, I don't want to have to switch role in my terminal or pipeline. The pattern I'm trying to maintain is a single AWS role that can assume the read or write roles to the s3 bucket backend during
init/plan
vs
apply
.
e
Yes, this is a problem we've dealt it
It's actually one of the many reasons we are moving away from role-assumption inside of terraform
On top of that, the roles might be different for developers than in CI, and the auth providers as well (SSO vs OIDC)
You'll need to move auth outside of terraform.
Good news though, that's the problem we have solved: https://atmos.tools/changelog/introducing-atmos-auth
This is natively solved by https://atmos.tools
And can also be combined with https://atmos.tools/changelog/atmos-profiles/
y
Hello @Erik Osterman (Cloud Posse), Could you be more explicit? Because I'm not quite sure how to use 2 identities at the same time (one for s3, one for the plan/apply). I missed something.
e
Use the new required option. I will share a link when I am back to my desk.
g
thanks for the quick response @Erik Osterman (Cloud Posse)! I'll catch up on these links you've posted
e
What this will do is ensure that those identities are authenticated
Because all cloud SDKs only provide a single identity from a environment variable perspective, You'll need to rely on defining a statically coded profile name in your provider's configuration.
The profile name is the same name as the identity.
Let me know if that's not clear, I'll try and rephrase it.
a
Sorry to necro this, but I think that the forced reconfigure is happening the assume role parameter in the backend configuration is baked into the plan, so when it changes Terraform/OpenTofu forces a reinit. I'd love to learn why locking down the remote state backend configuration is a focus of security engineering energy. To me it seems like scoping the AWS
provider
blocks to read-only on plan, read-write on apply is higher leverage for strengthening security posture. But I'm open to other perspectives. 🙂
Also the other thing that sticks out to me:
The pattern I'm trying to maintain is a single AWS role that can assume the read or write roles to the s3 bucket backend during
init/plan
vs
apply
.
If a compromised plan job is in an environment where it has access to this single AWS role that can assume the read-only role and the read-write role anyway, then that pattern isn't guarding the state file from being altered during the plan phase anyway: if someone has RCE in the init-plan job, they could just assume the read-write role you didn't want to be able to access the remote state bucket.
e
This really comes down to configuration, and you’re right that if misconfigured, what you’re describing would absolutely be possible. However, when configured correctly, the plan job fundamentally cannot obtain credentials for the write role because the enforcement happens at the IAM trust policy level using GitHub’s OIDC token claims, not in Terraform. When GitHub issues an OIDC token, it includes a
sub
claim that encodes the repository and the GitHub Environment the workflow is running in. The write role’s trust policy is scoped to only allow tokens originating from a specific protected environment (e.g.
prod
). Since GitHub Environments can be restricted to the default branch and gated by approvals, only workflows running in that environment can ever produce a token that satisfies the trust policy. Plan jobs (like PR workflows) run outside that environment, so even with full RCE they cannot mint a token that IAM will accept for the write role. Apply jobs, on the other hand, run inside the protected environment and can. So the boundary is enforced by IAM + OIDC + GitHub Environment protections, not by Terraform configuration. Thus, this isn’t security theater—it’s actually a pretty elegant way to enforce least privilege in GitHub-native workflows.
a
Ah, I see. 🙂 That makes sense and that's a cool pattern, I appreciate you explaining it in more detail to me. In our environment we use AWS CodeBuild for all of our Terraform, so there's just the static execution role we're running with. I like that idea of having different roles you can assume based on the different sets of claims in the OIDC federation. That is elegant. 🌷