Terraform:创建多个实例,每个实例都有for_

Terraform:创建多个实例,每个实例都有for_,terraform,terraform-provider-aws,Terraform,Terraform Provider Aws,我想要一些关于Terraform的count/for_每个函数的帮助 目标是将多个json文件读入映射列表,并使用特定的命名约定创建特定数量的aws_实例 配置 cat test_service_1.json { "instance_name": "front", "instance_count": "3", "instance_type": "t2.micro", "subnet_type": "private", "elb": "y

我想要一些关于Terraform的count/for_每个函数的帮助

目标是将多个json文件读入映射列表,并使用特定的命名约定创建特定数量的aws_实例

配置

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]来实现这一点,但我不知道如何循环使用多个映射。

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

我们可以在Terraform中使用的构建块包括:

将一个集合值投影到另一个集合值。 生成给定计数的整数序列。 将多个嵌套列表转换为单个平面列表。 让我们一步一步来。我们将从您现有的表达式开始,从JSON文件加载数据:

locals {
  services = [
    jsondecode(file("${path.module}/test_service_1.json")),
    jsondecode(file("${path.module}/test_service_2.json")),
  ]
}
此表达式的结果是对象列表,每个文件一个。接下来,我们将这些对象中的每一个展开为一个对象列表,其长度在实例_count中给出:

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

要使用for_each,我们需要一个平面集合,每个实例有一个元素,因此我们将使用flatte函数来实现这一点:

locals {
  service_instances = flatten(local.service_instance_groups)
}
现在我们又有了一个对象列表,但是有六个元素,两个输入对象中的每一个都有三个元素,而不是两个

最后,我们需要将该列表投影为一个地图,其关键是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_组和service_instance步骤,因为中间service_instance_组的结果不太可能在其他地方重用。将所有这些结合到一个示例中,然后:

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配置。例如:

locals {
  services = [
    for fn in fileset("${path.module}", "services/*.json") :
    jsondecode(file("${path.module}/${fn}"))
  ]
}
上面将生成一个列表,其中包含services子目录中名称以.json结尾的每个文件的一个对象。

为每个文件使用资源时,我们的任务始终是编写一个表达式,该表达式生成一个映射,其中每个实例有一个我们想要创建的元素。在本例中,这似乎是一个表达式,它可以将包含计数的单个对象扩展为计数中给定数量的多个对象

我们可以在Terraform中使用的构建块包括:

将一个集合值投影到另一个集合值。 生成给定计数的整数序列。 将多个嵌套列表转换为单个平面列表。 让我们一步一步来。我们将从您现有的表达式开始,从JSON文件加载数据:

locals {
  services = [
    jsondecode(file("${path.module}/test_service_1.json")),
    jsondecode(file("${path.module}/test_service_2.json")),
  ]
}
此表达式的结果是对象列表,每个文件一个。接下来,我们将这些对象中的每一个展开为一个对象列表,其长度在实例_count中给出:

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

要使用for_each,我们需要一个平面集合,每个实例有一个元素,因此我们将使用flatte函数来实现这一点:

locals {
  service_instances = flatten(local.service_instance_groups)
}
现在我们又有了一个对象列表,但是有六个元素,两个输入对象中的每一个都有三个元素,而不是两个

最后,我们需要将该列表投影为一个地图,其关键是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_组和service_instance步骤,因为中间service_instance_组的结果不太可能在其他地方重用。带来 将所有内容合并为一个示例,然后:

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配置。例如:

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

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

是否有硬性要求该数据以json结尾?它是生成的吗?这似乎是terraform变量(例如TFVAR)的主要用例,所以我很好奇。是否有硬性要求这些数据使用JSON格式?它是生成的吗?这似乎是地形变量(例如TFVAR)的主要用例,所以我很好奇。