CodeRoad Map 程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。
由 程序员柏风 独立维护 · 关于作者
学习 更多
变量、输出与 tfvars
这一课你将能做到
用 variable 块声明带类型、默认值、校验规则的输入变量,并区分四种赋值方式的优先级
用 output 块输出 IP、地址等结果,理解敏感输出的处理
用 dev/prod 两套 tfvars 文件让同一份配置跑出不同环境
为什么要学
没有变量,你的 Terraform 配置只能服务一个环境:实例类型写死、环境名写死、密码写死。真实项目至少有开发、测试、生产三套,它们结构相同、参数不同。变量 + tfvars 就是 Terraform 的「参数化」机制,和 Java 方法入参是一个道理;输出则是把创建结果「return」出来,给人和其他系统看。CI 流水线里换环境,本质就是换一个 tfvars 文件。
人话:配置是菜谱,变量是「微辣还是特辣、加不加香菜」的勾选单,tfvars 就是事先填好的两种套餐;output 是菜做好后服务员告诉你的取餐号。
先建立图像
变量赋值的四种方式(优先级从低到高):
1. variable 块里的 default(最低)
2. terraform.tfvars / *.auto.tfvars 文件
3. -var-file 指定的文件、-var 单独传入
4. 环境变量 TF_VAR_xxx(与 -var 同级,按具体规则覆盖)
核心心智模型:
概念 Java 类比 作用 variable 方法参数 调用方传值进来 locals 方法内局部变量 配置内部计算复用 output return 值 把结果暴露出去
跟着做
步骤 1:声明带类型和默认值的变量
新建 vars-demo/variables.tf:
variable "project" {
description = "项目名,用于资源命名和标签"
type = string
default = "springboot-demo"
}
variable "instance_type" {
description = "EC2 实例规格"
type = string
default = "t3.micro"
}
variable "env" {
description = "环境标识:dev/staging/prod"
type = string
}
variable "allowed_ports" {
description = "安全组放行的端口列表"
type = list(number)
default = [22, 8080]
}
没有 default 的变量(env)必须在 plan/apply 时提供,否则 Terraform 会交互式询问。
步骤 2:加校验规则
variable "env" {
description = "环境标识"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.env)
error_message = "env 只能是 dev、staging、prod 之一。"
}
}
validation 在加载阶段执行,传入 test 会直接拒绝,连 plan 都进不去。这比把错误拖到云 API 报回来说清楚得多。
步骤 3:在配置中使用
main.tf:
terraform {
required_version = ">= 1.7.0"
}
resource "null_resource" "app" {
triggers = {
project = var.project
instance = var.instance_type
env = var.env
port_count = length(var.allowed_ports)
first_port = var.allowed_ports[0]
}
}
步骤 4:用 tfvars 文件区分环境 env = "dev"
instance_type = "t3.micro"
allowed_ports = [22, 8080]
env = "prod"
instance_type = "t3.small"
allowed_ports = [8080]
注意:生产 tfvars 只放非敏感参数,密码绝不写这里(见步骤 7)。
terraform plan -var-file=dev.tfvars
terraform plan -var-file=prod.tfvars
对比两次计划的差异——同一份配置,两套参数。文件名固定为 terraform.tfvars 时会自动加载,省去 -var-file。
步骤 5:输出结果 output "project_full_name" {
description = "环境与项目组合名"
value = "${var.env}-${var.project}"
}
output "instance_type" {
value = var.instance_type
}
apply 后终端会打印输出;不想每次 apply 都刷一屏,可用:
terraform output
terraform output instance_type
步骤 6:敏感变量与敏感输出 variable "db_password" {
description = "RDS 管理员密码"
type = string
sensitive = true
}
output "db_password_echo" {
value = var.db_password
sensitive = true
}
sensitive = true 的变量和输出不会显示在终端和日志里(显示为 sensitive)。但要清醒:它仍然明文存在于 state 文件中 ,sensitive 只防「屏幕肩窥」,不防 state 泄露。
步骤 7:用环境变量传密钥(不落盘) $env:TF_VAR_db_password = "你的强密码"
terraform plan -var-file=prod.tfvars
Terraform 自动把 TF_VAR_ 前缀的环境变量绑定到同名变量。CI 的密钥管理(GitHub Secrets、GitLab CI variables)都走这条路,密码不进任何文件。
步骤 8:复杂类型变量 variable "apps" {
type = map(object({
port = number
size = string
}))
default = {
order = { port = 8080, size = "t3.micro" }
pay = { port = 8081, size = "t3.small" }
}
}
output "app_ports" {
value = { for k, v in var.apps : k => v.port }
}
object/list/map 可以自由组合,1.3+ 还支持 optional() 标记对象内可选字段,配置模块接口时很好用。
讲清楚要点
变量只定义在一个地方 :variables.tf 声明,tfvars 赋值,资源中引用,三处各司其职,别在资源块里复制默认值。
tfvars 也建议进 Git (非敏感的):环境参数是配置的一部分,评审有价值;敏感值走环境变量或 Secrets Manager。
output 依赖资源属性时只在 apply 后有值 :plan 阶段会显示 <sensitive> 或 known after apply,属正常。
-var 适合临时覆盖单个值 :如 terraform apply -var="instance_type=t3.medium",但临时改动容易忘,团队流程里应留下记录。
变量类型要写 :不写类型默认 any,调用方传错形状要到很深层才报错;显式 string/number/bool/集合类型把错误挡在门口。
你可能会懵的地方
改了 tfvars 为什么 plan 没变化?
文件名不是 terraform.tfvars 又没加 -var-file,Terraform 不会自动加载任意命名文件。确认加载方式。
变量和 locals 到底选哪个?
需要调用方按环境改变的用 variable;只是配置内部避免重复、做派生计算的用 locals。locals 外部无法传值。
sensitive 了为什么 state 里还能看到?
sensitive 是 UI 层脱敏,state 需要真实值来管理资源。保护 state 存储(加密、桶权限)才是根本。
能在变量之间引用吗,比如 var.b 默认值引用 var.a?
不能。变量默认值必须是字面常量。需要联动就定义 locals 做派生。
tfvars 和 .auto.tfvars 有什么区别?
terraform.tfvars 和 *.auto.tfvars 自动加载,后者可以多文件并存(按字母序);其余命名必须显式 -var-file。
错法对照 错法 问题 正解 密码写进 prod.tfvars 提交 Git 凭据泄露 TF_VAR_ 环境变量或密钥服务 不写 type,全靠默认 any 传错类型晚报错 显式声明类型 资源里到处写默认值字符串 改环境时漏改 一律走 var.xxx output 打印完整密码还不标 sensitive 日志泄露 sensitive = true,最好不输出密钥 本地直接改 state 里的值当「改变量」 状态与配置分裂 改变量和配置,重新 apply
卡点急救
No value for required variable :变量无 default 且未传值。加 -var-file、-var 或 TF_VAR_ 环境变量。
validation 报错但不知传了啥 :检查命令行顺序和自动加载的 tfvars;terraform console 输入 var.env 可查看当前解析值。
tfvars 中文或路径问题 :文件保存为 UTF-8;路径含空格时 -var-file="my vars/dev.tfvars" 加引号。
面试常问
Terraform 变量有哪几种赋值方式?优先级如何?
default 默认值最低;自动加载的 terraform.tfvars / *.auto.tfvars;-var-file 与 -var;TF_VAR_ 环境变量(命令行参数优先级最高)。同名变量以后加载/高优先级者为准。
sensitive = true 能防止密码泄露吗?
只能防止输出到终端和日志;state 文件中仍为明文。需要同时严格限制 state 存储访问,或让资源直接从 Secrets Manager/KMS 取密钥。
dev/prod 共用一份配置如何实现?
配置只引用变量,每环境一份 tfvars(规格、副本、白名单不同),CI 按环境选择 -var-file;敏感值走环境变量注入。
自测三问
执行 plan 时不想交互输入,让 env=dev 自动生效,有哪几种做法?
为什么不建议把数据库密码写进 tfvars 文件?sensitive 能解决这个问题吗?
variable、locals、output 分别在什么时机用?
小结一句话 变量负责把「环境差异」从配置里抽出来,tfvars 按环境填值、环境变量传密钥,output 回传结果,sensitive 只管屏幕不管 state。
延伸