Learn · Replacement
create_before_destroy, and the trap it sets
The one meta-argument here that is straightforwardly a good idea, and the single way it fails — which is the way you will hit.
Reverse the pair
A replacement is a destroy and a create, in that order. Between them there is a window where the thing does not exist, and everything depending on it is pointing at nothing. That window is the whole cost of a replacement, over and above the rebuild.
create_before_destroy reverses it: build the new object, move the references, then throw the old one away. Nothing is ever absent, so nothing is disrupted. The plan says +/- instead of -/+, which is the only visible difference and is easy to miss.
- name = "orders-web"+ name = "orders-frontend"- resource "cloud_security_group" "web" { name = "orders-frontend" description = "Managed by Terraform" vpc_id = cloud_vpc.main.id tags = local.common_tags }+ resource "cloud_security_group" "web" { name = "orders-frontend" description = "Managed by Terraform" vpc_id = cloud_vpc.main.id tags = local.common_tags lifecycle { create_before_destroy = true } }
Watch: The glyph on the security group block: +/- rather than -/+.
The trap
It fails when the resource has a *unique name*. A security group name is unique within its VPC, a load balancer name is unique within a region, an S3 bucket name is unique across every account in the partition. You cannot create the new one while the old one still holds the name.
So the apply gets half way: the create fails, the destroy has not happened, and you are left with the old object still in place and a plan that will not converge. It is not a disaster — nothing was lost — but it is confusing, and the usual reaction is to remove the flag and take the outage instead.
The fix is name_prefix rather than name: let the provider generate a unique suffix, and the two objects can coexist for the second they need to.
create_before_destroy plus a hard-coded unique name is a plan that looks safer and fails halfway. The two have to be changed together.It has to spread
The flag has to propagate to everything downstream that is also being replaced. If a dependent is not also create_before_destroy, its change would have to happen both after the new object exists and before the old one goes — which has no solution, and the planner reports it as a cycle.
That error is genuinely confusing the first time, because there is no cycle in the configuration. The cycle is in the ordering constraints, and the fix is to add the flag to the dependents rather than to remove it from the original.
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.
Plan: 0 to add, 0 to change, 0 to destroy.
Nothing in this plan is destroyed.
The walk
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.