Terraform detected a circular dependency in your configuration where resources depend on each other in a way that creates an unresolvable loop. This breaks Terraform's directed acyclic graph requirement for determining resource creation order.
This error occurs when Terraform detects that two or more resources are dependent on each other, forming a cycle. Terraform needs to determine a valid order to create resources (a directed acyclic graph or DAG), but circular dependencies prevent this. This happens when resource A references resource B's attributes, and resource B references resource A's attributes, creating a loop Terraform cannot resolve.
Use the terraform graph command to see the dependency relationships. For detailed cycle detection, use:
terraform graph -draw-cycles | dot -Tsvg > graph.svgThis creates a visual representation where cycles are highlighted. You'll need Graphviz installed (apt-get install graphviz on Linux or brew install graphviz on macOS).
Examine the error message and the graph to find which resources are referencing each other. For example:
Error: Cycle: aws_instance.web, aws_security_group.webThis means the EC2 instance and security group are referencing each other.
Replace direct resource references with data sources where possible. Instead of:
resource "aws_security_group" "web" {
vpc_id = aws_vpc.main.id
}
resource "aws_instance" "web" {
security_groups = [aws_security_group.web.id]
vpc_id = aws_vpc.main.id
}Use a data source to look up the security group by name instead of creating a new reference:
data "aws_security_group" "web" {
name = "existing-sg"
vpc_id = aws_vpc.main.id
}
resource "aws_instance" "web" {
security_groups = [data.aws_security_group.web.id]
vpc_id = aws_vpc.main.id
}In some cases, you can break cycles by explicitly defining the creation order with depends_on. However, this only works if you're referencing specific outputs rather than attributes:
resource "aws_network_interface" "example" {
subnet_id = aws_subnet.example.id
}
resource "aws_instance" "example" {
depends_on = [aws_network_interface.example]
# Don't reference the network interface directly
}For complex cycles that can't be broken with data sources, split the configuration into multiple Terraform projects:
1. Create resource A and output its ID
2. In a separate Terraform configuration, use data sources to reference the ID from step 1
3. Create resource B using the data source
4. If needed, manually update resource A in a third Terraform run
This multi-step approach respects the DAG requirement while still managing complex infrastructure.
After refactoring, run:
terraform validate
terraform planEnsure the plan shows the resources in the correct order and no cycles remain.
Cycles are by design in Terraform - they prevent undefined behavior. However, there are legitimate real-world use cases for circular dependencies. If you have a genuine need (like a two-way VPC peering where both sides need to reference each other), consider: 1) Using null_resource with local-exec provisioners to handle post-apply updates; 2) Splitting into separate Terraform applies with local state; 3) Contributing to Terraform to enhance cycle detection for specific scenarios. The terraform graph command has a -draw-cycles flag that only works with -type=plan, plan-refresh-only, plan-destroy, or apply operations. Use this for detailed diagnostic information when refactoring complex configurations.
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