gpt4 book ai didi

Terraform:使用 for_each 创建多个实例

转载 作者:行者123 更新时间:2023-12-01 21:45:39 25 4
gpt4 key购买 nike

我希望在 Terraform 的 count/for_each 函数方面得到一些帮助。

目标是将多个 json 文件(当前两个)读入 map 列表,并创建具有特定命名约定的特定数量的 aws_instances。

配置

cat test_service_1.json
{
"instance_name": "front",
"instance_count": "3",
"instance_type": "t2.micro",
"subnet_type": "private",
"elb": "yes",
"data_volume": ["no", "0"]
}

cat test_service_2.json
{
"instance_name": "back",
"instance_count": "3",
"instance_type": "t2.micro",
"subnet_type": "private",
"elb": "yes",
"data_volume": ["no", "0"]
}

cat main.tf
locals {
services = [jsondecode(file("${path.module}/test_service_1.json")),
jsondecode(file("${path.module}/test_service_2.json"))]
}

resource "aws_instance" "test_instance" {
ami = "amzn-ami-hvm-2018.03.0.20200206.0-x86_64-gp2"
instance_type = "t2.micro"
tags = merge(
map("Name", "prod-app-?"),
map("env", "prod")
)
}

最终我希望代码遍历两个 json 文件并创建:

prod-front-1
prod-front-2
prod-front-3
prod-back-1
prod-back-2
prod-back-3

我可以用 [count.index +1] 做到这一点,但我不知道如何遍历多个 map 。

最佳答案

当使用资源 for_each 时,我们的任务始终是编写一个表达式来生成一个 map ,其中每个我们想要创建的实例都有一个元素。在这种情况下,这似乎是一个表达式,可以将包含计数的单个对象扩展为计数中给定数字的多个对象。

我们可以在 Terraform 中使用的构建 block 是:

让我们一步一步来。我们将从您现有的表达式开始,从 JSON 文件加载数据:

locals {
services = [
jsondecode(file("${path.module}/test_service_1.json")),
jsondecode(file("${path.module}/test_service_2.json")),
]
}

这个表达式的结果是一个对象列表,每个文件一个。接下来,我们将把这些对象中的每一个扩展成一个对象列表,其长度在 instance_count 中给出:

locals {
service_instance_groups = [
for svc in local.services : [
for i in range(1, svc.instance_count+1) : {
instance_name = "${svc.instance_name}-${i}"
instance_type = svc.instance_type
subnet_type = svc.subnet_type
elb = svc.elb
data_volume = svc.data_volume
}
]
]
}

这个结果是一个对象列表的列表,由于将值 i 连接到末尾,每个对象都将具有唯一的 instance_name 值。

要使用 for_each 虽然我们需要一个平面集合,每个实例一个元素,所以我们将使用 flatten 函数来实现这一点:

locals {
service_instances = flatten(local.service_instance_groups)
}

现在我们又有了一个对象列表,但有六个元素(两个输入对象各三个)而不是两个。

最后,我们需要将该列表投影为 map ,其键是 Terraform 将用于跟踪实例的唯一标识符。我通常更喜欢直接在 for_each 参数中执行最后一步,因为这个结果特定于该用例,不太可能在模块中的其他任何地方使用:

resource "aws_instance" "test_instance" {
for_each = {
for inst in local.service_instances : inst.instance_name => inst
}

ami = "amzn-ami-hvm-2018.03.0.20200206.0-x86_64-gp2"
instance_type = each.value.instance_type
tags = {
Name = "prod-app-${each.key}"
Env = "prod"
}
}

这应该会导致 Terraform 计划创建地址类似于 aws_instance.test_instance["front-2"] 的实例。

我分别写了上面的每个步骤来解释每个步骤的实现,但在实践中我通常会在一个单个表达式,因为该中间 service_instance_groups 结果不太可能在别处重用。将所有这些组合成一个示例,然后:

locals {
services = [
jsondecode(file("${path.module}/test_service_1.json")),
jsondecode(file("${path.module}/test_service_2.json")),
]
service_instances = flatten([
for svc in local.services : [
for i in range(1, svc.instance_count+1) : {
instance_name = "${svc.instance_name}-${i}"
instance_type = svc.instance_type
subnet_type = svc.subnet_type
elb = svc.elb
data_volume = svc.data_volume
}
]
])
}

resource "aws_instance" "test_instance" {
for_each = {
for inst in local.service_instances : inst.instance_name => inst
}

ami = "amzn-ami-hvm-2018.03.0.20200206.0-x86_64-gp2"
instance_type = each.value.instance_type
tags = {
Name = "prod-app-${each.key}"
Env = "prod"
}
}

作为奖励,除了您在这里询问的内容之外,如果您为这些 JSON 文件提供系统名称并将它们组合到一个子目录中,那么您可以使用 Terraform's fileset function以自动获取稍后添加到该目录中的任何新文件,而无需更改 Terraform 配置。例如:

locals {
services = [
for fn in fileset("${path.module}", "services/*.json") :
jsondecode(file("${path.module}/${fn}"))
]
}

上面的代码将生成一个列表,其中包含 services 子目录中名称以 .json 结尾的每个文件的对象。

关于Terraform:使用 for_each 创建多个实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60716579/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com