Set up OIDC for tofu-plan / tofu-apply¶
tofu-plan and
tofu-apply authenticate to AWS via
OIDC — no access keys, ever. Read
Explanation: OpenTofu state and caching
for the why; this page is the setup checklist.
1. Provision the OIDC trust on the AWS side¶
You need an IAM OIDC identity provider trusting gitlab.com, and an IAM
role whose trust policy accepts your project's OIDC subject. This is
infrastructure-side setup outside these components — do it once per AWS
account (or reuse terraform-aws-bootstrap's automation-role module if
you already have it).
The audience your GitLab pipeline presents (aud) must match the trust
policy's aud condition and the OIDC provider's client_id_list. The
components default aud to "sts.amazonaws.com" — leave it unless your
account pinned something else.
2. Fix the consumer-side gotcha: no static profile¶
This is the single most common way OIDC auth silently fails. If your
aws provider block hardcodes a profile, it short-circuits the
web-identity credential chain before OIDC gets a chance — the provider
looks for ~/.aws/credentials on a runner that has none, and the job
fails with an opaque "no valid credential sources" error. Use a variable
that's null in CI:
variable "aws_profile" {
description = "Local AWS CLI profile. Leave null in CI — OIDC is used instead."
type = string
default = null
}
provider "aws" {
region = var.region
profile = var.aws_profile # null in CI
}
3. Wire tofu-plan¶
include:
- component: gitlab.com/phpboyscout/cicd/[email protected]
inputs:
role_arn: "arn:aws:iam::<account-id>:role/phpboyscout-automation"
working_directory: "src/my-stack"
tofu-plan runs on MR pipelines (to review the plan) and the default
branch (to bank a plan artifact) — no further gating needed for the common
case.
4. Choose how tofu-apply retrieves the plan¶
Two options, and they're not independent choices — see Explanation: OpenTofu state and caching for why.
Same-pipeline (job mode, the default) — plan and apply run in the
same pipeline, normally the default branch:
include:
- component: gitlab.com/phpboyscout/cicd/[email protected]
inputs:
role_arn: "arn:aws:iam::<account-id>:role/phpboyscout-automation"
working_directory: "src/my-stack"
apply_when: manual # click "apply" in the UI after reviewing the plan
Cross-pipeline (ref mode) — for a tag-gated release flow, where a
main push banked the plan and a release tag applies it:
include:
- component: gitlab.com/phpboyscout/cicd/[email protected]
inputs:
role_arn: "arn:aws:iam::<account-id>:role/phpboyscout-automation"
working_directory: "src/my-stack"
plan_source: ref
plan_ref: main
apply_when: on_success
ref mode has an extra requirement: your IAM role's trust policy must
accept a tag OIDC subject (ref_type:tag:ref:*), not just an MR/branch
one — tofu-apply in this mode runs from a tag pipeline.
5. On GitLab Free: override plan_token¶
If you're using ref mode on GitLab Free, the default plan_token
($CI_JOB_TOKEN) will fail — job-token auth for the jobs-artifacts API is
Premium/Ultimate only. Create a personal or group access token with
job-artifact read (fine-grained: the Job Artifact resource, Read
scope), store it as a Masked CI/CD variable, and pass it:
job mode never uses plan_token — this only matters for ref mode.
6. Verify¶
Open an MR that touches the stack. tofu-plan should run and post a
plan-summary widget on the MR. Merge to the default branch (or cut a
release tag, for ref mode); tofu-apply should either wait for manual
approval or auto-apply, depending on apply_when.
See also¶
- Reference:
tofu-plan,tofu-apply - Explanation: OpenTofu state and caching
- Explanation: the token-input convention