{"id":654,"date":"2026-10-05T09:38:52","date_gmt":"2026-10-05T06:38:52","guid":{"rendered":"https:\/\/john-nessime.com\/blog\/?p=654"},"modified":"2026-10-05T09:38:52","modified_gmt":"2026-10-05T06:38:52","slug":"terraform-drift-detection-nobody-mutes","status":"publish","type":"post","link":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/","title":{"rendered":"Terraform Drift Detection That Nobody Mutes"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The first drift alert lands in a channel someone actually reads. The second arrives next morning saying almost the same thing: an autoscaling group&#8217;s desired count moved, a tag appeared that nobody wrote, a load balancer picked up an attribute the provider set on its own. By week three the channel is muted. By month two a security group opens SSH to the world, and that message scrolls past in the same wall of text as the noise.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is the failure worth writing about. Drift itself is normal. Somebody fixes production at 2am, a managed service rotates a value, a policy engine stamps a tag. The part that breaks is the alert, and it breaks quietly, because a muted channel looks identical to a quiet one.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Switching on Terraform drift detection takes one flag. Building Terraform drift detection that engineers still read six months later means deciding what <em>not<\/em> to send: which plan mode you run, what you classify as noise, how you route by blast radius, and whether the message names who made the change. A detector firing daily with the same content is worse than none, because no detector leaves you knowingly blind while a muted one leaves you blind with a green dashboard. Every section below asks the same question differently: does this message change what a human does in the next ten minutes?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Pick the right plan mode or you will alert on your own backlog<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is the biggest source of false drift, and it catches almost everyone building their first check. A normal <code>terraform plan<\/code> compares three things: your configuration, your state, and the real infrastructure. It returns a non-zero exit code when any of them disagree, so it fires for real drift and just as loudly for a pull request that merged this morning and has not been applied. Your drift channel becomes a notification feed for your own deployment backlog, and people learn that most messages are work in progress.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Refresh-only mode narrows the question. HashiCorp documents it as a plan whose goal is only to update state and root module outputs to match changes made to remote objects outside of Terraform. That is the actual definition of drift.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform init -input=false\n\nterraform plan -refresh-only -detailed-exitcode -lock=false -input=false -no-color\n\n# exit 0  state matches the real infrastructure\n# exit 1  the check itself failed\n# exit 2  something changed outside Terraform<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Two flags there matter more than they look. <code>-detailed-exitcode<\/code> is what makes the result machine-readable at all: without it a successful plan returns zero whether or not anything changed, so your job passes forever. And <code>-lock=false<\/code> is safe here specifically because a plan never writes state. Only <code>terraform apply -refresh-only<\/code> updates the state file. A read-only detector that grabs the state lock will collide with real applies, and the loser of that race is the engineer trying to ship.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The trade-off, stated plainly: refresh-only will not tell you that merged code has not shipped, and it will not find resources created outside Terraform, because it can only refresh what is already in state. I run both modes and route them separately. Refresh-only feeds the alert. Normal plan feeds a weekly digest tied to the repository, where unapplied changes are a delivery problem rather than a security one.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Classify the noise before you route anything<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most recurring drift falls into a few families. Sorting yours takes an hour and decides most of your routing rules.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Provider normalization<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A JSON policy document comes back with keys reordered. An identifier returns in different case. An empty block reads back as null instead of an empty map. Terraform sees a difference because it compares a stored representation against an API response, and that round trip is not always lossless. Fix these in the configuration where you can, because a phantom diff that follows you into every plan also hides real diffs on the same resource.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Attributes another system legitimately owns<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The classic is autoscaling capacity. Your config says two instances, a scaling policy moved it to five during a spike, and the next plan proposes taking it back to two. Applying that kills three instances mid-spike. The drift is real; reverting it is the wrong move.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>resource \"aws_autoscaling_group\" \"web\" {\n  min_size         = 2\n  max_size         = 10\n  desired_capacity = 2\n\n  lifecycle {\n    # Owned by the scaling policy after creation.\n    # Reverting this during a spike removes live capacity.\n    ignore_changes = [desired_capacity]\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Understand what you bought. <code>ignore_changes<\/code> stops Terraform proposing an update for that attribute, and it applies only after creation, not on the first apply. It also delegates the attribute permanently. If someone sets desired capacity to zero at 3am, your check will not raise a hand. Fair trade for capacity. Terrible trade for an ingress rule.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Tags stamped by something else<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Organization tag policies, cost allocation tooling, backup schedulers and compliance agents all add tags. The AWS provider exposes both the tags you set and the effective set including defaults, and ignoring the effective set silences all tag drift at once. Convenient, and slightly dangerous if you use tags for access control, because tag-based policy conditions stop being watched. The same logic covers values the platform rotates or defaults on its own, like a managed secret version: they drift on a schedule that has nothing to do with your team, and belong in a digest.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The goal here is not a clean plan. A clean plan is a side effect. The goal is a decision per family: see it immediately, see it weekly, or never see it again.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Route by blast radius, not by count<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">&#8220;Drift detected: 12 resources changed&#8221; makes a human open a terminal before they know whether to care, and that friction is where attention goes to die. Split the output by what each resource can do if it is wrong:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Wake someone.<\/strong> Anything governing exposure or access: security groups and firewall rules, IAM policies and roles, key policies, public access blocks, bucket policies, DNS records pointing at production.<\/li>\n\n<li><strong>Review today.<\/strong> Instance sizes, scaling bounds, database parameters, retention settings, anything that costs money or changes capacity.<\/li>\n\n<li><strong>Weekly digest.<\/strong> Tags, descriptions, names, and every family you classified as platform noise.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">To split them you need structure, not text. Terraform&#8217;s machine-readable output emits a message for each resource that changed outside Terraform, carrying the resource address and the action it would take.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform plan -refresh-only -no-color -json \n  | jq -c 'select(.type == \"resource_drift\") | .change'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check the exact field names inside that object against the machine-readable output reference for your Terraform version before building matching logic on them. Keep the severity mapping in the repository next to the code it describes. A resource type moving from digest to page should be a pull request, not something changed in a monitoring UI at midnight.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Make the alert answer &#8220;who did this&#8221;<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform tells you what changed. It has no idea who changed it, and that gap is where the investigation time goes. Half the drift you find is a legitimate incident fix nobody wrote down. If the alert names the actor, the response is a two-word reply in the thread instead of twenty minutes of archaeology. On AWS the audit trail can be queried by resource:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>aws cloudtrail lookup-events \n  --lookup-attributes AttributeKey=ResourceName,AttributeValue=sg-0123456789abcdef0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">CloudTrail&#8217;s console event history only reaches back a limited window, so a weekly check may be hunting an event that has aged out; a trail delivering to S3, or CloudTrail Lake, covers the gap. Azure exposes the equivalent through Activity Logs, Google Cloud through Cloud Audit Logs. Wiring the lookup into the alert body does more for adoption than any amount of formatting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Decide the remediation path before you automate it<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">There are three answers to a drift finding, and picking the wrong one can be worse than the drift.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Revert.<\/strong> Apply the configuration and push reality back. Right when the change was unauthorized or wrong.<\/li>\n\n<li><strong>Adopt.<\/strong> Change the code to match what is running. Right when the change was correct and the code was stale, which happens more than people admit.<\/li>\n\n<li><strong>Tolerate.<\/strong> Suppress the attribute deliberately, with a comment and an owner. Right when another system owns it.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">The trap is automating option one. Auto-approving a remediation apply sounds like the natural endpoint of a drift pipeline, and it is fine right up until the drifted attribute is one that forces replacement. Then &#8220;restore the desired state&#8221; means destroying and recreating a live resource, unattended, at whatever hour the schedule runs. If you automate anyway, gate it hard: an allowlist of resource types, a check that the planned action is an in-place update rather than a replace, and a notification either way.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting a drift job that quietly stopped working<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The nastiest failure mode is not a noisy check. It is a check reporting &#8220;no drift&#8221; because it never ran the plan successfully.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Exit code 2 kills your script<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">With <code>set -e<\/code>, a non-zero exit terminates the script. Exit code 2 means drift, which is the case you care about, so a naive script dies exactly when it has something to report. Capture the code without letting the shell act on it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>set -euo pipefail\n\nterraform plan -refresh-only -detailed-exitcode -lock=false \n  -input=false -no-color &gt; plan.txt 2&gt;&amp;1 &amp;&amp; code=0 || code=$?\n\ncase \"$code\" in\n  0) echo \"no drift\" ;;\n  2) notify_drift plan.txt ;;\n  *) notify_broken plan.txt; exit 1 ;;\nesac<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note that the failure branch notifies too. Exit code 1 deserves the same urgency as drift, because a broken detector and a clean estate produce identical silence.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Credentials that rot without complaining<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Drift checks run unattended, so nobody notices when a role trust policy changes, an OIDC subject claim stops matching after a repository rename, or a long-lived key gets rotated out. Add a deadman check: if the job has not reported within its expected interval, that absence is the alert. Same reasoning as heartbeat monitoring for backups.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">A provider upgrade that drifts everything<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Bump a provider and a new attribute default can appear across every resource of a type. Overnight the check reports hundreds of changed resources and whoever is on call assumes the tool is broken, which it sort of is. Commit the dependency lock file, pin the provider version, run the drift job on the same Terraform version as your apply pipeline, and treat provider upgrades as a reviewed change rather than something a bot merges.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Refresh times that outgrow the schedule<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Refresh queries the API for every resource in state. Large workspaces get slow, then overlap their own schedule, then time out. The durable fix is splitting state along boundaries that already exist in your team structure. Terraform does offer targeting to narrow a plan, but it is documented as an exceptional-use option and prints a warning for good reason, so treat it as a stopgap rather than the design.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When to stop building this and buy it<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A scheduled job in GitHub Actions or GitLab CI, or a systemd timer on a small always-on box from a provider like Contabo or InterServer, covers a handful of workspaces perfectly well and costs almost nothing. I would not reach for a platform before that stops working. What eventually stops working is the surrounding machinery: credentials for a dozen accounts across providers, concurrency against real applies, and retaining history so you can see that the same resource has drifted repeatedly this quarter, which is a process problem no single alert reveals.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is where HCP Terraform&#8217;s health assessments, or a platform like Spacelift, Scalr or env0, starts earning its fee, since drift detection becomes a workspace setting with history and notifications attached rather than code you maintain. Two honest caveats: HCP Terraform gates health assessments behind its paid editions and requires remote or agent execution, so it is not a drop-in for local-state workflows, and any platform will happily send you the same undifferentiated wall of drift the homegrown script did. Severity tiering is a decision about your infrastructure that no vendor can make for you. Buying the runner does not buy the judgement.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Common mistakes<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Using normal plan mode for the alert.<\/strong> You notify yourself about unapplied work and teach people the channel is noise.<\/li>\n\n<li><strong>Alerting on a count instead of a resource.<\/strong> &#8220;Nine resources changed&#8221; makes someone open a terminal to find out if they should care.<\/li>\n\n<li><strong>Treating exit code 1 as a non-event.<\/strong> A detector that errors is indistinguishable from an estate with no drift.<\/li>\n\n<li><strong>Taking the state lock for a read-only check.<\/strong> Monitoring should not be able to block a deployment.<\/li>\n\n<li><strong>Auto-approving remediation across all resource types.<\/strong> One force-replacement attribute and the fix becomes the outage.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Best practices worth the effort<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Run refresh-only for drift and normal plan for unapplied changes, routed to different destinations.<\/li>\n\n<li>Keep the severity mapping in the repository, version controlled and reviewed like code.<\/li>\n\n<li>Give every suppression a comment naming the system that owns the attribute and a date to revisit.<\/li>\n\n<li>Alert on the detector failing and on the detector going quiet, not only on findings.<\/li>\n\n<li>Review recurring drift monthly. The same resource drifting repeatedly is a process gap, not an infrastructure one.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Terraform drift detection FAQ<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">What is the difference between a normal plan and refresh-only?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A normal plan compares configuration, state and reality, so it flags genuine drift and any committed change that has not been applied. Refresh-only compares state against reality only, which matches what most people mean by drift. Use refresh-only for the alert and normal plan for a delivery-focused report.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Does a refresh-only plan modify my state file?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. Plan operations do not write state. Only applying a refresh-only plan updates the state file to match the drifted remote objects, and even then it makes no changes to infrastructure. That is why a detection job can safely skip the state lock.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How often should Terraform drift detection run?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Tie the interval to how long you would tolerate not knowing. Daily for production and anything holding customer data, weekly for lower environments, less often for slow-moving workspaces where refresh is expensive. Frequency matters less than whether the message gets read.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How do I find resources created outside Terraform?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Not with a plan. Terraform refreshes only what is already in state, so a resource it has never seen is invisible. You need a cloud-side inventory such as AWS Config, Azure Resource Graph or Cloud Asset Inventory, compared against your state, and import blocks to bring the results under management. Worth knowing that driftctl, the open-source tool most often recommended here, has moved into maintenance mode.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Does ignore_changes hide drift I actually care about?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes, and that is the point of using it. It stops Terraform proposing updates for that attribute regardless of what changed it, so a deliberate suppression and a malicious change look identical afterwards. Scope it to specific attributes, never whole resources you still care about, and document the owner.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why did drift suddenly appear across hundreds of resources?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Almost always a provider or Terraform version change rather than something happening in your cloud account. A new provider release can add or reshape an attribute and every resource of that type reports a difference at once. Compare the versions used by the drift runner and the apply pipeline before investigating the infrastructure.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The one thing worth keeping<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform drift detection is not a monitoring problem, it is an attention problem. The flag that finds drift took an afternoon. Everything determining whether anyone still reads the output next quarter is judgement: refresh-only rather than normal plan, noise families sorted and suppressed deliberately, severity by blast radius rather than resource count, an actor name in the message, and an alert when the detector itself goes dark.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Judge it by one test: when a message lands in that channel, does anyone open it? If the honest answer is no, your detection is working and your alert is not.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Need help making your drift alerts worth reading?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is common work once a Terraform estate grows past one team. Things I can help with:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Auditing an existing drift job and finding the false positives that trained everyone to ignore it<\/li>\n\n<li>Building scheduled refresh-only checks in GitHub Actions, GitLab CI or a systemd timer, with correct exit code handling and a deadman alert<\/li>\n\n<li>Sorting recurring drift into families and deciding, per attribute, what to fix, adopt or suppress<\/li>\n\n<li>Building severity routing from the plan&#8217;s structured output so exposure changes page and tag changes do not<\/li>\n\n<li>Enriching alerts with CloudTrail, Activity Log or Cloud Audit Log lookups so findings arrive with an actor attached<\/li>\n\n<li>Splitting oversized state so refresh finishes inside its schedule, and judging whether a managed platform is worth it at your size<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you want a second opinion, send me a week of your drift output or the workflow file that produces it, and I will tell you what I would cut first.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.upwork.com\/freelancers\/~01f15a912ad84a6620\" target=\"_blank\" rel=\"noreferrer noopener\">Work with me on Upwork<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Switching on Terraform drift detection takes one flag. Keeping engineers reading the output takes plan-mode choices, noise classification, severity routing by blast radius, and an alert that names who made the change.<\/p>\n","protected":false},"author":1,"featured_media":1169,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[24,467,52],"tags":[428,729,932,95,480,705,122,610,91,949,844,730,88,502],"class_list":["post-654","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-monitoring","category-technical-guides","tag-alarm-fatigue","tag-alert-triage","tag-blast-radius","tag-ci-cd","tag-cloudtrail","tag-drift-detection","tag-exit-codes","tag-false-positives","tag-infrastructure-as-code","tag-refresh-only-mode","tag-state-locking","tag-suppression-rules","tag-terraform","tag-terraform-state","entry","has-media"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Terraform Drift Detection That Nobody Mutes<\/title>\n<meta name=\"description\" content=\"Most Terraform drift detection gets muted within weeks. Cut the false positives, route alerts by blast radius, and keep the signal worth reading.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Terraform Drift Detection That Nobody Mutes\" \/>\n<meta property=\"og:description\" content=\"Most Terraform drift detection gets muted within weeks. Cut the false positives, route alerts by blast radius, and keep the signal worth reading.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/\" \/>\n<meta property=\"og:site_name\" content=\"John Nessime\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/J.Nessime\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/J.Nessime\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-05T06:38:52+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/09\/Terraform-Drift-Detection-That-Engineers-Still-Read.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1536\" \/>\n\t<meta property=\"og:image:height\" content=\"1024\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"John Nessime\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"John Nessime\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/\"},\"author\":{\"name\":\"John Nessime\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"headline\":\"Terraform Drift Detection That Nobody Mutes\",\"datePublished\":\"2026-10-05T06:38:52+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/\"},\"wordCount\":2683,\"publisher\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"image\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/Terraform-Drift-Detection-That-Engineers-Still-Read.png\",\"keywords\":[\"Alarm Fatigue\",\"Alert Triage\",\"Blast Radius\",\"CI\\\/CD\",\"CloudTrail\",\"Drift Detection\",\"Exit Codes\",\"False Positives\",\"Infrastructure as Code\",\"Refresh-Only Mode\",\"State Locking\",\"Suppression Rules\",\"Terraform\",\"Terraform State\"],\"articleSection\":[\"DevOps\",\"Monitoring\",\"Technical Guides\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/\",\"name\":\"Terraform Drift Detection That Nobody Mutes\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/Terraform-Drift-Detection-That-Engineers-Still-Read.png\",\"datePublished\":\"2026-10-05T06:38:52+00:00\",\"description\":\"Most Terraform drift detection gets muted within weeks. Cut the false positives, route alerts by blast radius, and keep the signal worth reading.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/#primaryimage\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/Terraform-Drift-Detection-That-Engineers-Still-Read.png\",\"contentUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/Terraform-Drift-Detection-That-Engineers-Still-Read.png\",\"width\":1536,\"height\":1024,\"caption\":\"Terraform drift detection showing configuration versus real infrastructure, with an actionable security alert highlighting SSH exposure and reduced alert noise.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/terraform-drift-detection-nobody-mutes\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Terraform Drift Detection That Nobody Mutes\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/\",\"name\":\"John Nessime\",\"description\":\"Cloud, DevOps, Data &amp; AI \u2014 Built, Tested, Explained\",\"publisher\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\",\"name\":\"John Nessime\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"contentUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"width\":512,\"height\":512,\"caption\":\"John Nessime\"},\"logo\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\"},\"description\":\"AWS Certified Solutions Architect helping businesses build reliable cloud, data, reporting, and automation solutions. I help startups, agencies, and growing businesses replace manual processes and disconnected data with practical AWS architectures, clean data pipelines, useful dashboards, and maintainable automation.\",\"sameAs\":[\"https:\\\/\\\/john-nessime.com\\\/blog\",\"https:\\\/\\\/www.facebook.com\\\/J.Nessime\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/john-m-nessime\"],\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/author\\\/johnnessime\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Terraform Drift Detection That Nobody Mutes","description":"Most Terraform drift detection gets muted within weeks. Cut the false positives, route alerts by blast radius, and keep the signal worth reading.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/","og_locale":"en_US","og_type":"article","og_title":"Terraform Drift Detection That Nobody Mutes","og_description":"Most Terraform drift detection gets muted within weeks. Cut the false positives, route alerts by blast radius, and keep the signal worth reading.","og_url":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/","og_site_name":"John Nessime","article_publisher":"https:\/\/www.facebook.com\/J.Nessime","article_author":"https:\/\/www.facebook.com\/J.Nessime","article_published_time":"2026-10-05T06:38:52+00:00","og_image":[{"width":1536,"height":1024,"url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/09\/Terraform-Drift-Detection-That-Engineers-Still-Read.png","type":"image\/png"}],"author":"John Nessime","twitter_card":"summary_large_image","twitter_misc":{"Written by":"John Nessime","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/#article","isPartOf":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/"},"author":{"name":"John Nessime","@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"headline":"Terraform Drift Detection That Nobody Mutes","datePublished":"2026-10-05T06:38:52+00:00","mainEntityOfPage":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/"},"wordCount":2683,"publisher":{"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"image":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/#primaryimage"},"thumbnailUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/09\/Terraform-Drift-Detection-That-Engineers-Still-Read.png","keywords":["Alarm Fatigue","Alert Triage","Blast Radius","CI\/CD","CloudTrail","Drift Detection","Exit Codes","False Positives","Infrastructure as Code","Refresh-Only Mode","State Locking","Suppression Rules","Terraform","Terraform State"],"articleSection":["DevOps","Monitoring","Technical Guides"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/","url":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/","name":"Terraform Drift Detection That Nobody Mutes","isPartOf":{"@id":"https:\/\/john-nessime.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/#primaryimage"},"image":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/#primaryimage"},"thumbnailUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/09\/Terraform-Drift-Detection-That-Engineers-Still-Read.png","datePublished":"2026-10-05T06:38:52+00:00","description":"Most Terraform drift detection gets muted within weeks. Cut the false positives, route alerts by blast radius, and keep the signal worth reading.","breadcrumb":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/#primaryimage","url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/09\/Terraform-Drift-Detection-That-Engineers-Still-Read.png","contentUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/09\/Terraform-Drift-Detection-That-Engineers-Still-Read.png","width":1536,"height":1024,"caption":"Terraform drift detection showing configuration versus real infrastructure, with an actionable security alert highlighting SSH exposure and reduced alert noise."},{"@type":"BreadcrumbList","@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/terraform-drift-detection-nobody-mutes\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/john-nessime.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Terraform Drift Detection That Nobody Mutes"}]},{"@type":"WebSite","@id":"https:\/\/john-nessime.com\/blog\/#website","url":"https:\/\/john-nessime.com\/blog\/","name":"John Nessime","description":"Cloud, DevOps, Data &amp; AI \u2014 Built, Tested, Explained","publisher":{"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/john-nessime.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105","name":"John Nessime","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","contentUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","width":512,"height":512,"caption":"John Nessime"},"logo":{"@id":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png"},"description":"AWS Certified Solutions Architect helping businesses build reliable cloud, data, reporting, and automation solutions. I help startups, agencies, and growing businesses replace manual processes and disconnected data with practical AWS architectures, clean data pipelines, useful dashboards, and maintainable automation.","sameAs":["https:\/\/john-nessime.com\/blog","https:\/\/www.facebook.com\/J.Nessime","https:\/\/www.linkedin.com\/in\/john-m-nessime"],"url":"https:\/\/john-nessime.com\/blog\/author\/johnnessime\/"}]}},"_links":{"self":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/654","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/comments?post=654"}],"version-history":[{"count":1,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/654\/revisions"}],"predecessor-version":[{"id":656,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/654\/revisions\/656"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/media\/1169"}],"wp:attachment":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/media?parent=654"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/categories?post=654"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/tags?post=654"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}