About

terraform apply Succeeded but AWS Did Not Change — ElastiCache Pending Modifications and the "No changes" Trap Since Provider 6.58

The replica came up at once, but automatic failover was pushed to Sunday. I planned the same state with provider 6.57 and 6.58, and settled on what "done" should mean

Sometimes terraform apply finishes green and the setting on AWS hasn't moved. I hit this while turning on automatic failover for ElastiCache. After reading this you'll know why it happens (AWS pending modifications), why plan looks completely different depending on the AWS provider version, and the one command that tells you whether the change is really live.

ElastiCache is AWS's managed Redis (Valkey). A pending modification is a change AWS has accepted but parked: "got it, I'll apply it in the maintenance window."

What happened

We added a replica to a single-node Redis and enabled automatic failover. In terraform that's two lines.

num_cache_clusters         = 2     # 1 → 2
automatic_failover_enabled = true  # false → true

apply succeeded, and the replica started coming up right away. Automatic failover, though, was still off.

AutomaticFailover       disabled
PendingModifiedValues   { AutomaticFailoverStatus: enabled }

On ElastiCache, ModifyReplicationGroup has ApplyImmediately defaulting to false (API docs). With false, some changes don't go in right away. They sit in PendingModifiedValues until maintenance. That cluster's window was on Sunday, and we applied on Thursday. We nearly spent three days believing failover was on.

Why did the replica come up immediately?

Same apply, one change immediate, one parked. That looked odd, so I read the provider source (terraform-provider-aws main, 2026-11-13).

The reason is simple. Raising num_cache_clusters calls the IncreaseReplicaCount API on its own, and that call hard-codes ApplyImmediately: true. Everything else is batched into one ModifyReplicationGroup call, and only that call carries your apply_immediately value.

So within one resource, whether a change is immediate or parked depends on which API that attribute goes through. Remembering it per resource ("ElastiCache defers things") will mislead you.

terraform apply apply_immediately = false Apply complete! ElastiCache (real state) replicas 1 → 2: started at once AutomaticFailover: disabled PendingModifiedValues: AutomaticFailoverStatus: enabled waits for the next maintenance window provider 6.57 plan ~ failover false -> true provider 6.58+ plan No changes (still disabled) done = ask AWS, not terraform describe-replication-groups PendingModifiedValues is empty and AutomaticFailover is enabled 6.58+: flip apply_immediately plan: + apply_immediately = true apply: 1 changed ModifyReplicationGroup calls: 0

From provider 6.58, plan says "No changes"

This is the real trap.

When we hit it, plan kept showing automatic_failover_enabled = false -> true. apply had succeeded but the diff wouldn't go away, which at least told us something was off.

Provider 6.58.0 (2026-08-05) changed that. #48246: "Fix perpetual diff when changes are pending for the next maintenance window." Read now copies the values from PendingModifiedValues into state. A pending enabled is treated as already on.

To see it for myself I pulled provider 6.57.1 and 6.58.0 into OpenTofu 1.12.6. A local fake endpoint stood in for AWS and returned the pending state above, and I ran import, then plan.

6.57.1  ~ automatic_failover_enabled = false -> true   (Plan: 0 to add, 1 to change)
6.58.0  No changes. Your infrastructure matches the configuration.

AWS still says disabled, and 6.58.0 calls it clean. When the fake endpoint returned the settled state (enabled, nothing pending), both said No changes. From 6.58 on, a clean plan does not mean the change is live.

We have no record of which provider version we were on. Since plan kept the diff, I assume it was older than 6.58.

Flipping only apply_immediately does nothing (from 6.58)

We fixed it by setting apply_immediately = true and applying again. Replaying that with 6.57.1, this is the request that goes out.

Action=ModifyReplicationGroup
ApplyImmediately=true
AutomaticFailoverEnabled=true

automatic_failover_enabled still has a diff, so the value is sent again, this time applied immediately.

Do the same on 6.58.0 and plan shows one line, + apply_immediately = true, and apply ends with "1 changed". But the fake endpoint logged zero Modify calls. In the source, ModifyReplicationGroup only runs when some other attribute has a diff, and apply_immediately isn't on that list. State already thinks failover is on, so there's nothing to send.

I didn't try this against real AWS. It's from the fake endpoint and the source code. If something is already parked and you want it applied now, resending the same value with --apply-immediately from the CLI looks like the reliable route (my assumption; I haven't run it).

aws elasticache modify-replication-group --replication-group-id <id> --automatic-failover-enabled --apply-immediately

Decide "done" by asking AWS directly

Neither a successful apply nor a clean plan is the bar.

aws elasticache describe-replication-groups --replication-group-id <id> --query 'ReplicationGroups[0].{now:AutomaticFailover,pending:PendingModifiedValues}'

If pending is empty and now has the value you want, you're done. Against the fake endpoint above, this prints "now": "disabled" and "pending": {"AutomaticFailoverStatus": "enabled"}. The parked change is right there.

RDS works the same way. apply_immediately defaults to false, and deferred changes pile up in PendingModifiedValues from describe-db-instances (RDS docs). The provider's aws_db_instance still only special-cases a pending engine version. Its docs say other pending values can show up as a diff in plan. I didn't run the RDS side myself.

How to choose

  • The change has to go in now → set apply_immediately = true from the start. Note that this applies not just this change but every modification already pending (API docs). Something that needs a restart, like a node type or engine version change, may be hiding in there, so look at PendingModifiedValues first

  • The change can wait for the window → leave it false and write "goes live in Sunday's window" in the PR or work log

  • Either way, the last check is that PendingModifiedValues is empty. Even more so after upgrading the provider to 6.58 or later

If "it said done but it wasn't" keeps happening on your terraform-managed AWS, ask us at https://www.negabaro.com/contact. Setting up checks like this together is part of what we do.

How It Works

1

ElastiCache and RDS default apply_immediately to false; some changes wait in PendingModifiedValues until maintenance

2

It differs per API even within one resource: the provider always sends replica increases (IncreaseReplicaCount) as immediate

3

From provider 6.58.0, pending values land in state and plan says No changes; a clean plan is not proof

4

From 6.58, flipping only apply_immediately to true makes zero ModifyReplicationGroup calls (checked with a fake endpoint)

5

Call it done only when describe-replication-groups shows PendingModifiedValues empty

Use Cases

Turning on replicas, automatic failover or Multi-AZ for ElastiCache with terraform After upgrading the AWS provider to 6.58 or later, when you are not sure plan can be trusted apply succeeded but the plan diff will not go away, or the console shows a different value
← Three Ways to Show a Maintenance Page on AWS — ALB Rule, WAF, or DNS Switch, and Why DNS Lost