Skip to content

Learn · Replacement

ForceNew, and what carries it

Which attributes destroy a resource has nothing to do with how important they look. It is a property of which API calls the service happens to expose.

It is not about importance

The instinct is that big attributes replace and small ones do not. A database's size sounds expensive; a description sounds free. Both instincts are wrong, and in the same provider.

aws_iam_policy is the clearest case. The policy argument is the entire permission document — you can rewrite every statement in it, grant s3:* on every bucket, and it is one ~ line, because IAM creates a new policy version and sets it as default. The description argument is a sentence nobody reads, and changing it destroys the policy and detaches it from every role that referenced it, because there is no UpdatePolicyDescription API.

There is no principle to derive here. The flag records which API calls exist, and the only way to know is to read the schema — or the plan.

ForceNew is a fact about the service's API surface, not about the attribute's significance. That is why it cannot be guessed.

aws_iam_policy.descriptionForceNew in the real provider, while policy beside it is not. The same inversion, in the same resource, for the same reason.

The same word, opposite answers

An EC2 instance's instance_type changes in place: the provider stops the machine, modifies it and starts it again. An EKS node group's instance_types forces replacement, because a node group is defined by its launch template and there is no call to change one.

One is singular and the other is plural. That is the entire difference visible in the configuration, and the consequences are a reboot and a rebuild of every node in the pool.

Before you run it

Which of these edits, on a resource that already exists, does not destroy anything?

Try this
- instance_type = "t3.medium"
+ instance_type = "t3.xlarge"
- description = "Managed by Terraform" vpc_id = cloud_vpc.main.id tags = local.common_tags } resource "cloud_security_group" "web"
+ description = "Database tier only" vpc_id = cloud_vpc.main.id tags = local.common_tags } resource "cloud_security_group" "web"

Watch: Two edits, both of which look minor. One is three ~ rows and the other is a -/+.

It travels, but only along certain edges

When a resource is replaced, everything referencing it gets a new value — a new id, a new endpoint. Whether that also replaces the dependent depends on which attribute the reference lands on.

A subnet's vpc_id is ForceNew, so replacing a VPC replaces every subnet in it. A DNS record's records is not, so replacing a load balancer merely updates the record pointing at it. And depends_on carries nothing at all — it orders, and that is all it does.

This is why a one-character edit to a CIDR block can destroy three subnets and nine instances while leaving the load balancer that references the same subnets perfectly intact.

A cascade is not a property of the graph. It is a property of which attribute each edge lands on, and the same graph cascades differently depending on that.

The plan

The same service every claim above was made about. Nothing here is graded — load whatever you like, or change nothing and read what the configuration already produces.

No change loaded — the configuration as it stands.
Plan: 0 to add, 0 to change, 0 to destroy.

Nothing in this plan is destroyed.

The walk

cloud_bucket.uploadscloud_db_instance.prima…cloud_iam_policy.bucket…cloud_iam_role.applicat…cloud_vpc.maincloud_security_group.da…cloud_security_group.webcloud_subnet.private["a…cloud_subnet.private["b…cloud_subnet.private["c…cloud_instance.worker[0]cloud_instance.worker[1]cloud_instance.worker[2]cloud_lb.publiccloud_dns_record.api

Left to right is the order. Everything in one column could run at the same time — which is where the parallelism comes from, and why removing a `depends_on` makes an apply faster.

Answer the question above first.Read a plan
Everything you do here stays in this browser.Part of liter8.sh · v0.1.0