Sự phiền toái của các đối số loại trừ lẫn nhau
Tuần trước, tôi đã gặp bế tắc khi đang tái cấu trúc (refactoring) các S3 bucket cũ cho một khách hàng. Tôi đang cố gắng chuyển từ các canned ACL đơn giản sang một chính sách kiểm soát quyền sở hữu chi tiết hơn. Sau khi thêm khối cấu hình mới và chạy terraform plan, terminal đã trả về lỗi gây ức chế này:
Error: Invalid combination of arguments
An argument named "acl" cannot be specified when "access_control_policy" is specified.
Các Terraform provider thực thi các schema rất nghiêm ngặt. Khi hai thuộc tính được đánh dấu là loại trừ lẫn nhau (mutually exclusive), bạn phải chọn một trong hai. Bạn không thể sử dụng cả hai cùng một lúc. Đây không chỉ là một đặc điểm riêng của Terraform; nó thường phản ánh việc Cloud API bên dưới không thể xử lý các chỉ dẫn mâu thuẫn trong cùng một yêu cầu.
Xác định xung đột
Bước đầu tiên của bạn là xác định chính xác hai đối số nào đang xung đột. Trong ví dụ AWS của tôi, xung đột nằm trong tài nguyên aws_s3_bucket_acl. Provider coi chuỗi acl đơn giản và khối access_control_policy phức tạp là hai cách khác nhau để thực hiện cùng một công việc.
Dưới đây là đoạn mã bị lỗi trông như thế nào:
resource "aws_s3_bucket_acl" "example" {
bucket = aws_s3_bucket.my_bucket.id
# Thuộc tính cũ (legacy)
acl = "private"
# Chính sách chi tiết mới
access_control_policy {
grant {
grantee {
type = "Group"
uri = "http://acs.amazonaws.com/groups/global/AllUsers"
}
permission = "READ"
}
owner {
id = data.aws_canonical_user_id.current.id
}
}
}
Provider sẽ không tự đoán ý định của bạn. Ngay cả khi logic không chồng chéo, schema chỉ cho phép một đối số cấp cao nhất (top-level) để định nghĩa quyền hạn. Nếu bạn để cả hai, lệnh plan sẽ luôn thất bại.
Tái cấu trúc để tường minh hơn
Việc khắc phục lỗi này đòi hỏi một lựa chọn dứt khoát: thuộc tính nào đại diện cho "nguồn sự thật" (source of truth) của bạn? Nếu bạn đang nâng cấp lên một cấu hình phức tạp hơn, bạn cần xóa hoàn toàn thuộc tính đơn giản hơn thay vì chỉ để trống nó.
Bước 1: Chọn Logic của bạn
Quyết định xem thuộc tính nào phù hợp với yêu cầu hiện tại của bạn. Để kiểm soát chi tiết, hãy giữ lại khối phức tạp. Nếu bạn chỉ cần thiết lập nhanh, hãy dùng chuỗi ký tự đơn giản. Hầu hết các triển khai AWS hiện đại đang chuyển sang access_control_policy để phục vụ việc kiểm tra bảo mật (security auditing) tốt hơn.
Bước 2: Dọn dẹp tài nguyên
Đây là phiên bản đã được sửa đổi. Hãy chú ý rằng dòng acl đã biến mất, không còn chỗ cho sự mơ hồ.
resource "aws_s3_bucket_acl" "example" {
bucket = aws_s3_bucket.my_bucket.id
access_control_policy {
grant {
grantee {
type = "CanonicalUser"
id = data.aws_canonical_user_id.current.id
}
permission = "FULL_CONTROL"
}
owner {
id = data.aws_canonical_user_id.current.id
}
}
}
Xử lý các Dynamic Block và Câu lệnh điều kiện
Lỗi này thường xuất hiện khi sử dụng các khối dynamic hoặc các biến để bật/tắt các tính năng. Nếu logic của bạn không chặt chẽ, bạn có thể vô tình kích hoạt cả hai đối số. Để ngăn chặn điều này, hãy sử dụng giá trị null để vô hiệu hóa đối số không sử dụng một cách rõ ràng.
resource "some_resource" "example" {
# Nếu var.use_simple là true, đặt attribute_x. Ngược lại, đặt nó thành null.
attribute_x = var.use_simple ? var.x_value : null
dynamic "attribute_y" {
# Chỉ tạo khối nếu var.use_simple là false
for_each = var.use_simple ? [] : [1]
content {
# cấu hình phức tạp ở đây
}
}
}
Thói quen quy trình làm việc tốt hơn
Tôi đã bắt đầu chạy terraform validate như một git pre-commit hook tiêu chuẩn. Nó tốn chưa đầy 3 giây và phát hiện các vi phạm schema này ngay tại local. Điều này giúp bạn không phải chờ đợi cả một chu kỳ plan đầy đủ chỉ để tìm thấy một xung đột cú pháp.
Khi soạn thảo các chính sách JSON phức tạp, tôi thường trực quan hóa hệ thống phân cấp trong một Công cụ chuyển đổi YAML ↔ JSON trước. Việc phát hiện các điểm chồng chéo về cấu trúc trong YAML sẽ dễ dàng hơn nhiều. Khi logic đã ổn, tôi chuyển nó sang JSON để sử dụng trong các hàm jsonencode(). Quy trình này giúp các tệp HCL của tôi sạch sẽ và ngăn chặn tình trạng "ma trận đối số" dẫn đến các lỗi này.
Cách xác nhận việc sửa lỗi
Sau khi bạn đã loại bỏ mã gây xung đột, hãy làm theo ba bước sau để xác minh công việc của mình:
- Chạy Validation: Thực hiện lệnh
terraform validate. Nếu lỗi biến mất, cú pháp của bạn đã khớp với schema của provider. - Xem lại Plan: Chạy
terraform plan. Tìm kiếm các cảnh báo "forces replacement". Việc chuyển đổi giữa các đối số loại trừ lẫn nhau đôi khi sẽ kích hoạt việc tạo lại tài nguyên (resource recreation). - Áp dụng thay đổi: Chạy
terraform apply. Nếu cloud API chấp nhận dữ liệu gửi lên (payload), bạn đã thành công.
Những bài học rút ra
- Kiểm tra tài liệu: Hầu hết các provider đều có phần "Note" trong tài liệu của họ. Nó sẽ nêu rõ nếu "Đối số X loại trừ lẫn nhau với Đối số Y".
- Sử dụng Null rõ ràng: Khi sử dụng biến, hãy dùng
nullđể đảm bảo một đối số hoàn toàn bị provider bỏ qua. - Chú ý việc thay thế tài nguyên: Việc hoán đổi các đối số này có thể là một hành động mang tính phá hủy. Luôn kiểm tra xem Terraform có ý định hủy và tạo lại tài nguyên của bạn hay không trước khi bạn nhấn 'yes'.

