Security Engineering

Terraform Drift Detection + Auto-PR: The Developer-Friendly Path to AWS Security Hygiene

August 13, 2026 Postura Security Labs 6 min read

If your AWS account is managed by Terraform, drift is the quiet failure mode: someone opens the console during an incident, loosens a security group to unblock themselves, and forgets to reflect it back into the .tf files. terraform plan stays quiet because nobody re-ran it against the live state, and the exposure sits there until an audit or a scanner catches it. Drift isn't just an IaC hygiene complaint — a fair share of the security misconfigurations GuardDuty and Security Hub flag are drift: live state that no longer matches what the repo says should be running.

Why manual drift remediation doesn't stick

The obvious fix for drift is "someone notices it and runs terraform apply to reconcile." In practice that rarely happens promptly, for the same reason manual GuardDuty remediation stalls: fixing drift means understanding what changed, why, and whether reverting it manually breaks whatever the console change was actually for. And fixing it out-of-band — patching the live AWS resource directly instead of the Terraform source — just creates new drift in the opposite direction, so the next plan proposes silently reverting the very fix that just landed.

How the drift check actually works

PosturaNet keeps a cached, hashed snapshot of each tracked resource's state. A background reconciliation job periodically re-reads the live resource from AWS, hashes it, and compares that hash against the cached one:

# simplified from the actual drift-sync worker
live = await client.read_resource_state(resource_id, resource_type)
live_hash = sha256_hash(live)
if live_hash != cached.state_hash:
alert_ciso("Drift detected", resource_id, cached_hash, live_hash)

A hash mismatch means the resource's actual configuration diverged from the last known-good state — whether that's a manual console edit, an out-of-band script, or another team's IaC stepping on the same resource. That's the trigger; what happens next is the part that matters for whether the fix actually sticks.

Why the fix lands as a PR, not a live patch

For Terraform-managed resources, PosturaNet doesn't reconcile drift by writing directly to AWS. It renders the corrected resource as HCL, computes a real unified diff against the current file on the base branch (not a "before" block synthesized after the fact), and opens the PR through GitHub's git-data API — blob → tree → commit → ref — rather than the single-file content-update endpoint, so a patch touching more than one file lands as one clean commit instead of a stack of separate ones. Authentication is a GitHub App installation token scoped to exactly the repos that installation was granted, minted per request and cached for about 50 minutes — not a long-lived personal access token shared across every customer's repository.

The result: the fix goes through the same review process as every other infrastructure change on the team — a real PR, a real diff, CI runs against it, someone approves it — instead of a security tool reaching into the account and quietly changing something the next terraform plan has no record of.

Where the Z3 safety gate fits in

For remediation classes where the fix changes reachability — revoking a security-group ingress rule, for example — the same Z3 SMT gate used for direct API remediation runs before the PR is opened, not just before a live patch. The gate proves the resulting state (current rules minus the proposed revoke) is safe, not just that the diff looks reasonable — catching cases where a second, unrelated rule would still expose the same protected port even after the targeted rule is removed. If the solver can't prove the resulting state safe, the PR doesn't open; it routes to a human reviewer instead.

The developer-friendly part

None of this requires a new approval workflow, a new dashboard habit, or credentials sitting in a security tool. Access is a scoped, read-primary IAM role via STS AssumeRole. The output is a PR in the repo the team already reviews Terraform changes in. Security hygiene stops being a separate queue and becomes part of the same git history everything else already lives in — which is also why it actually gets merged, instead of sitting in a security backlog next to the GuardDuty tickets nobody gets to.

See a real drift-to-PR diff

Run the sandbox demo to see the Terraform PR PosturaNet opens for a synthetic drift finding.