search

11-Terraform Functions – Các hàm built-in thường dùng

calendar_today Đăng ngày: 02/05/2026

Khi viết Terraform, bạn sẽ gặp tình huống cần xử lý chuỗi, lọc danh sách, hoặc tính toán giá trị động thay vì hardcode. Đó là lúc các hàm built-in của Terraform phát huy tác dụng. Bài này mình tổng hợp các hàm hay dùng nhất trong thực tế – có ví dụ cụ thể để bạn áp dụng ngay được.

Xem thêm:

07-Terraform Data Types – Các kiểu dữ liệu trong Terraform

10-Terraform Locals – Khai báo biến cục bộ

14-Terraform Workspace – Quản lý nhiều môi trường từ một codebase

1. Terraform Functions hoạt động như thế nào?

Terraform có một tập hàm built-in sẵn, bạn gọi trực tiếp trong expressions mà không cần import hay cài thêm gì. Cú pháp cơ bản:

function_name(argument1, argument2, ...)

Bạn có thể test nhanh bất kỳ hàm nào bằng lệnh:

terraform console

Sau đó gõ thẳng biểu thức vào:

> upper("ttnguyen")
"TTNGUYEN"

> length(["a", "b", "c"])
3

Rất tiện để kiểm tra kết quả trước khi đưa vào code thật.

2. Nhóm 1: String Functions

format – Ghép chuỗi có định dạng

Tương tự printf trong C hay sprintf trong các ngôn ngữ khác.

locals {
  bucket_name = format("%s-%s-%s", var.project, var.environment, var.region)
  # Kết quả: "ttnguyen-production-ap-southeast-1"
}

Dùng khi cần ghép nhiều phần với separator cố định, rõ hơn template string trong một số trường hợp.

formatlist – Áp dụng format cho cả list

locals {
  hostnames = formatlist("server-%s.ttnguyen.net", ["01", "02", "03"])
  # Kết quả: ["server-01.ttnguyen.net", "server-02.ttnguyen.net", "server-03.ttnguyen.net"]
}

joinsplit

locals {
  tags_string = join(",", ["web", "production", "ap-southeast-1"])
  # Kết quả: "web,production,ap-southeast-1"

  parts = split(".", "ttnguyen.net")
  # Kết quả: ["ttnguyen", "net"]
}

replace – Thay thế chuỗi

locals {
  clean_name = replace(var.app_name, "_", "-")
  # "my_app" → "my-app"
}

AWS S3 không cho dùng underscore trong tên bucket – replace giải quyết nhanh gọn.

trimspace, upper, lower

locals {
  env = lower(trimspace(var.environment))
  # "  Production  " → "production"
}

Dùng khi bạn không kiểm soát được input từ user hoặc CI/CD system.

substr

locals {
  short_id = substr(var.commit_hash, 0, 8)
  # "a1b2c3d4e5f6..." → "a1b2c3d4"
}

3. Nhóm 2: Collection Functions

length – Đếm phần tử

locals {
  instance_count = length(var.availability_zones)
}

merge – Gộp nhiều map

Đây là hàm mình dùng rất nhiều khi làm việc với tags trên ttnguyen.net:

locals {
  common_tags = {
    Project   = var.project
    ManagedBy = "terraform"
  }

  resource_tags = merge(local.common_tags, {
    Component = "database"
    Tier      = "backend"
  })
}

Nếu có key trùng, map sau sẽ ghi đè map trước.

concat – Gộp nhiều list

locals {
  all_cidrs = concat(var.public_cidrs, var.private_cidrs)
}

flatten – Làm phẳng list lồng nhau

locals {
  nested = [["a", "b"], ["c", "d"]]
  flat   = flatten(local.nested)
  # Kết quả: ["a", "b", "c", "d"]
}

Hay gặp khi dùng for expressions trả về list of lists.

distinct – Loại bỏ phần tử trùng

locals {
  unique_zones = distinct(["ap-southeast-1a", "ap-southeast-1b", "ap-southeast-1a"])
  # Kết quả: ["ap-southeast-1a", "ap-southeast-1b"]
}

toset, tolist, tomap – Chuyển đổi kiểu

locals {
  zones_set = toset(var.availability_zones)
}

resource "aws_subnet" "main" {
  for_each          = local.zones_set
  availability_zone = each.value
  # ...
}

for_each yêu cầu set hoặc map, không nhận list trực tiếp – đây là lý do hay phải dùng toset.

lookup – Lấy giá trị từ map với default

locals {
  instance_types = {
    dev        = "t3.micro"
    staging    = "t3.medium"
    production = "t3.large"
  }

  instance_type = lookup(local.instance_types, var.environment, "t3.micro")
}

Argument thứ ba là giá trị mặc định nếu key không tồn tại. Dùng thay vì để Terraform báo lỗi khi key sai.

element – Lấy phần tử theo index (có vòng)

locals {
  # Phân bổ 5 instance vào 3 AZ theo vòng
  az_for_instance = [for i in range(5) : element(var.azs, i)]
  # Kết quả: [az[0], az[1], az[2], az[0], az[1]]
}

4. Nhóm 3: Numeric Functions

max, min

locals {
  replica_count = max(var.desired_replicas, 2)  # Tối thiểu 2 replicas
  disk_size     = min(var.requested_disk, 1000) # Tối đa 1000 GB
}

ceil, floor

locals {
  # Tính số AZ cần dùng, làm tròn lên
  az_needed = ceil(var.instance_count / 2)
}

abs

locals {
  diff = abs(var.target - var.current)
}

5. Nhóm 4: Filesystem & Encoding Functions

file – Đọc nội dung file

resource "aws_iam_policy" "custom" {
  name   = "ttnguyen-custom-policy"
  policy = file("${path.module}/policies/custom.json")
}

filebase64 – Đọc file và encode sang base64

resource "aws_lambda_function" "handler" {
  filename         = "lambda.zip"
  source_code_hash = filebase64sha256("lambda.zip")
  # ...
}

jsonencodejsondecode

locals {
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = ["s3:GetObject"]
      Resource = "*"
    }]
  })
}

Dùng jsonencode thay vì viết JSON string thủ công – tránh lỗi cú pháp và dễ đọc hơn nhiều.

locals {
  config      = jsondecode(file("config.json"))
  bucket_name = local.config.storage.bucket
}

base64encode / base64decode

locals {
  encoded_script = base64encode(file("userdata.sh"))
}

resource "aws_instance" "web" {
  user_data = local.encoded_script
  # ...
}

6. Nhóm 5: IP Network Functions

Hay dùng khi chia subnet động thay vì hardcode CIDR:

cidrsubnet – Tính CIDR con

locals {
  vpc_cidr = "10.0.0.0/16"

  public_subnets = [
    cidrsubnet(local.vpc_cidr, 8, 0),  # 10.0.0.0/24
    cidrsubnet(local.vpc_cidr, 8, 1),  # 10.0.1.0/24
    cidrsubnet(local.vpc_cidr, 8, 2),  # 10.0.2.0/24
  ]

  private_subnets = [
    cidrsubnet(local.vpc_cidr, 8, 10), # 10.0.10.0/24
    cidrsubnet(local.vpc_cidr, 8, 11), # 10.0.11.0/24
    cidrsubnet(local.vpc_cidr, 8, 12), # 10.0.12.0/24
  ]
}

Argument: cidrsubnet(prefix, newbits, netnum). Không cần tính tay nữa.

cidrhost – Lấy địa chỉ IP cụ thể trong subnet

locals {
  gateway_ip = cidrhost("10.0.1.0/24", 1)  # "10.0.1.1"
  dns_ip     = cidrhost("10.0.1.0/24", 2)  # "10.0.1.2"
}

7. Nhóm 6: Type Conversion & Logical Functions

coalesce – Trả về giá trị không null/empty đầu tiên

locals {
  instance_name = coalesce(var.custom_name, var.default_name, "ttnguyen-instance")
}

Giá trị nào không phải null và không phải chuỗi rỗng thì dùng cái đó.

can – Kiểm tra expression có hợp lệ không

locals {
  is_valid_json = can(jsondecode(var.config_string))
}

try – Thử expression, fallback nếu lỗi

locals {
  port = try(tonumber(var.port_string), 8080)
  # Nếu convert thất bại thì dùng 8080
}

8. Kết hợp nhiều hàm trong thực tế

Trong thực tế, bạn sẽ hay kết hợp các hàm lại. Ví dụ tạo tên resource chuẩn hóa:

locals {
  # Chuẩn hóa tên: lowercase, bỏ khoảng trắng, thay _ thành -
  normalized_name = replace(lower(trimspace(var.app_name)), "_", "-")

  # Tên đầy đủ với prefix + truncate để không quá 63 ký tự (giới hạn DNS)
  full_name = substr(
    format("%s-%s-%s", local.normalized_name, var.environment, var.region),
    0,
    63
  )
}

Hay khi tạo subnet map cho for_each:

locals {
  subnet_config = {
    for idx, az in var.availability_zones :
    az => {
      cidr = cidrsubnet(var.vpc_cidr, 8, idx)
      az   = az
    }
  }
}

resource "aws_subnet" "main" {
  for_each          = local.subnet_config
  availability_zone = each.value.az
  cidr_block        = each.value.cidr
  # ...
}

Tóm tắt

Nhóm Hàm hay dùng
String format, join, split, replace, lower, trimspace, substr
Collection merge, concat, flatten, distinct, toset, lookup, element
Numeric max, min, ceil, floor
Encoding jsonencode, jsondecode, file, base64encode
Network cidrsubnet, cidrhost
Logic coalesce, try, can

Bạn không cần nhớ hết tất cả. Cứ dùng terraform console để test nhanh khi cần, tra Terraform docs khi gặp use case mới.

Bài viết thuộc series HashiCorp Certified: Terraform Associate trên ttnguyen.net. Cảm ơn bạn đã đọc