Cartes multi-niveaux Terraform

Aug 27 2020

Je reçois des erreurs sur le "plan de terraform" lorsque j'essaie d'utiliser une carte à plusieurs niveaux (3+ niveaux) et je n'arrive pas à mettre le doigt sur le problème exact. L'erreur : "La valeur donnée n'est pas valide pour la variable "secgroups": l'élément "bastion": l'attribut "direction" est requis." Mes variables.tf sont-elles correctes telles qu'elles sont mappées sur secgroups.auto.tf ? ports_min et ports_max seront une liste exhaustive de ports à ouvrir pour le nom du groupe de sécurité.

Versions :

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

variables.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 (juste un extrait)

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

Main.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
           }
         }
       }
   })
 } 

Réponses

Marcin Aug 27 2020 at 08:00

Il y a plusieurs problèmes avec vos définitions.

En supposant que votre plein secgroups.auto.tfvarsest :

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 définition correspondante devrait être :

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

Mais l'objet interne ci-dessus supprimera tous les attributs supplémentaires, par exemple remote_ip_prefixparce que vos objets sont incohérents. Cependant, étant donné que , ingresset semblent cohérents, vous pourriez probablement utiliser ce qui suit :egresstcpudp

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

En tant que dernière ressource, si rien n'est cohérent, vous pouvez utiliser :

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

Mise à jour : sortie de test

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