如何使用terraform for_each来分离对象的映射并获取资源的ID



在我的项目中,我有两个客户端,每个客户端都有几个数据库。如何为具有特定客户端数据库的每个客户端创建单独的故障切换组?

这是每个客户的变量:

variable "client" {
type = map (any)
default = {
# Client1 Param
"client1" = 
"databases" = {
"db1" = "true",
"db2" = "true",
"db3" = "true",
},
},
# Client2 param
"client2" = {
"databases" = {
"db1" = "true",
"db2" = "false",
},
}

创建数据库的Terraform代码:

locals {
client_databases = flatten([
for client_key, client in var.client : [
for database, enabled in client.databases : {
client_name          = client_key
database_name        = database
database_enabled     = enabled
}
]
])
}
# ## Create Client Specific Database(s)
resource "azurerm_mssql_database" "client_primary" {
for_each = { for databases in local.client_databases :
"${databases.client_name}-${databases.database_name}" => databases
if databases.database_enabled == "true"
}
name            = "${each.key}"
server_id       = azurerm_mssql_server.db["primary"].id
elastic_pool_id = azurerm_mssql_elasticpool.db["primary"].id
}

创建故障转移组的代码:

resource "azurerm_sql_failover_group" "db" {
for_each            = var.client
name                = "${lower(azurerm_resource_group.db.name)}-${each.key}"
resource_group_name = azurerm_resource_group.db.name
server_name         = azurerm_mssql_server.db["primary"].name
databases           = [for database in azurerm_mssql_database.client_primary : database.id]
partner_servers {
id = azurerm_mssql_server.db["secondary"].id
}
read_write_endpoint_failover_policy {
mode          = var.db_failover_policy_mode
grace_minutes = var.db_failover_policy_grace
}

在这种情况下,我创建了两个故障转移组,每个组都有所有的数据库:

client1-db1
client1-db2
client1-db3
client2-db1
client2-db2

如何使用客户端自己的数据库创建两个故障转移组

Failover group 1:
client1-db1
client1-db2
client1-db3
Failover group 2:
client2-db1
client2-db2

当然,所有其他需要的资源都已创建!

听起来您的目标是为每个客户端声明一个单独的故障转移组,然后让每个故障转移组包括该客户端的所有数据库。

对于每个客户端应该有一个实例的资源,您的var.client似乎已经处于适合在for_each中使用的状态,因此不需要进一步转换它以在该资源中使用,正如您已经看到的:

resource "azurerm_sql_failover_group" "db" {
for_each            = var.client
# ...

剩下的问题是为databases生成一个合适的值,该值为每个"azurerm_mssql_database"使用提供者选择的id属性。这需要复制用于识别单个数据库的相同复合实例密钥:

databases = [
for db_name, enabled in each.value.databases :
azurerm_mssql_database.client_primary["${each.key}:${db_name}"].id
if enabled == "true"
]

这里使用字符串值来表示布尔值有点令人困惑。我认为您这样做是因为在混合类型时,type = map(any)无法自动为所有元素找到合适的单个类型。

你可以通过告诉Terraform你期望的精确类型约束来避免这个问题,这将使Terraform了解如何解释给定的数据结构:

variable "client" {
type = map(
object({
databases = map(bool)
})
)
}

上述类型约束消除了关于any在该上下文中的含义的所有模糊性,从而允许为该数据结构使用更合适的类型。

如果您这样做,那么您将需要删除模块中其他位置的所有== "true"比较,因为在Terraform中true不等于"true"。以下是我在上面分享的最后一个例子的替代版本,它是为使用正确类型的布尔值而编写的:

databases = [
for db_name, enabled in each.value.databases :
azurerm_mssql_database.client_primary["${each.key}:${db_name}"].id
if enabled
]

使用精确指定的类型约束通常会使模块在未来更容易使用和维护,方法是明确模块的确切要求,并确保模块内出现的值始终是指定的类型。

最新更新