Terraform's create_before_destroy lifecycle meta-argument can cause dependency cycle errors when removing resources or updating dependencies. This occurs because create_before_destroy propagates to all dependent resources, creating circular dependencies during destruction.
This error occurs when Terraform detects a circular dependency graph while attempting to apply or destroy resources that use the `create_before_destroy` lifecycle meta-argument. The `create_before_destroy` lifecycle rule instructs Terraform to create a replacement resource before destroying the old one. This is useful for preventing downtime during resource updates. However, Terraform propagates `create_before_destroy` to all resources that depend on a resource with this setting, potentially creating cycles in the dependency graph. When you remove a resource or change its dependencies while `create_before_destroy` is enabled, Terraform cannot determine the correct order of operations because: 1. The new resource must be created before the old is destroyed 2. But dependencies may require the old resource to be destroyed first 3. This creates a circular dependency that Terraform cannot resolve.
Use Terraform's -target flag to explicitly handle the resource with create_before_destroy first, bypassing dependency analysis. This defers the destruction of dependent resources:
terraform apply -auto-approve -target='aws_launch_configuration.example' && terraform apply -auto-approveThis two-step approach:
1. First applies changes to the targeted resource
2. Then applies the full configuration with all dependencies resolved
This workaround is particularly effective for removing resources or updating their dependencies.
Review your Terraform configuration to see which resources have create_before_destroy = true. Resources that depend on a create_before_destroy resource may implicitly inherit this behavior.
Look for patterns like:
resource "aws_autoscaling_group" "example" {
launch_configuration = aws_launch_configuration.example.id
lifecycle {
create_before_destroy = true
}
}
resource "aws_launch_configuration" "example" {
# This implicitly gets create_before_destroy if ASG has it
}The ASG's create_before_destroy propagates to the launch configuration, creating potential cycles.
Evaluate whether create_before_destroy = true is truly necessary for all resources in your configuration. Only use it when:
- The resource recreation causes downtime concerns
- The resource is part of an active service
- You absolutely need zero-downtime updates
For less critical resources or during development, disable it:
resource "aws_instance" "example" {
# ... config ...
lifecycle {
create_before_destroy = false # Explicitly disable if not needed
}
}Fewer create_before_destroy settings mean fewer dependency propagation issues.
If you're relying on implicit dependencies (resource references in arguments), consider using explicit depends_on to give Terraform more control over the dependency graph:
resource "aws_security_group" "app" {
vpc_id = aws_vpc.main.id
}
resource "aws_instance" "app" {
vpc_security_group_ids = [aws_security_group.app.id]
depends_on = [aws_security_group.app]
lifecycle {
create_before_destroy = true
}
}Explicit depends_on helps Terraform better understand the dependency direction.
As a last resort, you can manually edit the Terraform state file to add create_before_destroy to affected resources:
terraform state show aws_instance.example
terraform state editIn the state file, locate the resource and add:
"create_before_destroy": trueSave the file. This tells Terraform to handle the resource correctly on the next apply.
Warning: Editing state directly is risky. Always backup your state file first:
terraform state pull > backup.tfstateIf create_before_destroy previously left deposed (old) resources in your state, refresh the state to clean them up:
terraform refreshThis re-queries your actual cloud resources and removes stale deposed state entries that may be causing cycle errors.
After refreshing, run plan again to see if the cycle error persists:
terraform planDeposed Objects: When create_before_destroy is active, Terraform marks the old resource with a "deposed" status in the state file while the new one is being created. If apply fails partway through, deposed objects remain in state and can cause cycles on the next run.
Dependency Propagation: Terraform automatically applies create_before_destroy to all resources that a create_before_destroy resource depends on. This "infection" prevents cycles by ensuring all upstream resources are also recreated before being destroyed. However, this can cascade across your entire infrastructure if not carefully managed.
Module Boundaries: When create_before_destroy is used in a module with depends_on referencing another module, the cross-module dependency combined with create_before_destroy propagation can create unexpected cycles (Issue #26166).
Workaround Persistence: The -target workaround works because it limits Terraform's dependency analysis to specific resources, breaking the cycle artificially. After using targeted applies, run a full terraform apply without -target to ensure the configuration is fully applied.
Version Notes: This issue has been present in Terraform 0.12.x, 0.13.x, and later versions. Some edge cases have been fixed in recent versions, so upgrading Terraform may resolve cycles in some scenarios.
Performance Cost: Cascading create_before_destroy across many resources has a severe performance impact because all resources are recreated together instead of just the ones that need it.
Error: Error installing helm release: cannot re-use a name that is still in use
How to fix "release name in use" error in Terraform with Helm
Error: Error creating GKE Cluster: BadRequest
BadRequest error creating GKE cluster in Terraform
Error: External program failed to produce valid JSON
External program failed to produce valid JSON
Error: Unsupported argument in child module call
How to fix "Unsupported argument in child module call" in Terraform
Error: network is unreachable
How to fix "network is unreachable" in Terraform