大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
Outputs 输出与敏感输出
这一课你将能做到
- 定义 output 把资源创建后才知道的信息(公网 IP、域名、ARN)暴露给使用者
- 用
terraform output命令按名查询输出,并在脚本/CI 中消费这些值 - 正确处理敏感输出,理解 root module 与子模块 output 在可见性上的差异
为什么要学
apply 跑完之后,真正要告诉别人的信息往往是「apply 之后才存在」的:EC2 的公网 IP、ALB 的 DNS 名、RDS 的端点、S3 桶的 ARN。你不能指望每个人去翻 state 或上控制台找。output 就是 Terraform 配置的「返回值」——apply 结束时整齐打印,脚本里可查询,子模块还能把内部封装的信息选择性地交还给调用方。
对本站而言,典型链路是:Terraform 建完基础设施输出 ALB 域名,部署脚本拿到域名后做健康检查、再把域名登记到监控和 DNS;没有 output,自动化就断了一截。
先建立图像
┌─────────────── root module(你的工作目录)────────────────┐
│ resource ... resource ... │
│ │ │
│ └────▶ output "alb_dns" { value = aws_lb.web.dns } │
│ │ │
└─────────────────────┼──────────────────────────────────────┘
▼ apply 结束打印 / terraform output 查询
部署脚本、CI、运维同学
类比:variable 是函数入参,output 是函数返回值,state 是函数运行时闭包。
跟着做
步骤 1:定义基础输出
在前面课程的配置里新建 outputs.tf:
output "application_url" {
description = "本站访问地址"
value = "http://${aws_instance.web.public_ip}"
}
output "security_group_id" {
description = "Web 层安全组 ID"
value = aws_security_group.web.id
}
output "instance_ids" {
description = "全部 Web 实例 ID"
value = aws_instance.web[*].id # 配合 count/for_each 的批量引用
}
执行 terraform apply 后,结尾会出现:
Outputs:
application_url = "http://35.78.xx.xx"
instance_ids = [
"i-0a1b2c3d4e5f67890",
]
security_group_id = "sg-0123456789abcdef0"
输出值可以是任意表达式:字符串拼接、集合、对象,也可以引用 locals。
步骤 2:用 terraform output 消费
terraform output # 列出全部
terraform output application_url # 打印单个(带引号,原始格式)
terraform output -raw application_url # 去掉引号,适合脚本
部署脚本里的典型用法:
URL=$(terraform output -raw application_url)
for i in {1..30}; do
curl -fsS "http://$URL/actuator/health" && break
sleep 5
done
也支持 JSON:
terraform output -json > outputs.json
CI 可以用 jq 解析 JSON 拿到结构化值,比文本切割可靠。
步骤 3:敏感输出
output "db_endpoint" {
value = aws_db_instance.main.address
sensitive = true
}
apply 输出中它显示为:
db_endpoint = <sensitive>
但仍可用 terraform output db_endpoint 查询(这是有意设计——有权限执行 terraform 的人就能看,掩码只防屏幕泄露/日志收集)。规则和变量一致:sensitive 是展示掩码,跟着值传播,不是加密。 output 中只要包含任何敏感值(哪怕嵌在对象深处),整个 output 都必须标记 sensitive = true,否则 plan/apply 直接报错,逼你显式处理:
output "connection" {
value = {
host = aws_db_instance.main.address
password = aws_db_instance.main.password # 该属性本身敏感
}
sensitive = true # 不写会报错
}
步骤 4:depends_on 与输出更新时机
output "post_deploy_check" {
value = aws_instance.web.public_dns
depends_on = [aws_security_group.web]
}
一般不需要写 depends_on,引用关系本身就是依赖;只有当输出值「逻辑上要求某个先发生」却没有直接引用时才需要。另外注意:改 output 不会改动任何云资源,terraform apply 只更新 state 中的输出记录,这也是输出可以放心调整的原因。
步骤 5:子模块输出(为第 8 课预埋)
建立目录结构:
modules/
web/
main.tf
outputs.tf ← 模块自己的输出
main.tf
outputs.tf ← root 输出,可穿透引用模块输出
modules/web/outputs.tf:
output "role_arn" {
value = aws_iam_role.web.arn
}
root 的 main.tf 中调用模块后,root 的输出穿透暴露:
output "web_role_arn" {
value = module.web.role_arn
}
子模块自己的 output 不会自动打印,只有 root module 的 output 会在 apply 后显示。模块输出是模块对外的 API 面,下一课的模块机制会系统展开。
讲清楚要点
- output 只在 root module 打印:子模块 output 的作用是给调用方引用,不显示在终端。
- 输出是 apply 后的「合同信息」:把别人真正要消费的值显式列出,比让他们翻 state 友好且稳定(state 内部结构会变,output 名是你可控的接口)。
- 敏感值必须整条链路标记:Terraform 会追踪值的敏感性并强制标记,无法「偷偷」把敏感值塞进普通输出。
- 输出变更零资源风险:它只影响 state 与展示,可以频繁迭代;但删除一个被下游脚本依赖的 output 属于接口破坏,要协同。
-raw与-json面向自动化:人肉看默认格式即可,脚本一律用 -raw(单值)或 -json(结构化)。
你可能会懵的地方
-
为什么 apply 完没看到我写的 output? 只有根模块(执行 terraform 命令的目录)的输出会打印;写在被调用子模块里的 output 不会显示。检查文件所在位置。
-
output 能不能调用函数、做计算? 能,value 是完整表达式,如
join(",", aws_subnet.main[*].cidr_block);但不能引用资源自身之外的命令式操作。 -
敏感输出为什么
terraform output还能查到? 掩码防的是旁观者和日志,不是防操作者。访问控制应在 state 存储层(S3/IAM)解决,拿不到 state 的人根本执行不了 output。 -
(known after apply)是什么意思? plan 阶段该值还不存在,是资源创建后云平台才分配的,正常现象;apply 后 state 与输出里才会有真值。 -
输出里能放整个 resource 对象吗? 技术上可以(
value = aws_db_instance.main),但会暴露全部字段、易因 provider 版本变化而改变形态,工程上只输出明确的少数属性,保持接口精简。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 含敏感字段的 output 不标 sensitive | plan/apply 直接报错 | 整条输出标 sensitive,或用 nonsensitive() 精确摘字段 |
脚本用 terraform output url 文本切割 | 带引号、格式随版本变 | 单值 -raw,结构化 -json + jq |
| 在子模块写一堆 output 等终端显示 | 模块输出根本不打印 | 需要显示就在 root 再包一层 output |
| 把整个资源对象作为输出 | 接口臃肿、易随 provider 变化 | 只输出下游需要的具体属性 |
| 靠改 output 去「修复」云资源 | output 不触发任何云变更 | 改资源配置再 apply |
卡点急救
The output value has sensitive marks报错:输出表达式引用了敏感值,给 output 加sensitive = true;确有部分字段可公开时用nonsensitive(...)摘出非密字段(自行确认该字段确实不敏感)。- 脚本取到空字符串:确认资源真的创建成功(state show 检查),且输出引用的是正确属性名(public_ip vs private_ip 易混);再确认用了 -raw 去掉引号。
- 改了 outputs.tf 却没生效:output 更新也要跑一次 apply 才写入 state;仅保存文件不会刷新。
面试常问
-
Terraform output 的作用?根模块和子模块的 output 有什么不同? output 把 apply 后才确定的值(IP、DNS、ARN)作为返回值暴露;根模块输出会打印并可
terraform output查询,子模块输出仅用于向调用模块暴露属性,不显示。 -
敏感输出如何处理?能彻底保密吗? 标
sensitive = true后终端与日志中掩码显示,但有权限读 state 的人仍可查询,state 里也不是加密形态;真正的边界是 state 存储加密与 IAM 访问控制。 -
下游脚本要拿 Terraform 创建的资源地址,有哪些做法?
terraform output -raw name传单值、-json传结构化结果;也可在 CI 中用 remote state data source 只读引用另一仓库的输出(第 7、10 课),避免直接读 state 文件。
自测三问
- apply 后要让 CI 拿到 ALB DNS 名做健康检查,具体命令怎么写?
- 一个 output 对象里混了敏感和非敏感字段,有哪两种合规处理方式?
- 为什么不建议把整个
aws_db_instance.main作为输出?
小结一句话
Output 是 Terraform 的返回值:在 root 模块打印关键结果、支持 -raw/-json 被自动化消费、敏感链路全程标记,但它和变量一样只掩码不加密。
延伸
- Output Values 官方文档
- terraform output 命令参考
- Sensitive Data 处理说明
- 下一课:State 管理——本地 state 的问题、S3 远程后端、DynamoDB 锁与状态急救命令。