变量优先级与输出设计
目标
用四种方法给同一个变量赋值,亲自确认优先级,并完成一套包含验证、本地值、输出和敏感值的配置。
为什么重要
“改了变量却没有生效”这个问题的答案,几乎总是优先级。工具会从多个位置接收同名的值,即使发生冲突也不会报错,而是悄悄采用更强的那一个。这样虽然方便,调试却变得困难,所以需要有这样一种感觉:本来就是用来被覆盖的值放在弱的位置,必须坚守的值放在强的位置。尤其是通过命令行传入的值,无论什么情况都会胜出,却不会被记录在任何地方,如果在故障响应中使用之后没有反映到代码中,下一次部署就会悄悄把它改回去。最后,sensitive = true 只是遮住屏幕输出,值仍然原样保留在状态文件和 JSON 输出中。不了解这一局限,就相信“已经做了敏感标记,所以是安全的”,这是最常见的事故起点。
步骤
- 在
/root/tf/vars/variables.tf中声明environment变量。type = string、default = "dev"、description必须全部具备。在/root/tf/vars/outputs.tf中创建名称为environment的输出,按默认值应用之后,把输出以 JSON 形式保存到/root/tf/vars/out/default-outputs.json(.environment.value为dev)。 - 在
/root/tf/vars/terraform.tfvars中写入environment = "stage"并重新应用,把输出保存到/root/tf/vars/out/tfvars-outputs.json。值必须变成stage——文件胜过默认值。 - 这次在命令行中用
-var environment=prod传值并应用,把输出保存到/root/tf/vars/out/cli-outputs.json。tfvars-outputs.json必须保持不变(仍是stage)。 - 在
variables.tf中声明owner变量,但不要把默认值固定写成platform。用TF_VAR_owner=platform环境变量传值并应用,把输出保存到/root/tf/vars/out/env-outputs.json(.owner.value为platform)。同时在outputs.tf中也添加owner输出。 - 在
variables.tf中添加replica_count变量,并加入只允许 1 以上、5 以下的validation块。condition和error_message都必须具备,并且error_message中必须原样包含允许范围1-5这几个字符。用超出范围的值(例如-var replica_count=9)运行,把被拒绝的输出保存到/root/tf/vars/out/validation.txt。 - 在
/root/tf/vars/main.tf中创建locals块,把name_prefix定义为用连字符连接environment和owner的值。在outputs.tf中添加name_prefix输出。在第 8 步中重新创建的cli-outputs.json里,name_prefix必须是prod-platform。 - 把输出集中到
/root/tf/vars/outputs.tf一处,至少放置四个(environment、owner、name_prefix、config_path),并且给所有输出都加上description。config_path原样导出这个配置所创建的local_file的filename。 - 在
variables.tf中用sensitive = true声明api_token变量,并通过TF_VAR_api_token传值。在outputs.tf中也添加api_token输出,并设为sensitive = true。最后同时提供-var environment=prod、TF_VAR_owner=platform和TF_VAR_api_token并应用,然后重新创建cli-outputs.json,并把供人阅读格式的输出也保存到/root/tf/vars/out/sensitive.txt。后者中必须是被遮住的标记而不是值,并且不能看到真实的令牌字符串。
参考
- 值的强弱,从低到高依次为
default→TF_VAR_*→terraform.tfvars→*.auto.tfvars→-var-file→-var。 terraform output -json是机器可读的格式,terraform output是供人阅读的格式。敏感标记只在后者中被遮住。cli-outputs.json是第 6·7·8 步的评分都会查看的文件。每次添加输出后,最后都必须用第 8 步的值组合重新创建它。- 常见错误 1:每一步都保存到同一个文件,导致丢失前一步的结果。第 1–4 步必须分别保存在不同的文件中。
- 常见错误 2:把
owner的默认值写成platform。这样就无法证明环境变量确实起了作用,评分会拒绝。
声明具备类型、默认值和说明的变量
在 /root/tf/vars/variables.tf 中声明 environment 变量。type = string、default = "dev"、description 必须全部具备。在 /root/tf/vars/outputs.tf 中创建名称为 environment 的输出,按默认值应用之后,把输出以 JSON 形式保存到 /root/tf/vars/out/default-outputs.json(.environment.value 为 dev)。
变量块中除了值之外,还有需要附加的内容。三者都没有的话,几个月后就没人知道这个值是什么了。用 -json 导出输出并保存。
用 tfvars 文件覆盖默认值
在 /root/tf/vars/terraform.tfvars 中写入 environment = "stage" 并重新应用,把输出保存到 /root/tf/vars/out/tfvars-outputs.json。值必须变成 stage——文件胜过默认值。
目录中具有特定名称的文件,即使不单独指定也会被自动读取。文件中只使用 이름 = 값(韩文,意为“名称 = 值”)的格式。
用命令行的值胜过文件
这次在命令行中用 -var environment=prod 传值并应用,把输出保存到 /root/tf/vars/out/cli-outputs.json。tfvars-outputs.json 必须保持不变(仍是 stage)。
优先级的顶端是运行时直接给出的值。上一步保存的输出文件不要覆盖,保存到新文件中。
用环境变量注入值
在 variables.tf 中声明 owner 变量,但不要把默认值固定写成 platform。用 TF_VAR_owner=platform 环境变量传值并应用,把输出保存到 /root/tf/vars/out/env-outputs.json(.owner.value 为 platform)。同时在 outputs.tf 中也添加 owner 输出。
工具会自动读取在变量名前加上固定前缀的环境变量。如果把同样的值固定写在默认值中,就无法证明环境变量确实起了作用。
用代码固定允许范围
在 variables.tf 中添加 replica_count 变量,并加入只允许 1 以上、5 以下的 validation 块。condition 和 error_message 都必须具备,并且 error_message 中必须原样包含允许范围 1-5 这几个字符。用超出范围的值(例如 -var replica_count=9)运行,把被拒绝的输出保存到 /root/tf/vars/out/validation.txt。
在变量块中放入承载条件和消息的块。消息必须让用户仅凭它就能改正,所以原样写出允许范围。
用 locals 组合名称前缀
在 /root/tf/vars/main.tf 中创建 locals 块,把 name_prefix 定义为用连字符连接 environment 和 owner 的值。在 outputs.tf 中添加 name_prefix 输出。在第 8 步中重新创建的 cli-outputs.json 里,name_prefix 必须是 prod-platform。
locals 是无法从外部注入的具名表达式。构造出用连字符连接两个变量的值,并把它作为输出导出。
把带有说明的输出集中起来
把输出集中到 /root/tf/vars/outputs.tf 一处,至少放置四个(environment、owner、name_prefix、config_path),并且给所有输出都加上 description。config_path 原样导出这个配置所创建的 local_file 的 filename。
输出是这套配置的公共接口,所以集中在一个文件里。只要有一个缺少说明,评分就会通过统计数量把它找出来。
遮蔽敏感值,并确认其局限
在 variables.tf 中用 sensitive = true 声明 api_token 变量,并通过 TF_VAR_api_token 传值。在 outputs.tf 中也添加 api_token 输出,并设为 sensitive = true。最后同时提供 -var environment=prod、TF_VAR_owner=platform 和 TF_VAR_api_token 并应用,然后重新创建 cli-outputs.json,并把供人阅读格式的输出也保存到 /root/tf/vars/out/sensitive.txt。后者中必须是被遮住的标记而不是值,并且不能看到真实的令牌字符串。
变量和输出两边都需要敏感标记。分别保存供人阅读的输出和供机器读取的输出,比较这两种结果有何不同。