寻求更优雅的Terraform templatefile S3桶策略实现方案
Terraform S3桶策略Principal配置优化方案
我正在用Terraform通过模板创建S3桶策略,实现动态传入Principal名称的功能。目前功能已实现,但觉得用flatten和临时变量的写法不够简洁自然,而且不想给所有桶都添加policy_principals_read = ["ignore"]、policy_principals_write = ["ignore"]这类冗余配置。
我已经用try函数优化了逻辑,不用强制在bucket_defaults里包含这两个配置,但还是想找到更优雅的方式替代flatten([try(settings.policy_principals_read, ["ignore"]), "ignore"])这段代码。
优化方案1:用coalescelist替代try+flatten
Terraform的coalescelist函数能直接返回第一个非空的列表,刚好适配我们需要默认值的场景。搭配concat处理默认的"ignore",可以大幅简化代码:
buckets = merge({ for bucket_name, settings in local.bucket_defaults : bucket_name => merge(settings, { policy = templatefile("policies/${settings.policy_name}.json.tmpl", { principals_read = setunion([ for principal in concat(coalescelist(settings.policy_principals_read, []), ["ignore"]) : local.principals[principal] ]...) principals_write = setunion([ for principal in concat(coalescelist(settings.policy_principals_write, []), ["ignore"]) : local.principals[principal] ]...) principals = setunion([ for principal in concat(coalescelist(settings.policy_principals, []), ["ignore"]) : local.principals[principal] ]...) bucket_name = bucket_name }) }) })
说明
coalescelist(settings.policy_principals_read, []):如果settings.policy_principals_read不存在或为空,直接返回空列表,替代原来的try逻辑concat(..., ["ignore"]):把默认的"ignore"追加到列表末尾,不用再嵌套列表后调用flatten,逻辑更直观
优化方案2:提取辅助局部函数,复用逻辑
如果多个地方都要处理Principal列表的解析,可以把重复逻辑封装成局部函数,让代码更简洁易读:
locals { # 原有的principals和bucket_defaults保持不变... # 封装Principal解析逻辑:自动处理空值并添加默认ignore resolve_principals = func(principal_list) { return setunion([ for p in concat(coalescelist(principal_list, []), ["ignore"]) : local.principals[p] ]...) } buckets = merge({ for bucket_name, settings in local.bucket_defaults : bucket_name => merge(settings, { policy = templatefile("policies/${settings.policy_name}.json.tmpl", { principals_read = local.resolve_principals(settings.policy_principals_read) principals_write = local.resolve_principals(settings.policy_principals_write) principals = local.resolve_principals(settings.policy_principals) bucket_name = bucket_name }) }) }) }
说明
- 利用Terraform 0.14+支持的局部函数特性,把重复的循环、concat、setunion逻辑抽离出来
- 调用时直接传入对应的配置字段,代码复用性更强,也更容易维护
优化方案3:给bucket_defaults设置全局默认值
如果大部分桶的policy_principals_read/policy_principals_write都是默认的["ignore"],可以在遍历前给每个桶配置合并全局默认值,彻底消除冗余配置:
locals { # 原有的principals保持不变... bucket_defaults = { server-data = { policy_name = "allow_read_only" policy_principals = [ "p-ro-roles", "n-prod", "n-staging" ] # 删掉冗余的policy_principals_read和write配置 } client-data = { policy_name = "allow_read_write" policy_principals = [] policy_principals_read = ["p-ro-roles"] policy_principals_write = [ "n-prod", "n-staging" ] } } buckets = merge({ for bucket_name, settings in local.bucket_defaults : bucket_name => merge( # 全局默认配置:没有自定义字段时用这个值 { policy_principals_read = ["ignore"] policy_principals_write = ["ignore"] policy_principals = [] }, settings, { policy = templatefile("policies/${settings.policy_name}.json.tmpl", { principals_read = setunion([ for principal in settings.policy_principals_read : local.principals[principal] ]...) principals_write = setunion([ for principal in settings.policy_principals_write : local.principals[principal] ]...) principals = setunion([ for principal in settings.policy_principals : local.principals[principal] ]...) bucket_name = bucket_name }) } ) }) }
说明
- 用
merge把全局默认配置和每个桶的自定义配置合并,自定义配置会覆盖默认值 - 所有桶都不用再写
["ignore"]这类冗余配置,只有需要自定义的桶才添加对应字段 - 代码里不再需要
try或coalescelist,直接使用合并后的配置字段即可
内容的提问来源于stack exchange,提问作者Suraj
相关产品推荐
相关产品推荐

