Como for_each através de uma lista (objetos) no Terraform 0.12

11

tiver uma pergunta, preciso implantar uma lista de instâncias de computação do GCP, como faço o loop for_each através dos "vms" nesta lista de objetos como este

    "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"
        ]
      }
    ]    
}
TheShadow2707
fonte
Terraform usa uma linguagem declarativa, consequentemente, já não existem if, forou whiledeclarações. Você pode simular isso, no entanto, até certo ponto, em alguns casos, graças à countpropriedade Leia este excelente artigo sobre como reproduzir condições ou loops com o Terraform : blog.gruntwork.io/…
arvymetal
A partir do Terraform 0.12.6, os recursos podem usar for_eachblocos: 0.12.6 Changelog . Para o que vale a pena de, se / condicionais mais tem sido em Terraform por um par de versões agora: docs Terraform: expressões condicionais
Carlo Mencarelli

Respostas:

6

No Terraform 0.12, você pode usar o for_each com módulos como o seguinte:

modules / 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)
      }))
    }

modules / 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
}

servers.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"]
        },
    }
}

Obviamente, você pode adicionar quantas variáveis ​​forem necessárias e usá-las no módulo.

Tamás Juhász
fonte
É bom, mas a variável é fornecida dinamicamente a cada vez.
TheShadow2707
Variáveis ​​comuns podem ser adicionadas separadamente como variáveis ​​e reutilizadas como var.variable. Os únicos devem ser declarados separadamente de qualquer maneira.
Tamás Juhász
5

Parece que eu achei o que fazer. Se você não passar os mapas de mapas, mas a lista de mapas, poderá usar esse código

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)}"
  }
}

Ele criará o número real de instâncias e quando você remover, por exemplo, um dos três do meio (se você criar três :)), o terraform removerá o que solicitamos.

TheShadow2707
fonte
Isso funciona bem. A lista (objeto)) é convertida em um mapa que pode ser usado como o valor for_each. Basta selecionar uma chave (por exemplo, nome do host) que seja única
svinther
1

O uso do for_eachbloco é bastante novo e não há muita documentação. Algumas das melhores informações vêm de seu anúncio no blog: https://www.hashicorp.com/blog/hashicorp-terraform-0-12-preview-for-and-for-each/

Verifique também a seção Dynamic Blocks da documentação: https://www.terraform.io/docs/configuration/expressions.html#dynamic-blocks

Pelo que parece o seu exemplo, você precisa ter um conjunto de valores para cada instância criada para ter um mapa de mapas:

Abaixo está um exemplo que eu criei usando o 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"
    }
}

Saída do plano Terraform:

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.
Carlo Mencarelli
fonte
Então, eu preciso converter a lista em mapa, pois eu entendo que mude o índice da lista para chave e ele será o mapa.
TheShadow2707