1-08 Boucles et expressions conditionnelles dans un langage déclaratif comme Terraform
Objectifs
- Savoir utiliser les boucles et conditions logiques dans Terraform
Les boucles
Le fait que HCL soit un langage destiné à une interprétation permet d'intégrer des appels à des boucles afin de générer par exemple plusieurs instances d'un même objet.
Ce n'est pas courant parmi les langages de configuation qui sont le plus souvent statiques, cf. XML, JSON ou YAML .
Terraform propose plusieurs constructions de bouclage, chacune destinée à être utilisée dans un scénario légèrement différent.
- Le meta paramètre
count
: boucler sur les ressources et les modules - L'expression
for_each
: boucler sur les ressources, les blocs en ligne dans une ressource et les modules - L'expression
for
: boucler sur des listes et des maps - La directive de chaînes de caractère
for
: boucler sur les listes et les maps dans une chaîne
Le meta argument count
Ce méta-argument count peut provisionner dynamiquement plusieurs instances d'un Ressource.
Il prend pour paramètre un nombre entier qui détermine le nombre d'objets désirés.
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["neo", "trinity", "morpheus"]
}
resource "aws_iam_user" "example" {
count = length(var.user_names)
name = var.user_names[count.index]
}
La syntaxe count.index
permet d'accéder à l'entier correspondant à l'itération courante, de {0...N}
.
Pour accéder à une instance d'une ressource créée avec count, utilisez la notation crochet [], car il produit logiquement une liste de ressources.
aws_iam_user.example[0]
Il existe plusieurs limitations qui limitent l'usage de count
.
Il n'est utilisable que sur des blocks de ressources, et non sur des blocs intérieurs.
Il peut introduire des erreurs en raison de l'index d'itération.
Q: Que se passe-t-il si j'enlève l'utilisateur ayant l'index 1 de la liste ?
R: La valeur de aws_iam_user.example[1]
n'est plus la même et Terraform va replanifier en fonction.
L'expression for_each
Cette expression comparable à count
itére sur des listes et des maps pour créer plusieurs copies d'une ressource.
On peut l'utiliser comme count
en tant que paramètre de la ressource, en lui fournissant une structure complexe à parcourir.
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["neo", "trinity", "morpheus"]
}
resource "aws_iam_user" "example" {
for_each = toset(var.user_names)
name = each.value
}
Quelques remarques importantes
- On utilise la fonction
toset
pour convertir le typelist
en typeset
- On utilise la syntaxe
each.value
pour accéder à la valeur courante du pointeur dans la structure. - On peut aussi utiliser
each.key
pour utiliser la clef d'unemap
- Terraform utilise la chaîne
value
d'un set ou lakey
d'une map comme index du conteneur des ressources produites
aws_iam_user.example["neo"]
aws_iam_user.example["trinity"]
aws_iam_user.example["morpheus"]
L'intérêt de for_each
est qu'il s'agit d'une expression qui peut être utilisée plus généralement.
Au contraire de count
on peut l'utiliser dans toutes sortes de blocs à condition d'utiliser la directive dynamic
.
Ici on l'utilise pour générer plusieurs tags définis dans une variable.
variable "
resource "aws_autoscaling_group" "example" {
launch_configuration = aws_launch_configuration.example.name
vpc_zone_identifier = data.aws_subnets.default.ids
target_group_arns = [aws_lb_target_group.asg.arn]
health_check_type = "ELB"
min_size = var.min_size
max_size = var.max_size
tag {
key = "Name"
value = var.cluster_name
propagate_at_launch = true
}
dynamic "tag" {
for_each = var.custom_tags
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
}
La structure complexe parcourue par une expression for_each
doit être connue à l'avance.
On ne peut donc pas utiliser une structure construire sur la base d'informations d'une ressource connues après son obtention.
L'expression for
L'expression for
est utilisée pour parcourir et traiter une structure complexe.
Son usage est assez proche de celui des compréhensions de liste en python.
Elle produit des listes et des maps
> [for k,v in [1,3,5] : "${k}:${v}"]
[
"0:1",
"1:3",
"2:5",
]
> {for k,v in ["apple","kiwi","bananas"] : v => k}
{
"apple" = 0
"bananas" = 2
"kiwi" = 1
}
On peut également filtrer les résultats avec un if
et utilliser des fonctions.
> [for k,v in ["aba","aca","ada","afa","aga"] : upper("${k}:${v}") if k % 2 == 0]
[
"0:ABA",
"2:ADA",
"4:AGA",
]
La directive de chaînes de caractère for
Elle est utile pour créer des contenus de chaînes à partir de structures complexes.
> "%{ for k,v in ["apple","kiwi","bananas"] }${k}:${v}, %{ endfor }"
"0:apple, 1:kiwi, 2:bananas, "
Utilisation des conditions
Avec count
En combinant les conditionals et count
on peut désactiver certaines ressources.
resource "aws_autoscaling_schedule" "scale_out_during_business_hours" {
count = var.enable_autoscaling ? 1 : 0
scheduled_action_name = "${var.cluster_name}-scale-out-during-business-hours"
min_size = 2
max_size = 10
desired_capacity = 10
recurrence = "0 9 * * *"
autoscaling_group_name = aws_autoscaling_group.example.name
}
On peut aussi faire des conditions inverses
resource "aws_iam_user_policy_attachment" "neo_cloudwatch_full_access" {
count = var.give_user_cloudwatch_full_access ? 1 : 0
user = aws_iam_user.the_user.name
policy_arn = aws_iam_policy.cloudwatch_full_access.arn
}
resource "aws_iam_user_policy_attachment" "neo_cloudwatch_read_only" {
count = var.give_user_cloudwatch_full_access ? 0 : 1
user = aws_iam_user.the_user.name
policy_arn = aws_iam_policy.cloudwatch_read_only.arn
}
Utiliser des conditions pour for_each
On peut transformer la structure complexe à parcourir avant de la passer à for_each
.
dynamic "tag" {
for_each = {
for key, value in var.custom_tags:
key => upper(value)
if key != "Name"
}
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
Utiliser des conditions pour la directive de chaîne for
Dans l'exemple précédent, on avait le problème classique de la chaîne terminant par une virgule.
## console
> "%{ for k,v in ["apple","kiwi","bananas"] }${k}:${v}%{ if k < length( ["apple","kiwi","bananas"]) - 1}, %{ endif}%{ endfor }"
"0:apple, 1:kiwi, 2:bananas"
Rappel des objectifs
- Savoir utiliser les boucles et conditions logiques dans Terraform