Terraform根模块动态块条件默认值未生效问题
通用DynamoDB Terraform根模块配置问题
需求说明
要搭建适配公司多团队场景的通用DynamoDB Terraform根模块,核心要求:
- 支持
PROVISIONED和PAY_PER_REQUEST两种计费模式 - 针对GSI(全局二级索引):
- 计费模式为
PROVISIONED时,需指定read_capacity和write_capacity;PAY_PER_REQUEST时默认设为0(参考已部署表的tfstate) projection_type为INCLUDE时必须指定non_key_attributes,否则默认空数组[]range_key为可选属性,并非所有GSI都需要
- 计费模式为
当前环境
- Terraform版本:v1.4.6
- AWS Provider版本:hashicorp/aws v5.32.1
尝试过的配置
变量定义(第一种:全必填)
variable "global_secondary_indexes" { description = "A list of maps defining global secondary indexes" type = list(object({ name = string hash_key = string range_key = string projection_type = string gsi_read_capacity = number gsi_write_capacity = number non_key_attributes = list(string) })) default = [] }
变量定义(第二种:部分属性设为any支持null)
variable "global_secondary_indexes" { description = "A list of maps defining global secondary indexes" type = list(object({ name = string hash_key = string range_key = any projection_type = string gsi_read_capacity = any gsi_write_capacity = any non_key_attributes = any })) default = [] }
动态块配置(第一种)
resource "aws_dynamodb_table" "general_dynamodb_table" { name = var.name billing_mode = var.billing_mode hash_key = var.hash_key range_key = var.range_key read_capacity = var.read_capacity write_capacity = var.write_capacity dynamic "global_secondary_index" { for_each = var.global_secondary_indexes content { name = global_secondary_index.value.name hash_key = global_secondary_index.value.hash_key range_key = lookup(global_secondary_index.value, "range_key", "") projection_type = global_secondary_index.value.projection_type non_key_attributes = global_secondary_index.value.projection_type == "INCLUDE" ? lookup(global_secondary_index.value, "non_key_attributes", []) : [] read_capacity = var.billing_mode == "PROVISIONED" ? lookup(global_secondary_index.value, "gsi_read_capacity", 0) : 0 write_capacity = var.billing_mode == "PROVISIONED" ? lookup(global_secondary_index.value, "gsi_write_capacity", 0) : 0 } } }
动态块配置(第二种,适配any变量)
resource "aws_dynamodb_table" "general_dynamodb_table" { name = var.name billing_mode = var.billing_mode hash_key = var.hash_key range_key = var.range_key read_capacity = var.read_capacity write_capacity = var.write_capacity dynamic "global_secondary_index" { for_each = var.global_secondary_indexes content { name = global_secondary_index.value.name hash_key = global_secondary_index.value.hash_key range_key = lookup(global_secondary_index.value, "range_key", null) projection_type = global_secondary_index.value.projection_type non_key_attributes = global_secondary_index.value.projection_type == "INCLUDE" ? lookup(global_secondary_index.value, "non_key_attributes", []) : null read_capacity = var.billing_mode == "PROVISIONED" ? lookup(global_secondary_index.value, "gsi_read_capacity", null) : null write_capacity = var.billing_mode == "PROVISIONED" ? lookup(global_secondary_index.value, "gsi_write_capacity", null) : null } } }
测试用例
module "test_dynamodb" { source = "../dynamodb" name = "test_dynamodb" billing_mode = "PAY_PER_REQUEST" hash_key = "user_id" global_secondary_indexes = [ { name = "my-index-2" hash_key = "user_id" projection_type = "ALL" } ] }
遇到的错误
部署时始终报错:
attributes "gsi_read_capacity", "gsi_write_capacity", "non_key_attributes", and "range_key" are required.
但这些属性并非所有场景都需要定义。
解决方案
1. 正确定义可选属性的变量
Terraform 1.4+支持用optional()标记对象的可选属性,既保留类型校验,又允许属性缺失。更新variables.tf中的GSI变量:
variable "global_secondary_indexes" { description = "A list of maps defining global secondary indexes" type = list(object({ name = string hash_key = string range_key = optional(string) projection_type = string gsi_read_capacity = optional(number) gsi_write_capacity = optional(number) non_key_attributes = optional(list(string), []) })) default = [] }
- 用
optional()标记可选属性,非必填 - 给
non_key_attributes设置默认空数组,避免手动传入
2. 优化动态块,按需设置属性
在动态块中,只在需要的时候设置属性,避免传递不必要的空值或null:
resource "aws_dynamodb_table" "general_dynamodb_table" { name = var.name billing_mode = var.billing_mode hash_key = var.hash_key range_key = var.range_key # 同样建议把表的range_key也设为optional read_capacity = var.billing_mode == "PROVISIONED" ? var.read_capacity : null write_capacity = var.billing_mode == "PROVISIONED" ? var.write_capacity : null dynamic "global_secondary_index" { for_each = var.global_secondary_indexes content { name = global_secondary_index.value.name hash_key = global_secondary_index.value.hash_key projection_type = global_secondary_index.value.projection_type # 仅当range_key存在时设置 range_key = try(global_secondary_index.value.range_key, null) # 仅当projection_type为INCLUDE时设置non_key_attributes,否则不传递(用null让Terraform忽略) non_key_attributes = global_secondary_index.value.projection_type == "INCLUDE" ? global_secondary_index.value.non_key_attributes : null # 根据计费模式设置读写容量:PROVISIONED时用传入值或默认0,PAY_PER_REQUEST时不传递 read_capacity = var.billing_mode == "PROVISIONED" ? try(global_secondary_index.value.gsi_read_capacity, 0) : null write_capacity = var.billing_mode == "PROVISIONED" ? try(global_secondary_index.value.gsi_write_capacity, 0) : null } } }
- 使用
try()函数安全获取可选属性,避免属性缺失时报错 - 对于不需要的属性,传递
null让Terraform自动忽略该字段,而不是硬编码空值或0 - 表的
read_capacity和write_capacity也需根据计费模式动态设置,因为PAY_PER_REQUEST模式下不需要这些属性
3. 补充表级变量的可选配置
建议把表的range_key、read_capacity、write_capacity也设为可选,进一步提升模块通用性:
variable "range_key" { description = "Range key for the table" type = optional(string) default = null } variable "read_capacity" { description = "Read capacity units for PROVISIONED billing mode" type = optional(number) default = 0 } variable "write_capacity" { description = "Write capacity units for PROVISIONED billing mode" type = optional(number) default = 0 }
内容的提问来源于stack exchange,提问作者Thatsnotamuffin
相关产品推荐
相关产品推荐

