
Comment refuser une taille d'instance trop petite avant le plan ? Comment
vérifier qu'une instance a bien reçu une IP après sa création ? Comment
surveiller la santé d'un service sans faire échouer l'apply ? Ces trois
questions appellent trois dispositifs différents, et Terraform en a un
quatrième par-dessus.
Terraform offre quatre niveaux de validation : la validation de
variable, la precondition, la postcondition et le bloc check.
Ils se ressemblent à l'écrit, mais un seul n'arrête pas l'opération. Savoir
lequel mettre où, et lequel bloque, est le sous-objectif d'examen 2a, et
c'est le trou le plus fréquent en préparation.
Tous les exemples de ce guide ont été exécutés sur Terraform v1.15.4 avant publication, résultats compris.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »validation: refuser une entrée avant le plan, y compris en croisant deux variablesprecondition: refuser une hypothèse avant la création (et le piège decount = 0)postcondition: refuser un résultat après la création, avecselfcheck: surveiller sans bloquer, et pourquoi il fait sortirplan -detailed-exitcodeen 2- Le tableau
checksdeshow -json: le verdict des quatre en un appel machine
Prérequis
Section intitulée « Prérequis »- Terraform 1.15 ou plus récent (installer Terraform).
Les blocs
checkdatent de 1.5, la validation croisée de 1.9. - Variables et lifecycle maîtrisés (variables, lifecycle).
validation : refuser une entrée avant le plan
Section intitulée « validation : refuser une entrée avant le plan »Dans un bloc variable, un bloc validation refuse une valeur d'entrée avant
tout plan. Sa condition est un booléen, son error_message s'affiche quand
elle est fausse. Depuis Terraform 1.9, la condition peut référencer une
autre variable, ce qu'on appelle une validation croisée :
variable "replicas" { type = number validation { condition = var.replicas <= var.replicas_max error_message = "replicas ne peut pas dépasser replicas_max." }}precondition : refuser une hypothèse avant la création
Section intitulée « precondition : refuser une hypothèse avant la création »Dans un bloc lifecycle, une precondition vérifie une hypothèse au moment
du plan, avant que la ressource soit créée :
resource "aws_instance" "web" { # ... lifecycle { precondition { condition = var.instance_type != "t2.nano" error_message = "t2.nano est trop petit pour ce rôle." } }}postcondition : refuser un résultat après la création
Section intitulée « postcondition : refuser un résultat après la création »Toujours dans lifecycle, une postcondition vérifie le résultat après la
création. Elle est la seule à disposer de self, l'objet créé :
postcondition { condition = self.private_ip != "" error_message = "L'instance n'a pas reçu d'IP privée." }C'est la différence de fond avec la précondition : la précondition garde une entrée (connue au plan), la postcondition garde une sortie (connue après l'apply). Les deux bloquent l'opération quand leur condition est fausse.
check : surveiller sans bloquer
Section intitulée « check : surveiller sans bloquer »Un bloc check de niveau racine surveille un invariant. Contrairement aux
trois précédents, un assert en échec n'arrête pas l'apply : il produit un
avertissement, pas une erreur. C'est fait pour des vérifications de
continuité de service qu'on ne veut pas bloquantes.
check "sante_http" { data "http" "home" { url = "https://${aws_instance.web.public_ip}/" } assert { condition = data.http.home.status_code == 200 error_message = "La home ne répond pas 200." }}Un check peut contenir un data source scopé, comme ici. Point à connaître :
ce data source est relu à chaque plan, donc il apparaît toujours en action
read dans le plan.
Lire le verdict des quatre : le tableau checks
Section intitulée « Lire le verdict des quatre : le tableau checks »terraform show -json expose un tableau checks de premier niveau. Chaque
entrée porte un address.kind et un status :
address.kind | Dispositif | Bloque ? |
|---|---|---|
var | validation de variable | oui |
resource | precondition / postcondition | oui |
output_value | precondition d'un output | oui |
check | bloc check | non, avertit |
Un check en échec porte son message dans instances[].problems[].message.
C'est la preuve machine des quatre niveaux, sans jamais lire une sortie
humaine :
terraform show -json | jq '.checks[] | {kind: .address.kind, status}'Le dernier piège : validate ne joue pas ces conditions
Section intitulée « Le dernier piège : validate ne joue pas ces conditions »terraform validate contrôle la forme de la configuration, pas les
valeurs. Il ne connaît pas les valeurs des variables, donc il rend valid: true sur une entrée que le plan refusera :
terraform validate -json # valid: true, error_count: 0terraform plan -var replicas=99 # Error: Invalid value for variablevalidate est un contrôle de syntaxe et de références, utile en pré-commit ; il
ne remplace jamais un plan pour valider les valeurs.
À retenir
Section intitulée « À retenir »validation(variable) refuse une entrée avant le plan ; depuis 1.9 elle peut croiser deux variables.preconditiongarde une hypothèse avant création, mais n'est pas évaluée sicount = 0.postconditiongarde un résultat après création ; seule elle aself.- Ces trois-là bloquent ; le bloc
checkavertit seulement. - Un
checkavec data source scopé fait sortirplan -detailed-exitcodeen 2 sur une config pourtant convergée. - Le tableau
checksdeshow -jsondonne le verdict des quatre ;validatene joue aucune de ces conditions.
FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »Les questions ci-dessous portent sur ce qui bloque le plus souvent : quel niveau choisir, pourquoi une précondition ne se déclenche pas, et pourquoi un plan annonce des changements sur une configuration stable. Chaque réponse donne la commande de vérification correspondante.
Réponse courte
La precondition garde une hypothèse avant création (au plan) ; la postcondition garde un résultat après création.Détail
Les deux vivent danslifecycle et bloquent l'opération. La différence clé : seule la postcondition dispose de self, l'objet créé, donc elle seule peut vérifier un attribut connu après l'apply (une IP, un ARN).Réponse courte
La ressource est sans doute encount = 0 : sans instance, la precondition n'est jamais évaluée.Détail
Une precondition est jouée par instance. Surcount = 0 (ou un for_each vide), il n'y a aucune instance, donc rien à évaluer, et le plan passe. Ne comptez pas sur une precondition pour garder un invariant quand le nombre d'instances peut tomber à zéro.Réponse courte
Non. Unassert en échec dans un bloc check produit un avertissement, pas une erreur : l'apply réussit.Détail
C'est ce qui distingue lecheck de validation, precondition et postcondition, qui bloquent tous. Le check sert à surveiller un invariant (santé HTTP, capacité) sans faire échouer le déploiement. Son verdict apparaît en status: fail dans le tableau checks de show -json.Réponse courte
Un data source scopé dans uncheck est relu à chaque plan : ce read fait sortir -detailed-exitcode en 2.Détail
terraform plan -detailed-exitcode # rend 2, config pourtant convergée
Le plan JSON le confirme : la seule entrée non no-op de resource_changes est le data source du check, en action read. Une CI qui décide sur ce code croira toujours qu'il reste des changements.Réponse courte
Non.validate contrôle la forme, pas les valeurs : il rend valid: true sur une entrée que le plan refusera.Détail
terraform validate -json # valid: true
terraform plan -var replicas=99 # Error: Invalid value for variable
validate ne connaît pas les valeurs des variables, donc il ne joue ni les validation, ni les pré/postconditions. Utile en pré-commit, jamais en remplacement d'un plan.Réponse courte
Oui, depuis Terraform 1.9 : c'est la validation croisée.Détail
variable "taille_lot" {
type = number
validation {
condition = var.taille_lot <= var.taille_max
error_message = "taille_lot ne peut pas dépasser taille_max."
}
}
Avant 1.9, une validation ne pouvait référencer que sa propre variable. La validation croisée déplace vers l'entrée des contrôles qui demandaient avant une precondition.Réponse courte
Le tableauchecks de terraform show -json.Détail
terraform show -json | jq '.checks[] | {kind: .address.kind, status}'
Chaque entrée porte un address.kind (var, resource, output_value, check) et un status (pass/fail). Un check en échec porte son message dans instances[].problems[].message. C'est la preuve machine des quatre niveaux, sans lire de sortie humaine.Pour aller plus loin
Section intitulée « Pour aller plus loin »- Le bloc lifecycle : ou vivent
preconditionetpostcondition, avec les autres garde-fous de la ressource. - Les outputs Terraform : la
preconditiond'un output, pour garantir une sortie de module. - Tester un module Terraform : ce que
terraform testprouve, la ou une condition ne suffit plus. - Custom conditions : la reference officielle : les quatre niveaux et leurs messages.
- Format JSON du plan : le tableau
checks, verdict machine des quatre niveaux.