Lỗi 'Variables not allowed' trong Terraform: Dùng biến trong cấu hình Backend

intermediate🏗️ Terraform2026-04-21| Terraform 0.12+, mọi hệ điều hành (Linux, macOS, Windows), mọi backend (S3, GCS, Azure, Consul)

Error Message

Error: Variables not allowed: Variables may not be used here.
#terraform#backend#biến#cấu hình

Lỗi Gặp Phải

Bạn thêm một biến vào cấu hình backend — điều tưởng chừng hoàn toàn hợp lý — nhưng Terraform lại báo lỗi:

Error: Variables not allowed

  on main.tf line 4, in terraform:
   4:   bucket = var.state_bucket

Variables may not be used here.

Đây là lỗi cực kỳ phổ biến, đặc biệt khi bạn đang kết nối các module dùng chung hoặc thiết lập môi trường đa tầng, nơi bucket, prefix hay region của backend cần khác nhau theo từng deployment.

Nguyên Nhân

Terraform xử lý block backend trước tất cả mọi thứ khác — trước cả biến, locals, data sources, hay bất kỳ biểu thức nào. Backend được khởi tạo đầu tiên để Terraform biết nơi đọc và ghi state. Đến khi các thành phần khác được tải, var.*, local.*data.* vẫn chưa được phân giải.

Đây là thiết kế có chủ đích, không phải lỗi. Block backend chỉ chấp nhận giá trị literal — chuỗi, số, boolean. Không có nội suy biểu thức, không có ngoại lệ.

Tình Huống Thường Gặp

Đây là cách cấu hình hay gây ra lỗi này:

variable "environment" {
  type = string
}

variable "state_bucket" {
  type = string
}

terraform {
  backend "s3" {
    bucket = var.state_bucket        # ❌ Not allowed
    key    = "${var.environment}/terraform.tfstate"  # ❌ Not allowed
    region = "ap-southeast-1"
  }
}

Cả hai dòng đều bị lỗi. Ngay cả cú pháp nội suy chuỗi ${} cũng bị chặn bên trong block backend.

Cách Sửa 1: Dùng Flag -backend-config (Nhanh)

Loại bỏ hoàn toàn các giá trị động khỏi block backend. Chỉ giữ lại giá trị tĩnh — hoặc để skeleton trống:

terraform {
  backend "s3" {
    region = "ap-southeast-1"  # static values only
  }
}

Truyền các giá trị động lúc chạy init qua flag -backend-config:

terraform init \
  -backend-config="bucket=my-tfstate-prod" \
  -backend-config="key=prod/terraform.tfstate"

Hoặc dùng file cấu hình riêng cho từng môi trường:

# backends/prod.hcl
bucket = "my-tfstate-prod"
key    = "prod/terraform.tfstate"
region = "ap-southeast-1"
terraform init -backend-config=backends/prod.hcl

Hầu hết các pipeline CI/CD xử lý việc này bằng cách đặt ENV làm biến pipeline, rồi gọi terraform init với file .hcl tương ứng cho môi trường đó.

Cách Sửa 2: Cấu Hình Backend Một Phần (Khuyến Nghị Cho Team)

Chỉ đặt những phần thực sự tĩnh vào trong block backend. Để trống các trường động — Terraform sẽ hỏi bạn lúc init hoặc nhận chúng qua flag.

terraform {
  backend "s3" {
    # Only what never changes goes here
    region         = "ap-southeast-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

Kết hợp trong script init của bạn:

#!/bin/bash
ENV=${1:-staging}

terraform init \
  -backend-config="bucket=myapp-tfstate-${ENV}" \
  -backend-config="key=${ENV}/terraform.tfstate"

Không cần nhân bản block backend cho nhiều môi trường — chỉ một file main.tf gọn gàng và các file .hcl riêng cho từng môi trường.

Cách Sửa 3: Dùng Terragrunt (Cho Hệ Thống Đa Môi Trường Phức Tạp)

Quản lý nhiều môi trường với backend biến đổi nhiều? Terragrunt giải quyết vấn đề này một cách tự nhiên. Nó tạo cấu hình backend động trước khi gọi Terraform:

# terragrunt.hcl
remote_state {
  backend = "s3"
  config = {
    bucket = "myapp-tfstate-${local.environment}"
    key    = "${path_relative_to_include()}/terraform.tfstate"
    region = "ap-southeast-1"
  }
}

Terragrunt xử lý việc tạo động trước khi Terraform nhìn thấy block backend. Phân tách rõ ràng, không cần workaround.

Kiểm Tra Sau Khi Sửa

Sau khi sửa, chạy lại terraform init và kiểm tra kết quả:

terraform init -backend-config=backends/prod.hcl

# Expected output:
# Initializing the backend...
# Successfully configured the backend "s3"!

Xác nhận state backend đã kết nối:

terraform state list

Workspace đã có dữ liệu sẽ trả về danh sách tài nguyên. Workspace mới sẽ trả về danh sách trống. Dù thế nào, không có lỗi tức là backend đang hoạt động tốt.

Để kiểm tra trực tiếp cấu hình backend đã được phân giải:

cat .terraform/terraform.tfstate | python3 -m json.tool | grep -A10 '"backend"'

Tổ Chức File Backend

Với backend đa môi trường, cấu trúc thư mục sau đây sẽ dễ mở rộng:

infra/
├── main.tf            # backend block with static values only
├── variables.tf
├── backends/
│   ├── dev.hcl
│   ├── staging.hcl
│   └── prod.hcl
└── scripts/
    └── init.sh        # wrapper: terraform init -backend-config=backends/$ENV.hcl

Mỗi file .hcl chứa các giá trị backend đặc thù cho môi trường đó. Hãy commit chúng vào version control — chúng không chứa secrets, chỉ là tên bucket và key path.

Những Lỗi Thường Gặp Sau Khi Sửa

  • Chạy terraform plan mà không chạy lại init: Đã thay đổi cách cấu hình backend? Luôn chạy lại terraform init trước.
  • Quên flag -reconfigure khi đổi backend: Chuyển workspace hiện có sang vị trí backend mới cần dùng terraform init -reconfigure.
  • Trộn lẫn nguồn cấu hình backend: Đừng khai báo cùng một key trong cả block backend lẫn file -backend-config. Giá trị từ flag sẽ được ưu tiên, nhưng nên phân tách trách nhiệm rõ ràng để tránh nhầm lẫn.

Mẹo: Kiểm Tra File HCL Trước Khi Init

Chỉnh sửa thủ công file .hcl hay .tfvars rất dễ sai. Trước khi mất công chạy terraform init chỉ để phát hiện lỗi cú pháp, hãy kiểm tra cấu trúc trước. YAML ↔ JSON Converter trên ToolCraft rất hữu ích ở đây — cấu trúc HCL khá giống JSON, nên công cụ này có thể phát hiện nhanh các dấu ngoặc không khớp hay lồng ghép sai. Mọi thứ chạy trên trình duyệt, không có gì bị tải lên server.

Related Error Notes