My Terraform Drift Alert Could Not Explain the Drift
Admin User
Author
Summary
The Stack
Step 1: Publish Structured Drift Details
Step 2: Send the Event to SQS
resource "aws_sns_topic_subscription" "sqs" { topic_arn = aws_sns_topic.drift.arn protocol = "sqs" endpoint = aws_sqs_queue.drift_audit.arn }
The Queue Existed in the Wrong Region
Step 3: Classify Every Change with Lambda
# Severity definitions HIGH_RISK_TYPES = { "aws_security_group", "aws_security_group_rule", "aws_iam_role", "aws_iam_policy", "aws_iam_role_policy", "aws_iam_user", "aws_iam_group", "aws_iam_access_key", }
MEDIUM_RISK_TYPES = { "aws_instance", "aws_db_instance", "aws_ecs_cluster", "aws_ecs_service", "aws_ecs_task_definition", "aws_lb", "aws_lb_listener", "aws_lb_target_group", "aws_nat_gateway", "aws_route_table", "aws_network_acl", }
def classify_change(resource_type, actions): """Classify a single resource change by severity.""" if resource_type in HIGH_RISK_TYPES: return "HIGH" elif resource_type in MEDIUM_RISK_TYPES: # Delete or replace is more concerning than create if "delete" in actions or "replace" in actions: return "HIGH" return "MEDIUM" else: return "LOW"
def lambda_handler(event, context): """Process SNS message with drift details and classify severity.""" print(f"Received event: {json.dumps(event)}")
<span class="k">for</span> <span class="n">record</span> <span class="ow">in</span> <span class="n">event</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">Records</span><span class="sh">"</span><span class="p">,</span> <span class="p">[]):</span>
<span class="c1"># SNS message is in the SNS record
sns_message = record.get("Sns", {}).get("Message", "{}")
<span class="k">try</span><span class="p">:</span>
<span class="n">drift_data</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="nf">loads</span><span class="p">(</span><span class="n">sns_message</span><span class="p">)</span>
<span class="k">except</span> <span class="n">json</span><span class="p">.</span><span class="n">JSONDecodeError</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Failed to parse SNS message: </span><span class="si">{</span><span class="n">sns_message</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="k">continue</span>
<span class="n">timestamp</span> <span class="o">=</span> <span class="n">drift_data</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span>
<span class="sh">"</span><span class="s">timestamp</span><span class="sh">"</span><span class="p">,</span>
<span class="n">datetime</span><span class="p">.</span><span class="nf">now</span><span class="p">(</span><span class="n">timezone</span><span class="p">.</span><span class="n">utc</span><span class="p">).</span><span class="nf">isoformat</span><span class="p">()</span>
<span class="p">)</span>
<span class="n">project</span> <span class="o">=</span> <span class="n">drift_data</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">project</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">unknown</span><span class="sh">"</span><span class="p">)</span>
<span class="n">changes</span> <span class="o">=</span> <span class="n">drift_data</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">changes</span><span class="sh">"</span><span class="p">,</span> <span class="p">[])</span>
<span class="c1"># Classify each change
classified = {"HIGH": [], "MEDIUM": [], "LOW": []}
<span class="k">for</span> <span class="n">change</span> <span class="ow">in</span> <span class="n">changes</span><span class="p">:</span>
<span class="n">address</span> <span class="o">=</span> <span class="n">change</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">address</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">unknown</span><span class="sh">"</span><span class="p">)</span>
<span class="n">resource_type</span> <span class="o">=</span> <span class="n">change</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">type</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">unknown</span><span class="sh">"</span><span class="p">)</span>
<span class="n">actions</span> <span class="o">=</span> <span class="n">change</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">actions</span><span class="sh">"</span><span class="p">,</span> <span class="p">[])</span>
<span class="n">severity</span> <span class="o">=</span> <span class="nf">classify_change</span><span class="p">(</span><span class="n">resource_type</span><span class="p">,</span> <span class="n">actions</span><span class="p">)</span>
<span class="n">classified</span><span class="p">[</span><span class="n">severity</span><span class="p">].</span><span class="nf">append</span><span class="p">({</span>
<span class="sh">"</span><span class="s">address</span><span class="sh">"</span><span class="p">:</span> <span class="n">address</span><span class="p">,</span>
<span class="sh">"</span><span class="s">type</span><span class="sh">"</span><span class="p">:</span> <span class="n">resource_type</span><span class="p">,</span>
<span class="sh">"</span><span class="s">actions</span><span class="sh">"</span><span class="p">:</span> <span class="n">actions</span><span class="p">,</span>
<span class="p">})</span>
<span class="c1"># Build summary
summary = { "timestamp": timestamp, "project": project, "total_changes": len(changes), "high_count": len(classified["HIGH"]), "medium_count": len(classified["MEDIUM"]), "low_count": len(classified["LOW"]), "classified": classified, }
<span class="c1"># Log the classified drift
print( f"Drift classification: {json.dumps(summary, indent=2)}" )
<span class="c1"># TODO Phase 3: Auto-remediate HIGH severity changes
# TODO Phase 3: Alert on MEDIUM severity changes return {"statusCode": 200, "body": "Drift classified"}
The Lambda Package Needed Another Provider
Verification
What Is Next
Source
This article discusses content originally published by at Dev.to.