能否基于动态非整数值更新Terraform只写属性?
遇到的问题
我原本想把Terraform里RDS集群的只写密码属性和AWS SecretsManager绑定,靠Secret的版本ID自动触发密码更新,写了这段代码:
data "aws_secretsmanager_secret" "db_master_password" { name = local.db_master_user_password_secret_id } data "aws_secretsmanager_secret_version" "db_master_password" { secret_id = data.aws_secretsmanager_secret.db_master_password.id } resource "aws_rds_cluster" "app" { cluster_identifier = "my-rds-cluster" master_password_wo = aws_secretsmanager_secret_version.db_master_password.secret_string master_password_wo_version = aws_secretsmanager_secret_version.db_master_password.version_id }
结果直接踩坑——master_password_wo_version要求必须是单调递增的整数,而Secret的版本ID是随机字符串,根本不兼容。
翻了一圈官方文档和社区案例,所有提到*_wo这类只写属性的地方,全都是用常量版本号,还让用户要更新密码时手动把版本号加1。这完全不符合我的需求啊——我想要的是Secret自动轮换后,跑个terraform apply就能自动更新RDS密码,不想每次都改代码里的版本号。
当然,我也想过写个Lambda监控Secret变更,然后更新一个存整数的SSM参数来当版本号,但这实在太绕了,完全是额外的维护负担。我是不是漏看了什么?为什么版本号非得是数字?随便一个能标识变更的唯一值不都能触发更新吗?现在只能暂时放弃只写属性,继续用普通的master_password,全靠严格权限控制来保护Terraform状态文件。
问题根源与可行方案
1. 为什么*_wo_version必须是递增整数?
Terraform搞*_wo只写属性的初衷,就是不想把敏感值(比如数据库密码)存到状态文件里。而*_wo_version的作用就是给Terraform一个“变更信号”:只有当这个版本号比上次记录的大,Terraform才会认为需要更新对应的敏感属性。用整数是因为它的递增逻辑最直观,能避免像UUID、随机字符串这种无法判断顺序的值导致的误触发(比如回滚Secret版本时,版本ID变了但实际是旧值,这时候Terraform不该更新)。
2. 不用改代码的自动化方案
不用搞Lambda那套,有两个更轻量的方式实现自动触发:
- 用Secret的轮换次数当版本号:给SecretsManager开自动轮换后,让轮换的Lambda顺便把轮换次数写到Secret的标签里(比如
rotation_count: 3),然后用data "aws_secretsmanager_secret"读取这个标签值转成整数,作为*_wo_version - 用Secret版本的时间戳当版本号:直接取
aws_secretsmanager_secret_version的created_date,转成YYYYMMDDHHMMSS格式的整数,这个值每次轮换后肯定是递增的,完全符合要求。修改后的代码大概是这样:
注:只要Secret每次轮换都是生成新的版本,这个时间戳就会一直递增,不会有问题。data "aws_secretsmanager_secret" "db_master_password" { name = local.db_master_user_password_secret_id } data "aws_secretsmanager_secret_version" "db_master_password" { secret_id = data.aws_secretsmanager_secret.db_master_password.id } resource "aws_rds_cluster" "app" { cluster_identifier = "my-rds-cluster" master_password_wo = aws_secretsmanager_secret_version.db_master_password.secret_string # 用版本创建时间的时间戳整数作为版本号 master_password_wo_version = tonumber(formatdate("YYYYMMDDHHMMSS", data.aws_secretsmanager_secret_version.db_master_password.created_date)) }
3. 退而求其次的安全方案
如果不想折腾*_wo属性,继续用master_password的话,一定要把状态文件的安全做到位:
- 用AWS S3存状态文件,开启KMS加密
- 给S3桶和状态文件设置严格的IAM权限,只让必要的Terraform执行角色能访问
- 开启S3版本控制,防止误删或篡改状态文件
内容的提问来源于stack exchange,提问作者Peter Halverson

