for_each的Terraform循环

时间:2020-07-12 15:19:41

标签: amazon-web-services terraform terraform-provider-aws terraform0.12+

如果您使用for_each而不是count,那么如何获得子网ID? 就我而言,我正在做类似的事情

resource "aws_instance" "k8s" {
    for_each = var.profiles

    ami = data.aws_ami.latest-ubuntu.id
    instance_type = "t2.medium"
    iam_instance_profile = "${each.value}"
    subnet_id = ??????????????
    vpc_security_group_ids = [var.security_group]
    key_name = var.keyname
    
    connection {
    type        = "ssh"
    host        = self.public_ip
    user        = "ubuntu"
    private_key = file(var.private_key_path)

  }
    tags = {
     Name = "${each.key}"
  }
}

这是因为我正在创建相似的实例,但是需要为其分配不同的实例配置文件。

理想情况下,我会做类似的事情

subnet_id = element(var.subnets, count.index )

将实例放置在不同的子网中,但我认为 count for_each 不能在同一块定义中使用。

我必须拥有子网和4个实例,并且想遍历子网,将每个实例放在一个实例中。

有什么想法吗? 谢谢。

3 个答案:

答案 0 :(得分:1)

如果100是列表,则可以使用var.profiles获取每个元素的索引。

答案 1 :(得分:0)

您可以将个人资料作为对象列表提供:

例如:

 variable "profiles" {
  type = list(object({
    name = string
    key =  string
  }))
  default = 
    [
      {name = "exemple" , key = "exemplekey" },
      {name = "exemple2" , key = "exemplekey2" }
    ]
}

像这样each.key将包含列表中元素的索引,而each.value将包含对象{name,key}

所以您的代码将是这样

resource "aws_instance" "k8s" {
    for_each = var.profiles
    ami = data.aws_ami.latest-ubuntu.id
    instance_type = "t2.medium"
    iam_instance_profile = each.value
    subnet_id = element(var.subnets , each.key)
    vpc_security_group_ids = [var.security_group]
    key_name = var.keyname
    connection {
    type        = "ssh"
    host        = self.public_ip
    user        = "ubuntu"
    private_key = file(var.private_key_path)

  }
    tags = {
     Name = each.value.key
  }

}

答案 2 :(得分:0)

使用资源for_each的一种好的通用策略是设计传递给它的数据结构,以使each.value包含资源块内所需的所有按实例数据。

在这种情况下,这意味着for_each资源的aws_instance表达式将是一个对象映射,其中每个对象都具有实例配置文件和子网ID。

实现该目标的一种方法是编写一个for expression并将var.profiles(可能是一个set(string)值)转换为一个对象映射,以获得所需的结果。例如:

resource "aws_instance" "k8s" {
  for_each = {
    # This assigns a subnet to each of the profiles
    # by first sorting them by name to produce a list
    # and then selecting subnets based on the order
    # of the sort result.
    for i, profile_name in sort(var.profiles) : profile_name => {
      iam_instance_profile = profile_name
      subnet_id            = element(var.subnets, i)
    }
  }

  ami                    = data.aws_ami.latest-ubuntu.id
  instance_type          = "t2.medium"
  iam_instance_profile   = each.value.iam_instance_profile
  subnet_id              = each.value.subnet_id
  vpc_security_group_ids = [var.security_group]
  key_name               = var.keyname
}

count.indexelement元素一起使用依赖于具有自己索引的每个项目,但是对于一组字符串却并非如此,因此在上文中,我使用sort来假设在这种情况下,只要实例最终在子网之间大致均匀分布,将哪个子网分配给每个实例就不重要了。

但是,要牢记这一点有很大的含义:如果以后在var.profiles中添加新项目,则可能会导致subnet_id重新分配现有实例,因此要求重新创建这些实例。如果您不希望如此,那么您需要以某种方式使每个配置文件对子网的选择更加明确,这可以通过将var.profiles设为list(string)而不是{{ 1}},然后记录新配置文件仅应添加到该列表的末尾,或者您也可以通过使set(string)本身成为对象的映射,将决定向上移动到模块的调用者中然后为每个配置文件指定一个子网:

var.profiles

在这种情况下,您的资源块将变得更加简单,因为变量值已经具有合适的形状:

variable "profiles" {
  type = map(object({
    subnet_id = string
  }))
}