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.
Fixes E_CYCLE_DEPENDENCY
terraform graph -draw-cycles | dot -Tsvg > graph.svgterraform graph -draw-cycles | dot -Tsvg > graph.svgError: Cycle: resource dependency
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.