HCL 语法速查
本篇收录写 Terraform 配置时最常用的 HCL 类型字面量、内置函数和条件循环写法,适用于 Terraform 1.9+。
类型字面量
| 类型 | 写法示例 | 说明 |
|---|---|---|
| string | "nginx" | 双引号包裹,内部支持 ${} 插值 |
| number | 8080、3.14 | 整数浮点同属 number,自动推导 |
| bool | true / false | 不加引号 |
| list | ["a", "b", "c"] | 有序可重复,索引从 0 开始 |
| set | toset(["a", "b"]) | 无序不重复,无字面量写法,需 toset() 转换 |
| map | { "env" = "prod", "team" = "ops" } | 键值对,键统一为字符串 |
| object | { name = "web", ports = [80, 443] } | 带固定属性的结构体,各属性类型可不同 |
常用函数
| 函数 | 作用 | 示例 |
|---|---|---|
join | 把列表按分隔符拼成字符串 | join(",", ["a", "b"]) → "a,b" |
split | 按分隔符把字符串切成列表 | split(",", "a,b") → ["a", "b"] |
concat | 拼接多个列表为一个 | concat([1], [2, 3]) → [1, 2, 3] |
merge | 合并多个 map,后者覆盖前者同名键 | merge({ a = 1 }, { b = 2 }) → { a = 1, b = 2 } |
lookup | 按键查 map,查不到返回默认值 | lookup(var.sizes, "prod", "small") |
length | 返回字符串、列表或 map 的长度 | length(var.subnets) |
keys | 取 map 全部键,组成列表 | keys(var.tags) |
values | 取 map 全部值,组成列表 | values(var.tags) |
format | 按格式串生成字符串,类似 printf | format("%s-%02d", "web", 1) → "web-01" |
file | 读取本地文件内容为字符串 | file("${path.module}/user_data.sh") |
templatefile | 渲染模板文件并注入变量 | templatefile("init.tpl", { pkg = "nginx" }) |
try | 依次尝试多个表达式,返回首个不报错的结果 | try(var.cfg.port, 8080) |
coalesce | 返回首个非 null 的参数 | coalesce(var.name, "default") |
can | 判断表达式能否正常求值,返回 bool | can(regex("^ami-", var.ami_id)) |
contains | 判断列表是否包含某元素 | contains(var.azs, "cn-north-1a") |
distinct | 列表去重,保持原有顺序 | distinct(["a", "a", "b"]) → ["a", "b"] |
flatten | 把嵌套列表拍平为一层 | flatten([[1, 2], [3]]) → [1, 2, 3] |
try 与 can 的分工
try 拿值:一连串取值路径哪个通用哪个。can 判真假:常塞在 validation 块或条件表达式里做前置校验。两者都不会因表达式报错中断执行。
条件与循环
| 写法 | 适用场景 | 示例 |
|---|---|---|
三元 + count | 按条件决定资源建不建 | count = var.enable_monitor ? 1 : 0 |
for_each + map | 按 map 批量建资源,键稳定、可单独寻址 | for_each = var.instances,资源内用 each.key、each.value |
for 表达式 | 对列表/map 做转换、过滤,生成新集合 | 见下方代码块 |
for 表达式的三种典型形态:
# 列表推导:取出所有实例的私网 IP
private_ips = [for inst in aws_instance.web : inst.private_ip]
# map 推导:键值同时转换
upper_tags = { for k, v in var.tags : k => upper(v) }
# 带 if 过滤:只保留生产环境的子网 ID
prod_subnets = [for s in var.subnets : s.id if s.env == "prod"]
for_each 为什么优先用 map
for_each 接受 map 或 set of string。用 map 时每个实例以 each.key 作为状态地址的一部分,增删元素不影响其他实例;用 list 转 set 则元素一变地址全乱,容易触发大面积重建。
注释与多行字符串
# 单行注释(推荐风格)
// 单行注释(与 # 等价)
/*
多行块注释,
可跨任意行数
*/
# heredoc 多行字符串,${ } 插值照常生效
user_data = <<-EOF
#!/bin/bash
yum install -y ${pkg_name}
systemctl enable --now ${pkg_name}
EOF
<<EOF原样保留内容;<<-EOF允许内容缩进对齐,输出时自动去掉公共前导空白,配置里优先用带横线的写法- heredoc 结束标记必须顶格独占一行,中间内容里的
${}会按 Terraform 表达式求值