Examples in this article are illustrative, not customer case studies. Product availability and coverage are described on the linked capability pages.
A Terraform pull request merges, the finding flips to fixed, and weeks later someone checks the bucket directly and finds it still unencrypted. This post explains how to handle IaC drift by linking each infrastructure-as-code finding to the cloud resource it produced, so findings close on evidence from the account rather than on a merge event.
IaC drift is any difference between what infrastructure as code declares and what the cloud account or cluster actually runs. It comes from unapplied merges, console fixes never ported back, provider defaults that move and other teams’ automation. Closing an IaC finding therefore needs evidence from the live resource, not a merged pull request.
Our worked example is a nightly ETL job. Its Terraform module creates a storage bucket for raw extracts and an IAM role the job assumes to read from a production database. A scan flags the bucket for missing encryption at rest, the team merges a fix, and the finding is marked resolved. Whether the bucket is actually encrypted is a separate question, and it is the only one an attacker cares about.
A merged IaC fix is not a deployed fix
Merging a Terraform change does nothing to the account on its own. A plan has to be run and applied, by a pipeline, a scheduled job or a person at a terminal. Between merge and apply, a module can sit unapplied, reach one environment but not another, or be applied with variables that change its behaviour in ways the diff never showed, such as a for_each over a different set of inputs or a resource that only appears under a flag.
The ETL team’s fix applies cleanly in staging. The production workspace uses a separate pipeline that was paused during a database migration and has not run since. The finding says fixed, and the production bucket, which holds the real extracts, is exactly as it was.
Kubernetes has the same gap in a different form. A committed manifest is not running until Argo CD, Flux or a CI job applies it, and a cluster can happily run a manifest from several commits ago if a sync failed quietly. In both worlds, “the pull request merged” and “the fix is live” are different claims, and conflating them is how IaC programmes lose credibility.
Linking an IaC finding to its resource needs a chain of identifiers
Connecting a line of code to the resource it became means tracking a chain that IaC tools rarely expose to each other. That chain runs from the module or manifest that declared the resource, through the plan or render that computed its final configuration and the pipeline run that applied it, to the account it landed in and the resource’s own identifier: an ARN, a cloud resource ID or a Kubernetes UID.
Terraform state is the closest thing to an authoritative map, because it records which address in code corresponds to which real identifier. It has limits, though. State is per workspace, often stored in different backends by different teams, and it records only what Terraform believed it set at the last apply, not what the resource looks like now.
For Kubernetes, the link runs through labels, owner references and the GitOps controller’s own record of which commit produced which live object. That record is usually more reliable than reconstructing the mapping from manifests. Once the chain exists, a finding stops being a statement about a file and becomes a statement about a specific object in a specific account.
Code and cloud disagree in four recognisable ways
When you can compare declared configuration with live configuration, disagreements turn out to be common. They fall into four patterns, and each needs a different response.
Manual remediation never ported back. During an incident, an engineer tightens the ETL role’s permissions in the console. The resource is now correct, but the module still declares the old policy, and the next apply for an unrelated change quietly restores it. The fix is to port the change into code so it survives.
Partial rollout. This is the paused production pipeline from our example. The finding is closed in one environment and open in another, so any single “is it fixed” answer is wrong somewhere. The fix is the pipeline, not a re-triage of the finding.
Defaults that move underneath you. A provider changes the default for a setting the module never set explicitly, and the live configuration shifts without a line of code changing. The fix is to declare the value explicitly rather than inherit it.
Someone else’s automation. A cost-optimisation tool, an auto-remediation service or another team’s pipeline modifies a resource in the same account. The fix is an explicit declaration in code plus a clear conversation about who owns what in that account.
Closing IaC findings needs evidence from the account
The discipline is simple to state and easy to skip under deadline pressure. An IaC finding is closed when the resource it describes, checked directly in the account, no longer has the problem. Closing evidence therefore comes from the cloud side: a posture scan, a provider API call or a control-plane query, not a commit hash.
Finding status should also say what is actually known. “Merged, not yet applied” and “applied and confirmed in the account” are distinct states, and a dashboard that collapses them into “fixed” manufactures false confidence. The same thinking applies to audit preparation, where continuous evidence beats screenshots for exactly this reason.
Reconciliation also has to run continuously, because drift is not a one-off event. Once IaC findings are linked to live resources, they can be combined with workload and identity data to show which misconfigurations sit on a real route to sensitive data, as described in cloud attack paths. Teams that treat “merged” as “fixed” tend to learn otherwise during an incident, when someone finally checks the resource.
Key takeaways
- Record the resource identifier for every IaC finding, from state files or your GitOps controller, so each finding points at a real object.
- Track “merged” and “applied and confirmed” as separate finding states in every environment.
- Close IaC findings only on evidence from the live account, never on a merge event alone.
- Port every console fix back into code before the next unrelated apply reverts it.
How SecurityVault approaches this
SecurityVault combines IaC findings with collected cloud posture so teams can compare declared and observed configuration where identifying context is available. A clean source scan and a successful live-resource re-check answer different questions. Record both before calling a production remediation complete; a merged pull request alone is not evidence that the deployment changed.
Choose a resource with a documented link to its source configuration and test the source change separately from the deployment. Review the repository result before apply, the pipeline record after apply and the fresh posture result from the target account. Include a second environment that has not yet received the change. The review should preserve that distinction. This gives the application and infrastructure owners a common definition of completion and avoids presenting a source-level improvement as proof that every live environment has been remediated.
Explore IaC security or talk to our team about an evaluation with your engineering, security and compliance stakeholders.