Module 5 — Le langage de configuration
Durée indicative du cours : 2 h 30
Durée indicative de l’atelier : atelier du jour 3, partagé avec le module 6
Durée totale (cours + atelier) : le jour 3 réunit les modules 5 et 6 et l’atelier du jour
Ce module couvre les objectifs 4a à 4g. Le fil reste la fiche d’environnement. Le langage sert à ne plus recopier le nom, le suffixe et les contrôles à la main.
Ressource et data source
Un bloc resource décrit un objet que Terraform crée, modifie et détruit. Un bloc data lit un objet qui existe déjà, sans le prendre en charge.
resource "local_file" "fiche_environnement" {
filename = "${path.module}/fiche-environnement.txt"
content = "nom=${var.nom}\n"
}
data "local_file" "fiche_lue" {
filename = local_file.fiche_environnement.filename
}
resource écrit. data relit. Si vous n’avez pas besoin de gérer l’objet, vous le lisez. Si vous devez pouvoir le changer ou le détruire depuis cette configuration, c’est une ressource.
Références
Une ressource s’adresse par TYPE.NOM. Un attribut s’adresse par TYPE.NOM.attribut. Dans l’exemple, local_file.fiche_environnement.filename crée une dépendance : Terraform écrit la fiche avant de la relire.
path.module est le dossier du module courant. path.root est le dossier où vous lancez Terraform. path.cwd est le dossier du shell. Au jour 1, les trois coïncident. Dès qu’un module est appelé, path.module désigne le dossier du module, pas celui de l’appelant.
Variables, sorties et types
Une variable est une entrée. Un output est une sortie. Un local est une valeur calculée dans la configuration, pour ne pas répéter une expression.
variable "nom" {
type = string
description = "Nom de l'environnement"
default = "lab-equipe"
}
variable "etiquettes" {
type = map(string)
default = {
equipe = "plateforme"
}
}
locals {
ligne_nom = "nom=${var.nom}"
}
output "chemin_fiche" {
value = local_file.fiche_environnement.filename
description = "Emplacement de la fiche"
}
Les types simples sont string, number et bool. Les types composés de l’examen sont ceux-ci.
| Type | Rôle |
|---|---|
list(TYPE) |
Suite ordonnée, doublons possibles, index numérique |
set(TYPE) |
Ensemble, sans doublon, sans ordre utile pour un index |
map(TYPE) |
Paires clé et valeur, clés de type string |
object({ ... }) |
Attributs nommés, types différents possibles |
tuple([ ... ]) |
Suite courte, type fixé pour chaque position |
any |
Terraform déduit le type. À éviter dans une interface partagée |
optional(TYPE, DEFAUT) rend un attribut d’objet facultatif. Vous vous en servez dans le type d’une variable de module, quand l’appelant ne fournit pas chaque champ.
Les valeurs arrivent par défaut dans le bloc, par -var, par un fichier terraform.tfvars, ou par une variable d’environnement TF_VAR_nom. Un fichier .tfvars de secrets ne se commite pas. Le module 7 précise ce point.
terraform output lit les sorties du state. terraform output -raw NOM donne la valeur seule. terraform output -json donne le tout en JSON, pour un script.
Expressions et fonctions
Une expression calcule une valeur au lieu de la recopier.
"nom=${var.nom}"interpole.condition ? valeur_si_vrai : valeur_si_fauxchoisit.[for nom in var.noms : upper(nom)]transforme une liste.{for cle, valeur in var.etiquettes : cle => upper(valeur)}transforme une map.local_file.fiche[*].filenameest un splat : la liste des attributs.
Les fonctions à savoir utiliser sur ce fil :
join(separateur, liste)assemble un textelength(valeur)comptecontains(liste, valeur)teste une présencelookup(map, cle, defaut)lit une map avec une valeur de replimerge(map_a, map_b)fusionne, la map de droite l’emporte sur les clés communesfile(chemin)lit un fichier au moment du plantemplatefile(chemin, variables)fait la même chose avec des interpolations
file et templatefile lisent au moment où Terraform construit le plan. Le contenu entre dans la configuration. Ce n’est pas un secret à protéger : le module 7 explique pourquoi.
Plusieurs objets de même forme
count et for_each créent plusieurs ressources à partir d’un même bloc. C’est le cas des expressions dynamiques de l’examen.
count produit un nombre d’exemplaires, identifiés par count.index.
resource "local_file" "copie" {
count = 2
filename = "${path.module}/copie-${count.index}.txt"
content = "copie ${count.index}\n"
}
L’adresse dans le state est local_file.copie[0], puis [1]. Si vous retirez l’élément du milieu d’une liste qui sert d’index, Terraform décale les suivants et peut remplacer des objets qui n’avaient pas changé.
for_each identifie chaque objet par une clé stable. Vous le préférez dès que l’objet a un nom.
locals {
lignes = {
nom = var.nom
suffixe = random_id.suffixe.hex
}
}
resource "local_file" "ligne" {
for_each = local.lignes
filename = "${path.module}/${each.key}.txt"
content = "${each.value}\n"
}
L’adresse est local_file.ligne["nom"]. Retirer la clé nom ne renumérote pas suffixe.
each.key et each.value existent seulement dans un bloc for_each. count.index existe seulement dans un bloc count.
Un bloc imbriqué se répète avec dynamic. Le nom du bloc dynamique est le nom du bloc imbriqué attendu par le provider. for_each parcourt la collection. content écrit un exemplaire.
Dans content, le nom de l’itérateur est le nom du bloc, ici ligne, et non each. Vous utilisez dynamic quand le provider demande plusieurs blocs du même type à l’intérieur d’une ressource. Pour plusieurs ressources entières, vous restez sur count ou for_each.
Dépendances et cycle de vie
Terraform déduit le graphe dès qu’une ressource cite l’attribut d’une autre. depends_on ajoute un lien que cette citation ne peut pas exprimer, par exemple un ordre avec un objet dont vous n’utilisez aucun attribut.
resource "local_file" "recap" {
filename = "${path.module}/recap.txt"
content = "fiche-prete\n"
depends_on = [local_file.fiche_environnement]
}
Vous ne mettez pas depends_on partout. Une référence d’attribut suffit dans le cas courant, et elle documente pourquoi le lien existe.
Le bloc lifecycle change la manière dont Terraform remplace ou protège la ressource.
create_before_destroy = truecrée le remplaçant avant de détruire l’ancien. Utile quand un nom doit rester disponible pendant le remplacement.prevent_destroy = truefait échouer un plan qui détruirait la ressource.ignore_changes = [content]ignore les changements de cet argument vus dans la réalité ou dans la configuration, selon le cas que vous avez choisi de figer.replace_triggered_by = [random_id.suffixe]force le remplacement quand la valeur citée change.
create_before_destroy est le cas que l’examen cite avec depends_on. Les trois autres sont dans la même page de documentation. Vous devez pouvoir dire à quoi chacun sert.
Conditions custom
Une condition custom refuse une valeur avant qu’elle ne devienne un objet réel, ou après.
validation porte sur une variable. Elle est évaluée quand la variable est lue.
variable "nom" {
type = string
validation {
condition = can(regex("^[a-z0-9-]+$", var.nom))
error_message = "Le nom ne contient que des minuscules, des chiffres et des tirets."
}
}
precondition porte sur une ressource, un data source ou un output. Elle doit être vraie avant l’action. postcondition doit être vraie après. Le bloc check porte une règle sur l’ensemble de la configuration. Un check en échec est un avertissement. Il ne bloque pas l’apply, sauf si vous en faites une règle d’équipe dans HCP Terraform.
resource "local_file" "fiche_environnement" {
filename = "${path.module}/fiche-environnement.txt"
content = local.contenu
lifecycle {
precondition {
condition = length(var.nom) > 2
error_message = "Le nom doit dépasser deux caractères."
}
}
}
validation protège une entrée. precondition protège une action. postcondition contrôle un résultat. check surveille une règle plus large, sans le même blocage.
Ce que l’examen veut pour ce module
| Objectif | Vous devez pouvoir l’expliquer |
|---|---|
| 4a | resource gère un objet. data le lit |
| 4b | TYPE.NOM.attribut relie deux objets et crée la dépendance |
| 4c | Variable en entrée, output en sortie, local pour une valeur calculée |
| 4d | list, set, map, object, tuple, et optional dans un objet |
| 4e | Interpolation, condition, for, count, for_each, dynamic, et les fonctions join, lookup, merge, length, contains, file |
| 4f | Le graphe vient des références. depends_on ajoute un lien. create_before_destroy crée avant de détruire |
| 4g | validation, precondition, postcondition et check ne bloquent pas au même moment |
Points à retenir
- Vous citez un attribut pour exprimer une dépendance.
- Une interface de module, au module suivant, sera faite de variables et d’outputs.
fmtetvalidatene remplacent pas unevalidationmétier.ignore_changesfige un argument. Vous l’utilisez seulement quand vous savez quel écart vous choisissez d’ignorer.
Suite
Poursuivre avec le module 6 — Les modules.