You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 03:49:54