Fix lỗi Terraform: "count" và "for_each" Meta-Arguments Không Dùng Cùng Nhau

beginner🏗️ Terraform2026-05-11| Terraform 0.13+, mọi hệ điều hành (Linux, macOS, Windows), mọi cloud provider

Error Message

The "count" and "for_each" meta-arguments are mutually exclusive.
#terraform#count#for_each#meta-argument

Lỗi Gặp Phải

Bạn chạy terraform plan hoặc terraform apply và gặp ngay lỗi này:

│ Error: Invalid combination of "count" and "for_each"
│
│   on main.tf line 3, in resource "aws_instance" "web":
│    3: resource "aws_instance" "web" {
│
│ The "count" and "for_each" meta-arguments are mutually exclusive.

Một resource block (hoặc module block) đang có cả count lẫn for_each được định nghĩa cùng lúc. Terraform không thể xác định chiến lược lặp nào cần dùng, nên nó dừng lại ngay tại đó.

Nguyên Nhân

Cả countfor_each đều yêu cầu Terraform tạo nhiều instance của một resource — nhưng cách hoạt động của chúng hoàn toàn khác nhau:

  • count — tạo N bản sao, tham chiếu theo chỉ số (resource[0], resource[1], …)
  • for_each — tạo một bản sao cho mỗi key trong map hoặc phần tử trong set, tham chiếu theo key (resource["web"], resource["db"], …)

Khai báo cả hai trong cùng một block là mâu thuẫn. Giống như bảo đầu bếp nấu một miếng bít tết vừa tái vừa chín hoàn toàn cùng lúc — chỉ được chọn một.

Ba tình huống thường gặp phải lỗi này:

  • Đang chuyển code từ dạng count sang for_each, nhưng quên xóa dòng count
  • Copy-paste một resource block từ dự án khác đang dùng count, rồi thêm for_each vào mà không dọn dẹp
  • Một module call nhận cả hai làm tham số từ các nguồn khác nhau

Cách Khắc Phục Từng Bước

Bước 1: Tìm block gây lỗi

Thông báo lỗi của Terraform bao gồm tên file và số dòng chính xác. Mở file đó và tìm block có cả hai tham số nằm cạnh nhau:

resource "aws_instance" "web" {
  count    = 3                          # ← xóa một trong hai cái này
  for_each = toset(["a", "b", "c"])    # ← chỉ giữ lại một

  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
}

Bước 2: Chọn meta-argument phù hợp

Chọn cái thực sự phù hợp với trường hợp của bạn.

Dùng count khi bạn cần N bản sao giống nhau (hoặc gần giống nhau) và key không quan trọng:

resource "aws_instance" "web" {
  count = 3

  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name = "web-${count.index}"
  }
}

Dùng for_each khi mỗi instance có vai trò hoặc định danh riêng biệt — web, api, db — hoặc bạn cần tham chiếu chúng theo tên sau này:

locals {
  instances = {
    web = "t3.micro"
    api = "t3.small"
    db  = "t3.medium"
  }
}

resource "aws_instance" "servers" {
  for_each = local.instances

  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = each.value

  tags = {
    Name = each.key
  }
}

Tham chiếu đến một instance cụ thể sau này rất rõ ràng và dễ đọc:

output "web_ip" {
  value = aws_instance.servers["web"].public_ip
}

Bước 3: Xóa meta-argument không dùng

Xóa cái bạn không giữ lại. Kiểm tra kỹ toàn bộ block — một dòng count = ... hay for_each = ... ẩn ở cuối cũng đủ gây ra lỗi. Mỗi resource chỉ nên có đúng một cái (hoặc không cái nào, nếu bạn chỉ cần một instance duy nhất).

Module block tuân theo quy tắc tương tự

Các lệnh gọi module cũng dễ mắc lỗi này:

# Sai
module "vpc" {
  source   = "./modules/vpc"
  count    = 2
  for_each = toset(["us-east-1", "eu-west-1"])
}

# Đúng — chọn một trong hai:
module "vpc" {
  source   = "./modules/vpc"
  for_each = toset(["us-east-1", "eu-west-1"])

  region = each.value
}

Kiểm Tra Sau Khi Sửa

Chạy terraform validate trước — nhanh hơn plan đầy đủ và phát hiện lỗi cú pháp ngay lập tức:

terraform validate

Bạn sẽ thấy:

Success! The configuration is valid.

Sau đó xác nhận plan trông đúng không:

terraform plan

Một điều cần lưu ý: nếu bạn đang chuyển một resource đã có từ count sang for_each, Terraform sẽ lên kế hoạch xóa và tạo lại mọi instance. Định dạng key trong state thay đổi từ resource[0] sang resource["key"], và Terraform coi chúng là các đối tượng khác nhau. Dùng terraform state mv để migrate state thủ công nếu bạn cần tránh việc tạo lại resource.

Mẹo Nhanh

  • Ưu tiên dùng for_each cho hầu hết hạ tầng thực tế. Xóa một phần tử khỏi danh sách dùng count ở giữa và Terraform sẽ dịch chuyển tất cả các chỉ số phía sau — có thể xóa và tạo lại những resource bạn chưa hề chỉnh sửa. Với for_each, chỉ key bị xóa là biến mất.
  • Map hoặc set rỗng vẫn hợp lệfor_each = {} tạo ra không resource nào mà không báo lỗi.
  • Không thể truyền danh sách thông thường vào for_each — bọc nó bằng toset(): for_each = toset(var.names). Danh sách cho phép trùng lặp; for_each thì không.
  • Dynamic block là chuyện khác — dùng for_each bên trong một dynamic block trong một resource đã dùng count là hoàn toàn hợp lệ. Đó không phải xung đột mà Terraform đang phàn nàn.

Related Error Notes