Terraform plan forces RDS replacement when security group rule order changes

2026-11-13 · 2 min read

Terraform plan forces RDS replacement when security group rule order changes

A one-line reorder in a security group rule can show -/+ on aws_db_instance and turn a quick SG update into an RDS destroy/create: snapshot, downtime, a new endpoint, and an app config change. That is expensive for a change that should be an in-place security group update. A Terraform RDS forced replacement is not something to discover in the apply output.

What the plan is actually saying

Terraform marks replacement-triggering attributes with # forces replacement in the plan. For RDS, the security-group argument is usually vpc_security_group_ids or legacy security_groups [link needed]. If the reorder changes the value Terraform plans for that argument, the DB instance can be replaced instead of updated.

Rule order itself is often harmless: the AWS provider treats many security group inputs as sets, and order-only diffs may update in place. But mixed list/set inputs, legacy attributes, provider versions, or config that builds SG IDs from unsorted lists can make the diff look real [link needed].

Terraform RDS forced replacement: diagnose before applying

1. Save and inspect the plan:

```bash

terraform plan -out=tfplan

terraform show -no-color tfplan | grep -B5 -A10 "forces replacement"

```

2. Confirm the replacement is on aws_db_instance, not only on a security group rule. A rule-order diff that does not touch the DB is a different problem.

3. Separate order-only changes from membership changes. Compare sorted SG IDs against current state; if the IDs are identical and only the sequence differs, the fix is stability in config, not a DB migration.

4. Find why the DB argument changed: concat, list outputs, security_groups by name, or inline rule blocks that reorder.

5. Re-plan after the config fix. Apply only when the plan no longer shows aws_db_instance replacement, or schedule a maintenance window if replacement is unavoidable.

A useful check is to confirm that every SG ID in the diff still exists and is intended. If membership changed, replacement may be legitimate.

Stabilize the security group input

Use sets and sorted inputs so rule order cannot leak into the DB resource:


vpc_security_group_ids = toset([
  aws_security_group.db.id,
  aws_security_group.app.id,
])

If you need a computed list, sort it before assigning. Prefer standalone aws_vpc_security_group_ingress_rule resources over inline ingress blocks when rules churn [link needed]. Keep the RDS attachment argument stable even if rule resources change.

To stop a Terraform RDS forced replacement, fix the input shape first. Do not immediately add lifecycle { ignore_changes = [vpc_security_group_ids] }; that hides real membership changes. Also check whether identifier or another replacement-triggering attribute is in the same diff.

When NOT to do this

Do not refactor security group management just because a plan showed a reorder if the plan already says ~ update in place. Do not use create_before_destroy blindly on RDS if the identifier collides; confirm the provider behavior for your version [link needed]. Do not defer a legitimate replacement caused by SG membership drift just to avoid a maintenance window.

Checklist

ad slot — paste the network tag here