Find unauthorized infrastructure changes — whether they happened in the AWS console, CLI, or another tool — and restore your environment to its intended state.
Your team manages production AWS infrastructure with Terraform in ops0. A developer made a manual change in the AWS console last week — they opened SSH port 22 to the internet on a security group "just to debug something" and forgot to revert it. You need to find this change and fix it.
Two types of drift:
| Type | What it catches | How it works |
|---|---|---|
| Discovery drift | Any change, including resources not in Terraform | Compare two cloud scan sessions |
| IaC drift | Changes to Terraform-managed resources only | Compare live state against Terraform expected state |
Discovery drift compares two scan snapshots and shows exactly what changed — field by field. It catches changes to any resource, even ones not managed by Terraform.
The comparison groups changes into three categories:
| Category | Meaning |
|---|---|
| Modified | Resources that exist in both sessions but have changed fields — your drift candidates |
| Added | Resources present in the new session but not the baseline — created outside IaC |
| Removed | Resources present in the baseline but not the new session — deleted outside IaC |
Click any Modified resource to see a field-level diff. In our scenario, you'd see:
Modified: aws_security_group "web-sg" (sg-0abc123)
ingress_rules:
old: [{ port: 443, cidr: 10.0.0.0/8 }]
new: [{ port: 443, cidr: 10.0.0.0/8 },
{ port: 22, cidr: 0.0.0.0/0 }] ← unauthorized change
When reviewing modified resources, prioritize security groups, IAM roles, and network ACLs — these are the most commonly abused resources in manual changes.
If the security group is managed by an ops0 Terraform project, run a plan to confirm the drift against your declared state.
If the security group was changed manually, the plan shows what Terraform would do to reconcile the difference:
~ aws_security_group.web
ingress: [{ port: 443 }] → [{ port: 443 }, { port: 22, cidr: 0.0.0.0/0 }]
This confirms the drift. Now you can choose how to fix it.
Your existing Terraform doesn't include the SSH rule. Run Apply to restore the correct state.
If the change was intentional and you want to keep it, update your Terraform code instead.
If you're unsure whether a change was intentional, treat it as unauthorized (Option A). It's safer to revert and re-apply intentional changes through IaC than to accept a potentially malicious modification.
Add an automatic scan so drift is caught quickly rather than discovered manually.
Use an OPA policy to prevent this type of change from reaching production through ops0:
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_security_group_rule"
resource.change.after.from_port == 22
resource.change.after.cidr_blocks[_] == "0.0.0.0/0"
msg := "SSH access from 0.0.0.0/0 is not allowed"
}
Add this policy under Policies → New Policy and attach it to your production IaC project. Plans that introduce this rule will be blocked automatically.
After remediation, confirm everything is resolved:
last_modified timestamps, auto-rotating tags). Filter by resource type to focus on meaningful changes, and use the field filter to exclude known noisy attributes.lifecycle { ignore_changes = [...] } set in your Terraform code. Check the resource block — ignored attributes are excluded from apply even when they drift.