Programming

My Terraform Drift Alert Could Not Explain the Drift

A

Adil Sher

Author

Jul 22, 2026
7 min read
10 views
My Terraform Drift Alert Could Not Explain the Drift

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.

Read the original article

Share this article

Written by Adil Sher

Full stack developer building high-traffic platforms, AI services, and custom web applications. Explore my portfolio, learn about my background, or get in touch.

Related Articles

Stop Asking Users to "Have a Look": The UAT Lesson I Learned the Hard Way
Programming Aug 19

Stop Asking Users to "Have a Look": The UAT Lesson I Learned the Hard Way

I still remember the migration project that taught me this lesson. It was a healthcare system overhaul, data, workflows, everything. We spent four months rebuilding the database, testing the ETL pipeline obsessively, and felt genuinely confident about go-live. Then we sent the san...

I Learned the Hard Way: Data Migration Isn't What I Thought It Was
Programming Aug 19

I Learned the Hard Way: Data Migration Isn't What I Thought It Was

Three years ago, I was asked to help move customer data from an old CRM into a shiny new system. I thought I had this, extract some SQL, transform it, load it, done. I was confidently wrong. Six weeks later, staring at a production issue where 47 customer records had split into du...