Terraform mappe multilivello

Aug 27 2020

Ricevo errori sul "piano terraform" quando provo a utilizzare una mappa multilivello (3+ livelli) e non riesco a mettere il dito sul problema esatto. L'errore: "Il valore specificato non è valido per la variabile "secgroups": elemento "bastione": l'attributo "direzione" è obbligatorio." Il mio variable.tf è corretto come mappato su secgroups.auto.tf? ports_min e ports_max saranno un elenco completo di porte da aprire per il nome del gruppo di sicurezza.

Versioni:

Terraform v0.13.0
+ provider registry.terraform.io/hashicorp/local v1.4.0
+ provider registry.terraform.io/hashicorp/null v2.1.2
+ provider registry.terraform.io/hashicorp/tls v2.2.0
+ provider registry.terraform.io/terraform-providers/openstack v1.26.0

variabili.tf

variable "secgroups" {
  type = map(object({
  direction = (map(object({
    protocols = (map(object({
      name              = string
      description       = string
      ports_min         = list(number)
      ports_max         = list(number)
      remote_ip_prefix  = list(string)
      remote_group_id   = list(string)
      security_group_id = list(string)
    })))
  })))
 }))
}

secgroups.auto.tfvars (solo uno snippet)

  ssh_from_bastion = {
    ingress = {
      tcp = {
        ports_min         = [22]
        ports_max         = [22]
        remote_group_id   = ["openstack_networking_secgroup_v2.bastion.id"]
        security_group_id = ["openstack_networking_secgroup_v2.bastion.id"]
      },
      udp = {
        ports_min         = [0]
        ports_max         = [0]
        remote_group_id   = ["openstack_networking_secgroup_v2.bastion.id"]
        security_group_id = ["openstack_networking_secgroup_v2.bastion.id"]
      }
    },
    egress = {
      tcp = {
        ports_min         = [0]
        ports_max         = [0]
        remote_ip_prefix  = ["0.0.0.0/0"]
        security_group_id = ["openstack_networking_secgroup_v2.bastion.id"]
      },
      udp = {
        ports_min         = [0]
        ports_max         = [0]
        remote_ip_prefix  = ["0.0.0.0/0"]
        security_group_id = ["openstack_networking_secgroup_v2.bastion.id"]
      }
    }
  },

Principale.tf

 locals {
   security_groups = flatten({
   for secgroup_name,direction in var.secgroups : {
     name        = each.secgroup_name
     description = "Security group for ${each.secgroup_name}"
       for protocol,config in each.direction : {
         direction = each.direction
         protocol  = each.protocol
           for config_value in config : {
             ports_min         = each.config_value.ports_min
             ports_max         = each.config_value.ports_max
             remote_ip_prefix  = each.config_value.remote_ip_prefix
             security_group_id = each.config_value.security_group_id
           }
         }
       }
   })
 } 

Risposte

Marcin Aug 27 2020 at 08:00

Ci sono diversi problemi con le tue definizioni.

Supponendo che il tuo pieno secgroups.auto.tfvarssia:

secgroups = {
 ssh_from_bastion = {
    ingress = {
      tcp = {
        ports_min         = [22]
        ports_max         = [22]
        remote_group_id   = ["openstack_networking_secgroup_v2.bastion.id"]
        security_group_id = ["openstack_networking_secgroup_v2.bastion.id"]
      },
      udp = {
        ports_min         = [0]
        ports_max         = [0]
        remote_group_id   = ["openstack_networking_secgroup_v2.bastion.id"]
        security_group_id = ["openstack_networking_secgroup_v2.bastion.id"]
      }
    },
    egress = {
      tcp = {
        ports_min         = [0]
        ports_max         = [0]
        remote_ip_prefix  = ["0.0.0.0/0"]
        security_group_id = ["openstack_networking_secgroup_v2.bastion.id"]
      },
      udp = {
        ports_min         = [0]
        ports_max         = [0]
        remote_ip_prefix  = ["0.0.0.0/0"]
        security_group_id = ["openstack_networking_secgroup_v2.bastion.id"]
      }
    }
  }
}

La definizione corrispondente dovrebbe essere:

variable "secgroups" {
  type = map(map(map(object({
        ports_min         = list(number)
        ports_max         = list(number)
        security_group_id = list(string)
      }))))
}

Ma l'oggetto interno sopra eliminerà tutti gli attributi extra, ad esempio remote_ip_prefixperché i tuoi oggetti sono incoerenti. Tuttavia, poiché ingress, egress, tcpe udpsembrano essere coerenti, potresti probabilmente utilizzare quanto segue:

variable "secgroups" {
  type = map(object({
            ingress = object({tcp = map(any), udp = map(any)})
            egress =  object({tcp = map(any), udp = map(any)})
        }))
}

Come ultima risorsa, se nulla è coerente, puoi utilizzare:

variable "secgroups" {
  type = map(map(map(map(any))))
}

Aggiornamento: output di prova

output "test" {
  value = var.secgroups.ssh_from_bastion.ingress.tcp.ports_min
}