3. Terraform变量与凭据注入基础
在 Terraform 中,变量是让同一份配置适配不同环境、不同账号、不同区域的关键机制。但变量一旦承载密码、Token、Access Key 这类敏感数据,它就不再只是“参数化工具”,而成为凭据注入链路的一部分。本节围绕 input 变量、TF_VAR_ 环境变量、变量优先级以及安全传参的初步实践展开,目标是让你既能正确传值,也能判断值从哪里来、会不会被写进状态文件或日志。
一、input 变量的三种声明与传值方式
Terraform 的 input 变量通过 variable 块声明,至少包含名称,通常还会声明类型、默认值、描述和是否敏感。例如声明一个数据库密码变量:
variable "db_password" {
type = string
description = "RDS 主账号密码,仅通过安全渠道注入"
sensitive = true
}
variable "db_port" {
type = number
default = 3306
}
声明之后,值可以通过多种途径传入,常见的有四种:交互式输入、-var 命令行参数、-var-file 变量文件、以及 TF_VAR_ 环境变量。交互式输入适合本地临时测试;-var 会把明文留在 shell 历史和进程列表中,不适合敏感值;-var-file 适合结构化传参,但文件本身必须妥善保护;TF_VAR_ 环境变量则适合 CI/CD 中由密钥管理系统动态注入。
需要特别注意命名规则:环境变量名必须是 TF_VAR_ 加上变量名。变量 db_password 对应的环境变量是 TF_VAR_db_password,大小写敏感。写成 TF_VAR_DB_PASSWORD 不会被识别,Terraform 会认为该变量未赋值,若没有默认值就会在 plan 阶段报错。
二、变量优先级:谁覆盖谁
当同一个变量被多个来源赋值时,Terraform 按固定优先级决定最终值。理解这个顺序,是排查“为什么我传的值没生效”的核心。优先级从低到高依次为:
- 环境变量
TF_VAR_name(最低) terraform.tfvars文件terraform.tfvars.json文件- 任意
*.auto.tfvars或*.auto.tfvars.json文件(按文件名字典序,后者覆盖前者) -var-file指定的文件-var命令行参数(最高)
也就是说,命令行 -var 永远赢,环境变量永远输。这个顺序带来一个重要实践结论:不要把敏感值放在优先级最高的 -var 里,因为它会进入 shell 历史;也不要把敏感值放在 terraform.tfvars 里并提交到 Git。相对安全的做法是让敏感值走 TF_VAR_,由 CI 的 secret 注入,同时用非敏感的 *.auto.tfvars 提供区域、实例规格等普通参数。
三、sensitive 标记与状态文件的关系
把变量标记为 sensitive = true 后,Terraform 在 plan 和 apply 的输出中会用 (sensitive value) 替代明文,避免终端和 CI 日志泄露。但必须清楚它的边界:sensitive 只影响输出显示,不改变值的存储方式。如果这个变量最终被写进资源属性,它仍会以明文形式保存在 state 文件中。因此,state 后端必须启用加密与访问控制,例如 S3 后端配合 SSE-KMS 和桶策略,或使用 Terraform Cloud 的加密状态存储。
另一个常见误区是认为 sensitive 能阻止值出现在 terraform output 中。实际上,如果 output 引用了敏感变量且未标记 sensitive = true,Terraform 会直接报错拒绝输出;只有 output 也标记 sensitive,才会以掩码形式展示。这说明敏感信息需要在变量和输出两端都做标记。
四、安全传参的初步实践
下面给出一个可参考的实践结构。假设要部署一台 RDS 实例,密码来自 CI 的 secret,区域和规格来自版本库中的普通配置。
第一步,在 variables.tf 中声明变量,敏感项加标记:
variable "region" {
type = string
default = "cn-hangzhou"
}
variable "db_instance_class" {
type = string
default = "rds.mysql.c1.large"
}
variable "db_password" {
type = string
sensitive = true
}
第二步,在 terraform.auto.tfvars 中放非敏感参数,该文件可以提交版本库:
region = "cn-shanghai"
db_instance_class = "rds.mysql.c1.xlarge"
第三步,在 CI 流水线中通过环境变量注入密码,而不是写进任何文件:
export TF_VAR_db_password="$DB_PASSWORD_FROM_VAULT"
terraform plan -out=tfplan
terraform apply tfplan
执行后如何判断传参是否正确?可以在 plan 输出中观察:region 应显示为 cn-shanghai(来自 auto.tfvars,覆盖了默认值 cn-hangzhou);db_password 应显示为 (sensitive value),而不是明文。如果密码位置出现明文,说明 sensitive 未生效或变量名拼写不一致;如果 region 仍是 cn-hangzhou,说明 auto.tfvars 未被加载或文件名不符合 *.auto.tfvars 规则。
terraform console 可以交互式检查变量最终取值,例如输入 var.region 查看解析结果。但不要在其中打印敏感变量,因为 console 输出同样可能进入日志。
TF_VAR_db_password 写进 Dockerfile、Makefile 或 CI 的明文变量配置中。环境变量在进程层面可被同主机其他进程读取,应尽量缩短其生命周期,并在使用后及时清理。
五、优先级冲突的排查思路
当实际值与预期不符时,按以下顺序排查:先确认变量名拼写与大小写;再确认 TF_VAR_ 前缀是否正确;然后检查是否存在多个 *.auto.tfvars 文件互相覆盖;最后确认命令行是否传了 -var 或 -var-file。由于 -var 优先级最高,一个遗留的调试参数就可能悄悄覆盖生产配置,这是实践中非常隐蔽的问题。