Back to Terraform GitOps Pipeline

Technical notes · Platform engineering and automation

Terraform GitOps Pipeline: delivery controls

Jovan Hernandez ·

The question I wanted my Terraform GitOps project to answer was simple: can the pipeline show exactly what was reviewed, and apply that same change?

The repository is a reference implementation with Jenkins, GitHub Actions and a hardened EC2 module. It has offline infrastructure tests. The stack has not been applied to a live AWS account, so the code demonstrates the control model rather than a production deployment history.

Make the change reviewable

The Jenkins pipeline writes a binary plan, renders it as text and JSON, and produces a summary that puts deletions and replacements first. A pull request gets that summary, with the full plan available as a build artifact.

That makes review concrete. A reviewer can see the resources that will change instead of trying to infer the result from a Terraform diff alone. A successful validation stage is useful, but it does not answer whether the proposed change is the intended one.

Apply the saved plan

The apply stage consumes the saved tfplan. It does not create a fresh plan after approval. Only the configured trunk branch can reach apply, and production requires a recorded approval from the designated approver group.

Development has a narrower gate: changes that delete or replace resources also need approval. A plan-only run never applies. These are explicit conditions in the pipeline, so the behavior can be inspected alongside the infrastructure code.

Separate the ability to plan from the ability to apply

Plan and apply use different Jenkins credentials. The apply credential is bound inside the apply stage. GitHub Actions runs offline formatting, validation, tests and scanning without AWS credentials.

The pipeline also holds a per-environment Jenkins lock from planning through application. The S3 state backend uses its native lockfile. Those controls address different scopes: pipeline sequencing and state access. They do not remove the need to control other writers to the environment.

Treat drift as something to inspect

A scheduled plan-only path uses Terraform’s detailed exit code to flag differences. It reports them without applying a repair automatically. That keeps a detection signal separate from a decision to change infrastructure.

The module tests use a mocked AWS provider to check configuration such as encrypted volumes, IMDSv2 and Session Manager access. They help catch mistakes before credentials are involved. They do not prove that a real account has the required permissions, networking or service capacity.

This is the kind of automation I want to build: a short path from proposed change to visible evidence, with the decisions and limits clear enough that someone else can operate it.

Back to the project