3
votes

Problèmes liés à l'itération sur une liste ou un ensemble d'éléments dans Terraform

J'essaie de lancer des instances AWS dans différents sous-réseaux créés dans des zones de disponibilité en utilisant la variable count sur une liste d'ID de sous-réseau, cela échoue avec des messages d'erreur avec des approches différentes.

J'ai essayé d'utiliser element () et d'utiliser [count .index], j'utilise TF v12.

Voici un extrait du code, veuillez ignorer les lignes avec # (commenté).

Error: Error in function call

on k8-cluster.tf line 85, in resource "aws_instance" "workers":
85:   subnet_id = "${element(data.aws_subnet_ids.subnet_list.ids, count.index)}"
|----------------
| count.index is 3
| data.aws_subnet_ids.subnet_list.ids is set of string with 4 elements

Call to function "element" failed: cannot read elements from set of string.

Ci-dessous sont deux extraits d'erreur.

Error: Invalid index

on k8-cluster.tf line 85, in resource "aws_instance" "workers":
85:   subnet_id = "${data.aws_subnet_ids.subnet_list.ids[count.index]}"
|----------------
| count.index is 2
| data.aws_subnet_ids.subnet_list.ids is set of string with 4 elements

This value does not have any indices.

Une autre erreur avec element ():

resource "aws_instance" "workers" {
#count         =   length(data.terraform_remote_state.network.outputs.public_subnet_list)
count         = length(data.aws_subnet_ids.subnet_list.ids)
instance_type = var.worker_instance_type
ami           = var.k8_ami
key_name      = aws_key_pair.ssh_key.key_name
#subnet_id     = "${data.terraform_remote_state.network.outputs.public_subnet_list[count.index]}"
subnet_id = "${element(data.aws_subnet_ids.subnet_list.ids, count.index)}"
vpc_security_group_ids = [
aws_security_group.kubernetes.id
]
}


0 commentaires

3 Réponses :


1
votes

Il vous suffit de changer le décompte comme ceci:

resource "aws_instance" "workers" {
    #count         =   "${length(data.terraform_remote_state.network.outputs.public_subnet_list)}"
    count         = "${length(data.aws_subnet_ids.subnet_list.ids)}"
    instance_type = "${var.worker_instance_type}"
    ami           = "${var.k8_ami}"
    key_name      = "${aws_key_pair.ssh_key.key_name}"
    #subnet_id     = "${data.terraform_remote_state.network.outputs.public_subnet_list[count.index]}"
    subnet_id = "${element(data.aws_subnet_ids.subnet_list.ids, count.index)}"
    vpc_security_group_ids = [
    "${aws_security_group.kubernetes.id}"
    ]
}

Et lorsque vous citez les variables, je vous suggère de le faire comme ceci:

"${var.var_name}" 


5 commentaires

Merci, mais cela donne toujours l'erreur Erreur: Erreur dans l'appel de la fonction ... L'appel de la fonction "élément" a échoué: impossible de lire les éléments du jeu de chaînes. ' EDIT: juste pour ajouter, c'est activé TF v12, pas v11. Cela pourrait être un bug?


@ mzs_47 Vous pouvez peut-être afficher les subnet_ids pour vérifier s'il obtient vraiment les ID de sous-réseau.


Oui, il contient quatre éléments, si vous avez remarqué les extraits d'erreur ci-dessus, le nombre varie et il mentionne `` est un ensemble de chaînes avec 4 éléments '', par souci de concision, je viens d'en coller un pour chaque méthode que j'ai essayée.


@ mzs_47 Je n'ai aucune expérience avec AWS. Mais d'après l'erreur que vous montrez, il semble que le count.index 1 soit OK et les après pas. Si c'est OK si vous utilisez la v11?


Malheureusement, je ne peux pas passer à la v11 atm,: (Je vais devoir prendre le temps de citer les expressions pour que cela fonctionne. Je vais essayer cela plus tard. Merci!



11
votes

Le problème racine ici est que data.aws_subnet_ids.subnet_list.ids est une valeur set , plutôt qu'une valeur list , et donc ses éléments ne sont pas dans un ordre particulier et ne sont donc pas accessibles par index numérique dans une liste.

Pour l'utiliser comme une liste, il faut décider comment ordonner les éléments. Dans ce cas, il semble que l'ordre n'est pas vraiment important car le but est simplement de créer une instance par sous-réseau, donc passer l'ensemble à la fonction sort devrait être suffisant pour les trier lexicalement: p>

resource "aws_instance" "workers" {
  # Planned for a future Terraform release; not in v0.12.0
  for_each = data.terraform_remote_state.network.outputs.public_subnet_list

  instance_type = var.worker_instance_type
  ami           = var.k8_ami
  key_name      = aws_key_pair.ssh_key.key_name
  subnet_id     = each.value
  vpc_security_group_ids = [
    aws_security_group.kubernetes.id
  ]
}

Dans une future version de Terraform (non disponible au moment de la rédaction de la v0.12.0), une nouvelle fonctionnalité for_each est prévue pour rendre cela plus simple:

resource "aws_instance" "workers" {
  count         = length(data.terraform_remote_state.network.outputs.public_subnet_list)
  instance_type = var.worker_instance_type
  ami           = var.k8_ami
  key_name      = aws_key_pair.ssh_key.key_name
  subnet_id     = sort(data.terraform_remote_state.network.outputs.public_subnet_list)[count.index]
  vpc_security_group_ids = [
    aws_security_group.kubernetes.id
  ]
}

Un avantage d'utiliser for_each une fois qu'il est implémenté (mis à part la brièveté) est qu'il indiquera également à Terraform d'identifier instances individuelles de cette ressource par la chaîne d'identifiant de sous-réseau, plutôt que par position dans la liste. Cela signifie que l'ajout de nouveaux sous-réseaux à l'avenir n'entraînera pas de "décalage" des instances ultérieures et de recréation inutile, comme ce serait le cas avec mon exemple original ci-dessus.


1 commentaires

Merci pour ça!



5
votes

Ce problème se produit car les données renvoyées sont sous la forme d'un "SET", tandis que l'élément attend l'entrée sous la forme d'une "LISTE".

Pour résoudre le problème, vous devez le convertir en liste.

subnet_id = "$ {element (tolist (data.aws_subnet_ids.subnet_list.ids), count.index)}"


0 commentaires