如何通过 Terraform 0.12 中的列表(对象)for_each

The*_*707 43 foreach loops list object terraform

我需要部署 GCP 计算实例列表。我如何通过像这样的对象列表中的“vms”循环 for_each:

    "gcp_zone": "us-central1-a",
    "image_name": "centos-cloud/centos-7",
    "vms": [
      {
        "hostname": "test1-srfe",
        "cpu": 1,
        "ram": 4,
        "hdd": 15,
        "log_drive": 300,
        "template": "Template-New",
        "service_types": [
          "sql",
          "db01",
          "db02"
        ]
      },
      {
        "hostname": "test1-second",
        "cpu": 1,
        "ram": 4,
        "hdd": 15,
        "template": "APPs-Template",
        "service_types": [
          "configs"
        ]
      }
    ]    
}
Run Code Online (Sandbox Code Playgroud)

The*_*707 47

好像我找到了该怎么做。如果你传递的不是地图的地图而是地图列表,你可以使用这样的代码

resource "google_compute_instance" "node" {
    for_each = {for vm in var.vms:  vm.hostname => vm}

    name         = "${each.value.hostname}"
    machine_type = "custom-${each.value.cpu}-${each.value.ram*1024}"
    zone         = "${var.gcp_zone}"

    boot_disk {
        initialize_params {
        image = "${var.image_name}"
        size = "${each.value.hdd}"
        }
    }

    network_interface {
        network = "${var.network}"
    }

    metadata = {
        env_id = "${var.env_id}"
        service_types = "${join(",",each.value.service_types)}"
  }
}
Run Code Online (Sandbox Code Playgroud)

它将创建实际数量的实例,当您删除例如三个中的一个(如果您创建三个:))时,terraform 将删除我们要求的内容。

  • @user3508953 - 这是一种设置资源“键”和“值”的方法,其中“vm.hostname”是键,“vm”是值。因此,在此示例中,我们通过主机名唯一标识循环中的每个资源。如果我们的列表中有多个具有相同主机名但不同 CPU 的虚拟机,那么我们可能会重写为 `"${vm.hostname}:${vm.cpu}" => vm`。这允许 terraform 通过您定义的键跟踪资源... IIRC Terraform 通过索引跟踪,否则(如果您重新排序,这可能会出现问题) (8认同)
  • 愿意分享 `: vm.hostname => vm` 在 `for_each` 行中做了什么:`for_each = {for vm in var.vms: vm.hostname => vm}`? (5认同)

Tam*_*ász 24

从 Terraform 0.12 开始,您可以将 for_each 与如下模块一起使用:

模块/google_compute_instance/variables.tf

variable "hosts" {
    type = map(object({
        hostname        = string
        cpu             = number
        ram             = number
        hdd             = number
        log_drive       = number
        template        = string 
        service_types   = list(string)
      }))
    }
Run Code Online (Sandbox Code Playgroud)

模块/google_compute_instance/main.tf

resource "google_compute_instance" "gcp_instance" {
  for_each = var.hosts

  hostname      = each.value.repository_name
  cpu           = each.value.cpu
  ram           = each.value.ram
  hdd           = each.value.hdd
  log_drive     = each.value.log_drive
  template      = each.value.template
  service_types = each.value.service_types
}
Run Code Online (Sandbox Code Playgroud)

服务器.tf

module "gcp_instances" {
    source = ./modules/google_compute_instance"

    hosts = {
        "test1-srfe" = {
            hostname        = "test1-srfe",
            cpu             = 1,
            ram             = 4,
            hdd             = 15,
            log_drive       = 300,
            template        = "Template-New",
            service_types   = ["sql", "db01", "db02"]
        },
        "test1-second" = {
            hostname        = "test1-second",
            cpu             = 1,
            ram             = 4,
            hdd             = 15,
            log_drive       = 300,
            template        = "APPs-Template",
            service_types   = ["configs"]
        },
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您可以根据需要添加任意数量的变量并在模块中使用它们。

  • 你是一个英雄。最后我从头到尾找到了一个合适的例子。 (2认同)

Neb*_*tic 11

我经常使用 Terraform 中的迭代器。这总是让我头疼。因此,我确定了两种最常见的模式,即字符串列表和对象列表。对于字符串,你可以随时使用的列表toset()和循环遍历它for_each。处理对象列表时,您需要将其转换为键为唯一值的映射。对于唯一键,您可以在列表中的所有对象中使用索引、散列或仅使用唯一值。另一种方法是在您的 terraform 配置中放置一张地图。就个人而言,我认为在您的配置中使用对象列表而不是地图看起来更简洁。除了标识地图中的不同项目外,密钥通常没有其他用途,因此可以动态构建。

字符串列表:

locals {
  ip_addresses = ["10.0.0.1", "10.0.0.2"]
}

resource "example" "example" {
  for_each   = toset(local.ip_addresses)
  ip_address = each.key
}
Run Code Online (Sandbox Code Playgroud)

对象列表:

locals {
  virtual_machines = [
    {
      ip_address = "10.0.0.1"
      name       = "vm-1"
    },
    {
      ip_address = "10.0.0.1"
      name       = "vm-1"
    }
  ]
}    

resource "example" "example" {
  for_each   = {
    for index, vm in local.virtual_machines:
    index => vm
    # OR: vm.name => vm (not always unique if names are the same)
    # OR: sha1(vm.name) vm => (not always unique if names are the same)
    # NOT: uuid() => vm (gets recreated everytime)
  }
  name       = each.value.name
  ip_address = each.value.ip_address
}
Run Code Online (Sandbox Code Playgroud)

  • 这比官方文档更好 (12认同)

byr*_*edo 9

您可以执行以下操作:

for_each = toset(keys({for i, r in var.vms:  i => r}))
cpu = var.vms[each.value]["cpu"]
Run Code Online (Sandbox Code Playgroud)

假设您有以下情况:

variable "vms" {
    type = list(object({
        hostname        = string
        cpu             = number
        ram             = number
        hdd             = number
        log_drive       = number
        template        = string 
        service_types   = list(string)
    }))
    default = [
        {
            cpu: 1
            ...
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)


Car*_*lli 5

使用这个for_each块是很新的,而且没有太多的文档。一些最好的信息来自他们的公告博客文章:https : //www.hashicorp.com/blog/hashicorp-terraform-0-12-preview-for-and-for-each/

还要确保查看其文档的动态块部分:https : //www.terraform.io/docs/configuration/expressions.html#dynamic-blocks

从您的示例看起来,您需要为每个创建的实例设置一组值,以便您拥有一张地图:

下面是我使用 Terraform 0.12.12 创建的示例:

variable "hostnames" {
    default = {
        "one" = {
            "name" = "one",
            "machine" = "n1-standard-1",
            "os" = "projects/coreos-cloud/global/images/coreos-stable-2247-5-0-v20191016",
            "zone" = "us-central1-a"
        },
        "two" = {
            "name" = "two",
            "machine" = "n1-standard-2",
            "os" = "projects/centos-cloud/global/images/centos-8-v20191018",
            "zone" = "us-central1-b"
        }
    }
}

resource "google_compute_instance" "default" {
    for_each = var.hostnames
    name         = each.value.name
    machine_type = each.value.machine
    zone         = each.value.zone

    boot_disk {
        initialize_params {
            image = each.value.os
        }
    }

    scratch_disk {
    }

    network_interface {
        network = "default"
    }
}
Run Code Online (Sandbox Code Playgroud)

地形计划输出:

Terraform will perform the following actions:

  # google_compute_instance.default["one"] will be created
  + resource "google_compute_instance" "default" {
      + can_ip_forward       = false
      + cpu_platform         = (known after apply)
      + deletion_protection  = false
      + guest_accelerator    = (known after apply)
      + id                   = (known after apply)
      + instance_id          = (known after apply)
      + label_fingerprint    = (known after apply)
      + machine_type         = "n1-standard-1"
      + metadata_fingerprint = (known after apply)
      + name                 = "one"
      + project              = (known after apply)
      + self_link            = (known after apply)
      + tags_fingerprint     = (known after apply)
      + zone                 = "us-central1-a"

      + boot_disk {
          + auto_delete                = true
          + device_name                = (known after apply)
          + disk_encryption_key_sha256 = (known after apply)
          + kms_key_self_link          = (known after apply)
          + mode                       = "READ_WRITE"
          + source                     = (known after apply)

          + initialize_params {
              + image  = "projects/coreos-cloud/global/images/coreos-stable-2247-5-0-v20191016"
              + labels = (known after apply)
              + size   = (known after apply)
              + type   = (known after apply)
            }
        }

      + network_interface {
          + address            = (known after apply)
          + name               = (known after apply)
          + network            = "default"
          + network_ip         = (known after apply)
          + subnetwork         = (known after apply)
          + subnetwork_project = (known after apply)
        }

      + scheduling {
          + automatic_restart   = (known after apply)
          + on_host_maintenance = (known after apply)
          + preemptible         = (known after apply)

          + node_affinities {
              + key      = (known after apply)
              + operator = (known after apply)
              + values   = (known after apply)
            }
        }

      + scratch_disk {
          + interface = "SCSI"
        }
    }

  # google_compute_instance.default["two"] will be created
  + resource "google_compute_instance" "default" {
      + can_ip_forward       = false
      + cpu_platform         = (known after apply)
      + deletion_protection  = false
      + guest_accelerator    = (known after apply)
      + id                   = (known after apply)
      + instance_id          = (known after apply)
      + label_fingerprint    = (known after apply)
      + machine_type         = "n1-standard-2"
      + metadata_fingerprint = (known after apply)
      + name                 = "two"
      + project              = (known after apply)
      + self_link            = (known after apply)
      + tags_fingerprint     = (known after apply)
      + zone                 = "us-central1-b"

      + boot_disk {
          + auto_delete                = true
          + device_name                = (known after apply)
          + disk_encryption_key_sha256 = (known after apply)
          + kms_key_self_link          = (known after apply)
          + mode                       = "READ_WRITE"
          + source                     = (known after apply)

          + initialize_params {
              + image  = "projects/centos-cloud/global/images/centos-8-v20191018"
              + labels = (known after apply)
              + size   = (known after apply)
              + type   = (known after apply)
            }
        }

      + network_interface {
          + address            = (known after apply)
          + name               = (known after apply)
          + network            = "default"
          + network_ip         = (known after apply)
          + subnetwork         = (known after apply)
          + subnetwork_project = (known after apply)
        }

      + scheduling {
          + automatic_restart   = (known after apply)
          + on_host_maintenance = (known after apply)
          + preemptible         = (known after apply)

          + node_affinities {
              + key      = (known after apply)
              + operator = (known after apply)
              + values   = (known after apply)
            }
        }

      + scratch_disk {
          + interface = "SCSI"
        }
    }

Plan: 2 to add, 0 to change, 0 to destroy.
Run Code Online (Sandbox Code Playgroud)