Fix lỗi Terraform "Error: Cycle detected in resource dependencies"

intermediate🏗️ Terraform2026-03-25| Terraform 0.13+ / 1.x, mọi hệ điều hành (Linux, macOS, Windows), mọi nhà cung cấp cloud (AWS, GCP, Azure)

Error Message

Error: Cycle detected in resource dependencies
#terraform#cycle#dependency#graph

Lỗi

Error: Cycle detected in resource dependencies

  aws_security_group.web (expand) -> aws_instance.app (expand) -> aws_security_group.web (expand)

Hai hoặc nhiều resource đang phụ thuộc vào nhau theo vòng tròn. Resource A cần Resource B tồn tại trước. Nhưng Resource B cũng cần Resource A. Không cái nào có thể đi trước — vì vậy Terraform từ chối apply.

Nguyên nhân

Trước khi áp dụng bất kỳ thay đổi nào, Terraform xây dựng một đồ thị có hướng không chu trình (DAG) cho tất cả các resource. Mỗi tham chiếu thuộc tính tạo thêm một cạnh. Nếu các cạnh đó quay vòng lại chính chúng, đồ thị không còn là acyclic nữa — và kế hoạch sẽ thất bại ngay lập tức.

Các nguyên nhân thường gặp:

  • Hai resource cùng tham chiếu output của nhau (ví dụ: tham chiếu chéo security_group_idprivate_ip)
  • Một chuỗi depends_on vô tình quay vòng lại điểm xuất phát
  • Hai module truyền output qua lại cho nhau
  • Một resource tham chiếu thuộc tính của chính nó (tinh tế hơn nghe có vẻ)

Bước 1: Đọc đường dẫn vòng lặp trong lỗi

Terraform luôn in ra đường dẫn vòng lặp chính xác. Hãy đọc kỹ trước khi làm bất cứ điều gì:

Error: Cycle detected in resource dependencies

  aws_security_group.web (expand)
  -> aws_instance.app (expand)
  -> aws_security_group.web (expand)

Vòng lặp ở đây rất rõ ràng: aws_security_group.webaws_instance.app → quay lại aws_security_group.web. Đó là điểm khởi đầu của bạn. Hãy tìm ngay đến hai resource đó trong code.

Bước 2: Trực quan hóa toàn bộ đồ thị phụ thuộc

Với các cấu hình phức tạp có nhiều resource, đồ thị trực quan giúp tiết kiệm rất nhiều thời gian tìm kiếm:

terraform graph | dot -Tsvg > graph.svg

Mở graph.svg trong bất kỳ trình duyệt nào. Bạn đang tìm các mũi tên trỏ theo cả hai hướng giữa hai nút. Cạnh hai chiều đó chính là vòng lặp của bạn. Hãy cài đặt Graphviz trước nếu chưa có:

# Ubuntu/Debian
sudo apt install graphviz

# macOS
brew install graphviz

Bước 3: Xác định tham chiếu vòng tròn trong code

Đây là pattern lỗi điển hình — một security group và một EC2 instance đều cần cái kia tồn tại trước:

resource "aws_security_group" "web" {
  name = "web-sg"

  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    # BAD: referencing aws_instance creates a cycle
    cidr_blocks = ["${aws_instance.app.private_ip}/32"]
  }
}

resource "aws_instance" "app" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"
  # BAD: this also references the security group above
  vpc_security_group_ids = [aws_security_group.web.id]
}

Security group cần private IP của instance. Instance cần ID của security group. Không cái nào có thể được tạo trước. Terraform bị kẹt.

Bước 4: Phá vỡ vòng lặp

Chọn phương án phù hợp với tình huống của bạn.

Phương án A: Xóa bỏ tham chiếu hai chiều

Hầu hết thời gian, một trong hai tham chiếu không thực sự cần thiết. Trong ví dụ trên, liệu security group có thực sự cần giới hạn ingress đến IP cụ thể của một instance không? Thường là không. Hãy dùng dải CIDR thay thế:

resource "aws_security_group" "web" {
  name = "web-sg"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    # FIXED: static CIDR, no reference to aws_instance
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "app" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"
  vpc_security_group_ids = [aws_security_group.web.id]
}

Phương án B: Dùng aws_security_group_rule (resource riêng biệt)

Đôi khi bạn thực sự cần cả hai resource biết về nhau. Mẹo ở đây: tạo security group trước không có ingress rule nào, sau đó thêm rule như một resource độc lập sau khi instance đã tồn tại.

resource "aws_security_group" "web" {
  name = "web-sg"
  # No inline ingress rules here
}

resource "aws_instance" "app" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"
  vpc_security_group_ids = [aws_security_group.web.id]
}

# This resource depends on both, but neither depends on it — no cycle
resource "aws_security_group_rule" "allow_instance" {
  type              = "ingress"
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
  cidr_blocks       = ["${aws_instance.app.private_ip}/32"]
  security_group_id = aws_security_group.web.id
}

Resource rule nằm ở phía sau cả hai. Nó phụ thuộc vào chúng — chúng không phụ thuộc vào nó. Vòng lặp đã được phá vỡ.

Phương án C: Xóa depends_on không cần thiết

Các block depends_on tường minh là nguồn gốc phổ biến gây ra vòng lặp vô tình. Terraform đã theo dõi các phụ thuộc ngầm thông qua tham chiếu thuộc tính — bạn không cần khai báo lại chúng:

# Check if this depends_on is creating a loop
resource "aws_s3_bucket_policy" "example" {
  bucket = aws_s3_bucket.main.id
  policy = data.aws_iam_policy_document.example.json

  # The bucket reference above already creates the dependency.
  # This explicit depends_on is redundant — remove it.
  depends_on = [aws_s3_bucket.main]
}

Chỉ dùng depends_on khi không có tham chiếu thuộc tính nào giữa hai resource nhưng một cái vẫn cần cái kia tồn tại trước.

Phương án D: Tái cấu trúc thành các module riêng biệt

Các vòng lặp trải dài hai module là vấn đề kiến trúc. Pattern điển hình: module A xuất thứ gì đó mà module B cần, và module B xuất thứ gì đó mà module A cần. Giải pháp là trích xuất resource dùng chung vào một module thứ ba mà cả hai cùng sử dụng:

# Instead of module A outputting to module B and module B outputting back to A,
# create a third "shared" module that owns the shared resource
module "network" {
  source = "./modules/network"
  # owns VPC, subnets, security groups
}

module "compute" {
  source = "./modules/compute"
  sg_id  = module.network.web_sg_id  # one-way dependency only
}

Bước 5: Xác nhận bản sửa lỗi

Chạy terraform validate trước, sau đó terraform plan:

terraform validate
# Expected: Success! The configuration is valid.

terraform plan
# Should produce a clean plan with no cycle errors

Vẫn thấy lỗi vòng lặp? Đọc lại đường dẫn — lúc này có thể nó chỉ đến một cặp resource khác. Lặp lại Bước 1–4 cho từng vòng lặp mới cho đến khi kế hoạch sạch.

Mẹo tránh lỗi này trong tương lai

  • Đừng trộn lẫn inline rules với các rule resource riêng biệt. Nếu bạn đang dùng các resource aws_security_group_rule, hãy xóa tất cả các block ingressegress khỏi aws_security_group cha. Trộn lẫn cả hai gây ra xung đột và vòng lặp.
  • Phác thảo hướng phụ thuộc trước khi viết code. Một sơ đồ nhanh chỉ mất hai phút. Các mũi tên luôn phải chảy theo một chiều giữa bất kỳ hai resource nào — không bao giờ theo cả hai chiều.
  • Chạy terraform graph khi thêm resource. Phát hiện vòng lặp ở resource thứ 5 dễ hơn nhiều so với việc gỡ rối ở resource thứ 50.
  • Cẩn thận với depends_on ở cấp module. Nó tạo ra phụ thuộc ngầm lên từng resource đơn lẻ bên trong module đó — không chỉ những cái bạn dự định. Phạm vi rộng đó có thể kéo vào các vòng lặp bất ngờ.

Related Error Notes