我正在尝试创建一个通用的 dynamodb terraform 根模块,它可以处理我的组织团队中的一些用例。一些团队需要创建一个具有
PAY_PER_REQUEST
计费模式的 dynamodb,并且该数据库表还需要有一个或多个 global_secondary_index
块。我宁愿用一些条件值来处理所有这些,而不是围绕多个可以保存不同设置的地图创建一些“奇怪”的逻辑。
但是,当其他一些变量未设置或具有特定值时,Terraform 不尊重我的条件默认值。
我目前正在使用它作为我的提供程序和 terraform 版本:
hashicorp/aws v5.32.1
和 Terraform v1.4.6
例如:如果
billing_mode
为 PROVISIONED
,团队需要为 global_secondary_index
和 read_capacity
设置 write_capacity
值,而 billing_mode
为 PAY_PER_REQUEST
则无法设置这些值或者至少在我查看 0
后需要默认的 terraform.tfstate
。还有一个问题是,当 projection_type
的值为 INCLUDE
时,需要设置 non_key_attributes
,否则无法设置或需要默认为 []
,再次查看 terraform.tfstate
其他当前部署的表。
所以我在我的
variables.tf
中为此创建了一个对象映射列表。
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 = []
}
在我的
main.tf
中,我定义了一个dynamic
块,如下
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
}
}
}
这是我一直用来部署的测试模块
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"
}
]
}
在我将
global_secondary_indexes
块值设置为 any
的示例中,我将其作为我的动态块
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
}
}
当我尝试部署此模块时,我在所有尝试中都收到此错误
attributes "gsi_read_capacity", "gsi_write_capacity", "non_key_attributes", and "range_key" are required.
range_key
遵循类似的逻辑,可能不需要在所有情况下都必须进行定义。
在您当前的配置中,所有属性都是必需的
variable "test" {
type = list(object({
name = string
data = number
}))
}
terraform.tfvars
test = [
{
data = 1
name = "value"
},
{
name = "foo"
}
]
如果我们对此进行 Terraform 计划,我们会收到错误:
Planning failed. Terraform encountered an error while generating this plan.
╷
│ Error: Invalid value for input variable
│
│ on terraform.tfvars line 1:
│ 1: test = [
│ 2: {
│ 3: data = 1
│ 4: name = "value"
│ 5: },
│ 6: {
│ 7: name = "foo"
│ 8: }
│ 9: ]
│
│ The given value is not suitable for var.test
| declared at main.tf:1,1-16: element 1: attribute "data" is required.
╵
我们可以使用可选修饰符来解决这个问题:
https://developer.hashicorp.com/terraform/language/expressions/type-constraints#optical-object-type-attributes
variable "test" {
type = list(object({
name = string
data = optional(number, 0)
}))
}
output "test" {
value = var.test
}
这将向我们展示以下计划:
Changes to Outputs:
+ test = [
+ {
+ data = 1
+ name = "value"
},
+ {
+ data = 0
+ name = "foo"
},
]
You can apply this plan to save these new output values to the Terraform state,
without changing any real infrastructure.