Running Terraform Safely in CI/CD Pipelines
Letting CI run terraform apply unattended is powerful and terrifying. Here's the pipeline structure, gates, and credential handling I use to do it without blowing up prod.
- #terraform
- #ci-cd
- #automation
- #pipelines
- #devops
- #gitops
Running Terraform from a laptop doesn’t scale and doesn’t audit. Eventually every serious estate moves apply into CI/CD. The trouble is that an unattended terraform apply with production credentials is one bad merge away from a very bad day. After 25 years I’ve settled on a pipeline structure that gets the automation benefits without handing the robot a loaded weapon.
Here’s how I wire it up.
Separate plan from apply, always
The single most important rule: plan and apply are different stages with different triggers.
planruns on every pull request. It’s read-only and safe. Its job is to show reviewers exactly what will change.applyruns only after merge to the protected branch, and only against a saved plan from that exact commit.
plan:
on: pull_request
run: terraform plan -out=tfplan
apply:
on:
push:
branches: [main]
run: terraform apply tfplan
Applying a saved plan file is critical. It guarantees CI applies exactly what was reviewed — not a fresh plan computed against whatever the world looks like at merge time.
Post the plan where humans see it
A plan buried in CI logs gets rubber-stamped. Render it into the PR as a comment so the diff is the first thing a reviewer sees:
Plan: 2 to add, 1 to change, 0 to destroy.
That “0 to destroy” line is the one I scan for every single time. A reviewer who sees the human-readable plan inline catches the dangerous changes that get lost in a wall of logs.
Credentials: short-lived, never stored
Long-lived cloud keys in CI secrets are a breach waiting to happen. Use OIDC federation so the pipeline assumes a role and gets short-lived credentials per run:
permissions:
id-token: write
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/tf-ci-apply
aws-region: us-east-1
No static keys to leak. The plan role is read-only; the apply role can write but is scoped to exactly what the estate needs. Least privilege isn’t optional for a robot with production access.
Gates that prevent disasters
A few gates I put between merge and a production apply:
- Manual approval on production environments. CI computes and presents the plan; a human clicks go.
- Policy-as-code via OPA/Conftest or Sentinel that fails the build on, say, a public S3 bucket or an untagged resource.
- Destroy guards — a job that fails automatically if the plan deletes a resource tagged
protect=true. - Concurrency locks so two pipelines never apply the same state at once. The backend lock helps, but failing fast in CI is cleaner than a mid-apply collision.
Pin everything for reproducibility
A pipeline that floats its Terraform and provider versions is non-deterministic. Pin the Terraform binary, commit your .terraform.lock.hcl, and pin module sources to tags. The plan a reviewer approved must be the plan that applies — version drift between stages quietly breaks that promise.
Where AI fits in the pipeline
I use AI in two places around the pipeline, never inside the apply itself.
First, on the PR: an assistant summarizes the rendered plan in plain English — “this adds two subnets and widens an SG ingress to 0.0.0.0/0, which you probably didn’t intend.” That extra read catches the change a tired reviewer skims past. Our Code Review tool does this as a structured pass on the diff.
Second, for pipeline authoring itself, I keep Terraform prompts for generating the OIDC config, policy rules, and destroy-guard jobs. AI is great at the YAML boilerplate.
What AI never does: approve a plan or trigger an apply. The gate stays human.
The takeaway
Safe Terraform CI/CD comes down to a few hard lines: split plan from apply, apply only saved plans, use short-lived OIDC credentials with least privilege, render plans into the PR, and gate production behind human approval and policy. Get those right and CI becomes the safest place to run Terraform — more auditable and more consistent than any laptop ever was.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.