Lỗi Gặp Phải
Error: Duplicate resource "aws_s3_bucket" configuration
on main.tf line 15:
15: resource "aws_s3_bucket" "example" {
A managed resource "aws_s3_bucket" "example" has already been declared at main.tf:5,1-33
Terraform báo lỗi này ngay khi bạn chạy terraform plan hoặc terraform validate. Hai resource block dùng chung type và name — Terraform không tự đoán bạn muốn dùng cái nào. Nó dừng lại ngay ở bước parse, trước khi chạm vào state hay gọi bất kỳ provider nào.
Nguyên Nhân
Mỗi resource trong Terraform có địa chỉ gồm hai phần: resource_type.resource_name. Tổ hợp này phải là duy nhất trong một module. Nếu trùng lặp, Terraform sẽ không tải được cấu hình. Dưới đây là bốn nguyên nhân phổ biến nhất:
- Copy-paste nhưng quên đổi tên — bạn nhân đôi một block để chỉnh vài giá trị rồi bỏ quên không đổi label.
- Cùng một resource xuất hiện trong hai file
.tf— Terraform gộp tất cả các file.tftrong thư mục lại. Một resource trongstorage.tfvà cùng resource đó trongmain.tfsẽ xung đột nhau. - Gọi một module hai lần với cùng label — nếu child module khai báo resource bên trong và bạn gọi nó hai lần với label giống nhau, các tên resource sẽ xung đột trong namespace của parent.
- Còn sót lại từ merge conflict — một lần merge Git cẩu thả để lại cả hai phiên bản của resource block chưa được giải quyết trong cùng một file.
Cách Khắc Phục Từng Bước
Bước 1 — Tìm tất cả vị trí khai báo
Thông báo lỗi đã chỉ rõ cả hai vị trí khai báo: main.tf:5 cho khai báo đầu tiên, main.tf:15 cho bản trùng lặp. Với các project lớn có nhiều file, dùng grep sẽ nhanh hơn tìm thủ công:
# Tìm kiếm trong tất cả các file .tf trong thư mục module
grep -rn 'resource "aws_s3_bucket" "example"' .
Tất cả file và số dòng khớp sẽ hiện ra ngay lập tức.
Bước 2 — Quyết định giữ block nào
Mở cả hai vị trí để so sánh. Thường thì một cái là bản gốc, cái còn lại là bản thừa vô tình để lại — hoặc một trong hai đã lỗi thời. Giữ lại cái đúng và xóa cái kia.
Trước khi sửa (bị lỗi):
# main.tf — dòng 5-10
resource "aws_s3_bucket" "example" {
bucket = "my-app-assets"
}
# main.tf — dòng 15-20 (copy-paste rồi quên xóa)
resource "aws_s3_bucket" "example" {
bucket = "my-app-assets"
tags = { Environment = "prod" }
}
Sau khi sửa — xóa block đầu tiên, giữ lại block có tags:
resource "aws_s3_bucket" "example" {
bucket = "my-app-assets"
tags = { Environment = "prod" }
}
Bước 3 — Nếu cần hai bucket, đổi tên một cái
Có thể cả hai block đều cần thiết — chúng chỉ vô tình bị đặt cùng label. Đặt cho block thứ hai một tên khác biệt là xong:
resource "aws_s3_bucket" "example" {
bucket = "my-app-assets"
}
resource "aws_s3_bucket" "example_logs" {
bucket = "my-app-assets-logs"
}
Bước 4 — Sửa trường hợp module được gọi hai lần với cùng label
Gọi một child module hai lần trong cùng một parent là hoàn toàn hợp lệ — miễn là mỗi lần gọi có label riêng. Nếu không có label khác nhau, cả hai instance sẽ cố đăng ký cùng tên resource trong namespace của parent:
# BỊ LỖI: dùng cùng một label hai lần
module "storage" {
source = "./modules/storage"
bucket_name = "assets"
}
module "storage" {
source = "./modules/storage"
bucket_name = "logs"
}
# ĐÃ SỬA: mỗi lần gọi có label riêng
module "storage_assets" {
source = "./modules/storage"
bucket_name = "assets"
}
module "storage_logs" {
source = "./modules/storage"
bucket_name = "logs"
}
Mỗi lần gọi có namespace riêng. Các resource bên trong sẽ không xung đột nữa.
Bước 5 — Với nhiều resource tương tự, dùng for_each
Việc copy resource block để tạo nhiều infrastructure tương tự chính là thói quen dẫn đến lỗi này. for_each là giải pháp — một block, nhiều instance:
locals {
buckets = {
assets = "my-app-assets"
logs = "my-app-assets-logs"
backup = "my-app-backup"
}
}
resource "aws_s3_bucket" "example" {
for_each = local.buckets
bucket = each.value
tags = {
Name = each.key
}
}
Kết quả là ba instance riêng biệt — aws_s3_bucket.example["assets"], aws_s3_bucket.example["logs"], và aws_s3_bucket.example["backup"] — từ một block duy nhất. Không trùng lặp, không xung đột.
Kiểm Tra Sau Khi Sửa
terraform validate là bước kiểm tra nhanh nhất. Lệnh này phát hiện lỗi HCL mà không cần credentials hay quyền truy cập state:
terraform validate
Kết quả khi thành công:
Success! The configuration is valid.
Sau đó chạy terraform plan để xác nhận mọi thứ trông đúng.
Một trường hợp đặc biệt cần lưu ý: nếu bạn đổi tên resource thay vì xóa bản trùng lặp, Terraform sẽ lên kế hoạch xóa cái cũ và tạo lại với tên mới. Để tránh chu trình destroy/recreate này, hãy di chuyển state entry trước:
# Di chuyển state sang địa chỉ mới trước khi apply
terraform state mv aws_s3_bucket.example aws_s3_bucket.example_logs
Sau khi di chuyển, terraform plan sẽ không hiển thị thay đổi nào cho resource đó.
Checklist Nhanh
- Chạy
grep -rn 'resource "TYPE" "NAME"'trên tất cả các file.tfđể tìm mọi vị trí khai báo. - Kiểm tra các dấu hiệu merge conflict chưa được giải quyết (
<<<<<<<,=======,>>>>>>>) bên trong các file.tf. - Hai lần gọi module với cùng label? Đổi tên ít nhất một cái.
- Cần tạo nhiều resource tương tự? Dùng
for_eachthay vì copy block. - Đã đổi tên resource? Chạy
terraform state mvtrước khi apply để tránh destroy/recreate.

