Terraform 函数:让你的基础设施获得超能力
Source: Dev.to
引言
在 AWS 30 天挑战期间,我花了整整两天时间探索 Terraform 的内置函数。它们把许多冗长、易出错的配置转化为简洁、可投入生产的代码。下面是我发现的最实用的模式,每个都配有实际示例。
使用 Terraform 控制台
在编辑 main.tf 之前,先启动交互式控制台来测试表达式:
terraform console
> lower("HELLO WORLD")
"hello world"
> max(5, 12, 9)
12
> split(",", "80,443,8080")
[
"80",
"443",
"8080",
]
> reverse(["a", "b", "c"])
[
"c",
"b",
"a",
]
控制台让你在不进行任何部署的情况下验证逻辑。
1. 规范化资源名称
问题 – AWS 资源通常要求使用小写、连字符的名称。
解决方案
locals {
clean_name = lower(replace(var.project_name, " ", "-"))
}
resource "aws_resourcegroups_group" "project" {
name = local.clean_name
}
lower() 和 replace() 对保持命名一致性至关重要。
2. 清理 S3 Bucket 名称
问题 – S3 Bucket 名称必须小写、不能包含下划线或特殊字符,且长度 ≤ 63 字符。
解决方案
locals {
sanitized_bucket_name = substr(
lower(
replace(
replace(var.bucket_name_input, "_", "-"),
"/[^a-z0-9-]/", ""
)
),
0,
63
)
}
嵌套函数让你一次性完成下划线替换、非法字符剔除、转小写以及截断。
3. 从 CSV 生成安全组规则
问题 – 将逗号分隔的端口列表转换为单独的 ingress 块。
解决方案
variable "allowed_ports" {
default = "80,443,8080"
}
locals {
ports_list = split(",", var.allowed_ports)
}
resource "aws_security_group" "web" {
name = "web-sg"
dynamic "ingress" {
for_each = local.ports_list
content {
from_port = tonumber(ingress.value)
to_port = tonumber(ingress.value)
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
}
split() 将字符串转为列表,进而驱动 dynamic 块。
4. 根据环境选择实例类型
问题 – 不同环境需要不同的实例规格。
解决方案
variable "environment" {
default = "dev"
}
locals {
instance_types = {
dev = "t3.micro"
staging = "t3.small"
prod = "t3.large"
}
selected_type = lookup(local.instance_types, var.environment, "t3.micro")
}
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = local.selected_type
}
lookup() 提供基于映射的条件逻辑,并带有安全的默认值。
5. 验证实例类型模式
问题 – 强制实例类型遵循公司命名约定(例如 t2.* 或 t3.*)。
解决方案
variable "instance_type" {
type = string
validation {
condition = can(regex("^(t2|t3)\\.", var.instance_type))
error_message = "Instance type must start with 't2.' or 't3.'"
}
}
regex() 检查模式,can() 则在匹配失败时安全返回 false 而不是报错。
6. 命名与密钥处理
问题 – 确保备份名称遵循约定并且密码保持机密。
解决方案
variable "backup_name" {
type = string
validation {
condition = endswith(var.backup_name, "-backup")
error_message = "Backup name must end with '-backup'"
}
}
variable "db_password" {
type = string
sensitive = true
}
output "backup_info" {
value = {
name = var.backup_name
password = sensitive(var.db_password)
}
sensitive = true
}
endswith()/startswith() 用于字符串校验,sensitive() 防止机密泄露。
7. 成本计算
问题 – 计算月度总费用、应用抵扣并找出单项最高支出。
解决方案
variable "monthly_costs" {
default = [120.50, 85.00, 200.75, 50.00]
}
variable "credit" {
default = -50.00 # Negative credit
}
locals {
total_cost = sum(var.monthly_costs)
actual_credit = abs(var.credit)
final_cost = max(local.total_cost - local.actual_credit, 0)
highest_cost = max(var.monthly_costs...)
}
output "cost_summary" {
value = {
total_before_credit = local.total_cost
credit_applied = local.actual_credit
final_bill = local.final_cost
highest_single_cost = local.highest_cost
}
}
sum()、abs()、max() 以及展开运算符 (...) 让算术运算变得直观。
8. 时间戳格式化
问题 – 为标签和资源名称生成统一的时间戳。
解决方案
locals {
current_time = timestamp()
formatted_tags = {
CreatedAt = formatdate("YYYY-MM-DD hh:mm:ss ZZZ", local.current_time)
BackupDate = formatdate("YYYY-MM-DD", local.current_time)
Year = formatdate("YYYY", local.current_time)
}
}
resource "aws_s3_bucket" "backups" {
bucket = "backups-${formatdate("YYYY-MM-DD", timestamp())}"
tags = local.formatted_tags
}
timestamp() 返回 UTC 时间,formatdate() 让你按需塑形。
9. 读取 JSON 配置并存储密钥
问题 – 加载包含数据库凭证的 JSON 文件并将其存入 AWS Secrets Manager。
解决方案
locals {
# Verify the file exists
config_exists = fileexists("${path.module}/config/database.json")
# Read and decode JSON (only if the file is present)
db_config = jsondecode(
file("${path.module}/config/database.json")
)
}
resource "aws_secretsmanager_secret" "db" {
name = "db-credentials"
}
resource "aws_secretsmanager_secret_version" "db_version" {
secret_id = aws_secretsmanager_secret.db.id
secret_string = jsonencode(local.db_config)
}