Terraform Plan Shows No Changes But It Should

Why Terraform Says No Changes When You Know There Are Some

Terraform troubleshooting has gotten complicated with all the conflicting advice flying around. As someone who spent three hours yesterday staring at a terraform plan shows no changes message while simultaneously watching our RDS database drift in the AWS console, I learned everything there is to know about this particular headache. Today, I will share it all with you.

Here’s the thing that actually broke my brain when I first hit this: Terraform doesn’t compare your code to what’s running in production. It compares your code to what it thinks is running — which lives in your state file. That distinction is everything.

But what is a state file? In essence, it’s a point-in-time snapshot of resources Terraform believes it created. But it’s much more than that. When you run terraform plan, Terraform reads that snapshot, reads your config, and asks: “Did anything in my notes change?” It never asks: “Is any of this still true?” That’s the gap. That’s where your missing changes are hiding.

State drift happens without a sound. Someone edits a security group rule manually in the console. A CloudFormation stack quietly updates a Lambda environment variable. An auto-scaling group resizes itself at 2 AM. Terraform’s state file doesn’t know. From its perspective, nothing changed. So terraform plan returns empty — no error, no warning, nothing.

Step 1 — Check If Your State Is Out of Sync

First, you should refresh your state and see what Terraform actually knows — at least if you want a real picture of your infrastructure. Run this:

terraform refresh

This tells Terraform to hit your providers — AWS, GCP, Azure, whatever you’re using — and update the state file to match reality. Nothing gets changed. It just looks. After running it, pull up the full state:

terraform show

Scan for anything wrong. If your RDS instance shows allocated_storage = 20 but you manually bumped it to 50 GB in the console last Tuesday, there’s your problem. State says 20. Config says 20. Terraform says no changes needed. Everyone’s happy except you.

Sometimes the drift is subtle — almost invisible. I once found that a KMS key’s rotation status had been toggled by our compliance team. It wasn’t in my Terraform code at all. State showed it as enabled. Config said nothing. Terraform technically reported it correctly, but I still had to decide whether to absorb that change into the config or ignore it forever. That’s an uncomfortable decision at 4 PM on a Friday.

If terraform show reveals drift, you’ve got three paths: update your configuration to match reality, use terraform import to re-adopt the resource cleanly, or manually edit the state file. That last option is dangerous. Don’t make my mistake — I edited a state file directly once, corrupted two resource references, and spent six hours cleaning it up. Update the config. It’s slower and safer.

Step 2 — Check Your Workspace and Backend Config

Probably should have opened with this section, honestly. The number of times I’ve debugged a “missing changes” issue only to realize I was planning against the wrong workspace — it’s embarrassing. I’m apparently a workspace-blind engineer and double-checking this first works for me while skipping it never does.

Workspaces are separate state files living inside the same configuration. You might have a dev workspace and a prod workspace — same backend, completely separate state. If your config changes only affect prod but you’re currently in dev, Terraform will correctly report no changes. It’s right. You’re just looking at the wrong state.

Check your current workspace:

terraform workspace show

List everything available:

terraform workspace list

Switch if needed:

terraform workspace select prod

Backend configuration might be the best option to investigate next, as Terraform requires an accurate pointer to the right state file. That is because a mismatch in your backend config can silently aim terraform plan at an entirely different state. I’ve watched engineers work locally with no backend configured, push to CI/CD using a remote S3 backend, then spend 45 minutes wondering why their local plans looked nothing like the pipeline output.

An S3 backend path looks something like s3://my-terraform-state/prod/terraform.tfstate. One wrong prefix and you’re planning against a ghost. Verify your backend inside backend.tf or your root terraform block. If you’re using environment variables, confirm they’re actually set before running anything:

echo $TF_BACKEND_CONFIG_BUCKET

Changed your backend config recently? Run init again — it re-initializes the backend and migrates state if necessary:

terraform init

Step 3 — Look for Lifecycle Rules and Ignored Changes

This one gets missed constantly. The lifecycle block lets you declare which attributes Terraform should flat-out ignore during planning. It’s genuinely useful when something external manages a specific field. It’s also a quiet visibility killer when left unchecked.

Here’s the exact scenario that bit me hard on a production auto-scaling group:

resource "aws_autoscaling_group" "app" {
  name                = "app-asg"
  desired_capacity    = 5
  min_size            = 2
  max_size            = 10
  
  lifecycle {
    ignore_changes = [desired_capacity]
  }
}

Someone manually scaled this group from 5 to 8 instances through the console. My code still said 5. Terraform reported no changes — because the ignore_changes rule suppressed the planned modification entirely. No error. No warning. Just silence. That’s what makes lifecycle rules so endearing to us infrastructure engineers until they aren’t.

The worst variant is ignore_changes = all. That tells Terraform to never touch the resource again after creation. Sometimes it’s necessary for messy third-party integrations. But it also means Terraform goes completely blind to that resource — forever, until someone removes the rule.

Search your whole codebase for these:

grep -r "ignore_changes" ./

Review every result. Ask whether each one is still necessary. If it is, leave a comment explaining why — your future self will appreciate it at 11 PM during an incident. If it isn’t, remove it.

Step 4 — Force a Re-Plan With Targeted Flags

So, without further ado, let’s dive in to the nuclear options — for when steps one through three found nothing useful.

While you won’t need to blow away your entire state, you will need a handful of targeted commands to force Terraform to reconsider specific resources. Start here:

terraform plan -refresh=true

The refresh flag is technically default behavior, but running it explicitly guarantees Terraform reads current infrastructure state before planning. Catches stale local cache issues more reliably than you’d expect.

Know which resource is causing trouble? Target it directly:

terraform plan -target=aws_instance.web

Narrowing the plan to one resource isolates whether the problem is widespread or contained. Faster feedback loop. Less noise to parse through.

Need Terraform to actually recreate a resource rather than update it in place? For Terraform 0.15.2 and newer, use:

terraform apply -replace=aws_instance.web

On older versions, terraform taint handles this — it marks the resource as corrupted and schedules a full destroy-and-recreate on the next apply. That was the only option before mid-2021. Use either command carefully. Review the plan output before touching apply. A replace on a stateful resource — an RDS instance, a Kafka cluster — is not a casual operation.

Working through this diagnostic flow in order has saved me more hours than I can count. Next time Terraform shows no changes but you know something is wrong: start with state drift, move to workspaces, check lifecycle rules, then reach for targeted flags. Nine times out of ten, one of those four steps will surface exactly what you’re looking for.

Jason Michael

Jason Michael

Author & Expert

Jason covers aviation technology and flight systems for FlightTechTrends. With a background in aerospace engineering and over 15 years following the aviation industry, he breaks down complex avionics, fly-by-wire systems, and emerging aircraft technology for pilots and enthusiasts. Private pilot certificate holder (ASEL) based in the Pacific Northwest.

48 Articles
View All Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

Stay in the loop

Get the latest stigcloud updates delivered to your inbox.