Aller au contenu
Infrastructure as Code medium

Comprendre le state Terraform : rôle, contenu, secrets et dérive

15 min de lecture

logo terraform

Le state Terraform est le fichier qui relie votre code au monde réel. Sans lui, Terraform ne sait plus quelles ressources il gère et tente de tout recréer. Mais le state fait plus qu'une simple table de correspondance, et c'est là que sont les pièges : il garde les secrets en clair, il ne détecte une dérive que s'il a rafraîchi le monde réel juste avant, et il se protège contre certaines réécritures par deux champs, serial et lineage.

Toutes les affirmations mesurables de ce guide (valeurs de serial, présence du backup, codes de sortie, messages de terraform state push) ont été rejouées sur Terraform v1.15.4, avec hashicorp/local et hashicorp/random, sur un state local.

  • Les trois raisons d'être du state : mapping, dépendances, cache
  • Le contenu du state, serial et lineage compris
  • Les secrets en clair : ce que sensitive masque, ce qu'il ne cache pas
  • La dérive : pourquoi elle ne se voit qu'avec un refresh
  • Les commandes d'écriture du state et les garde-fous

La doc officielle donne trois rôles au state, là où on n'en retient souvent qu'un :

  1. Le mapping : associer chaque adresse (random_password.db) à l'objet réel.
  2. Les métadonnées : mémoriser les dépendances entre ressources. C'est ce qui donne l'ordre de destruction quand une ressource a déjà disparu du code, « the order cannot be determined from the configuration alone ».
  3. La performance : le state sert de cache d'attributs. Sur une grosse infrastructure, -refresh=false et -target accélèrent les opérations, au prix d'un state potentiellement périmé.

Le state est un JSON nommé terraform.tfstate, au format version: 4. Il porte à la racine version, terraform_version, serial, lineage, outputs, resources et check_results. Chaque instance de ressource porte en plus schema_version, sensitive_attributes, dependencies et private.

  • serial est un compteur d'écritures du state, pas d'apply. Terraform réécrit le state plusieurs fois par opération : sur une configuration à deux ressources, le premier apply laisse serial: 3, l'apply suivant qui modifie une ressource laisse serial: 6.
  • lineage est l'UUID de la lignée du state. Il a un usage bien réel (voir les garde-fous plus bas), ce n'est pas un identifiant décoratif.

Le backup terraform.tfstate.backup n'existe pas après le premier apply : il n'est créé qu'à la première réécriture du state, et porte alors le serial précédent (3 quand le courant vaut 6).

C'est le malentendu le plus dangereux. Marquer une valeur sensitive ne la retire pas du state : « Terraform stores values with the sensitive argument in both state and plan files, and anyone who can access those files can access your sensitive values ».

Pire, l'outil entretient une fausse confiance. Vérifié sur 1.15.4 :

Fenêtre de terminal
terraform state show random_password.db # result = (sensitive value)
terraform show -json | jq '...result' # le mot de passe EN CLAIR

terraform state show masque la valeur, alors que le fichier de state et terraform show -json la contiennent en clair. Le même attribut figure dans sensitive_attributes de l'instance : la marque existe, la protection non.

C'est le trou le plus grave pour un profil Professional (sous-objectif 1e). Terraform ne surveille pas votre infrastructure : il ne voit une dérive (un changement fait hors Terraform) que lorsqu'il rafraîchit le state en relisant le monde réel, ce qu'un plan fait par défaut. Vérifié sur 1.15.4, après modification d'un fichier géré hors Terraform :

CommandeCode de sortieCe que ça prouve
terraform plan -detailed-exitcode2la dérive est vue (refresh)
terraform plan -refresh=false -detailed-exitcode0le state seul ne voit rien

La détection vient donc du rafraîchissement, pas du state. Une CI qui court avec -refresh=false pour aller vite peut rater une dérive.

Pour rafraîchir sans planifier de changements, terraform apply -refresh-only demande une confirmation avant de réécrire le state. La commande terraform refresh est dépréciée au profit de -refresh-only.

On ne lit et n'écrit le state que par les commandes terraform state, jamais en éditant le fichier :

CommandeRôle
state list, state showlister, inspecter (show masque les secrets)
state pull, state pushexporter, importer le state complet
state mvrenommer une ressource sans destruction
state rmretirer une ressource du state (sans la détruire)

state mv est ce qui permet de renommer une ressource sans destruction : vérifié, un mv donne un plan vide, là où un simple renommage dans le code planifie 1 à créer et 1 à détruire (préférez cependant le bloc moved, déclaratif).

Contrairement à une idée reçue, le serial n'est pas le mécanisme de concurrence : la protection contre deux opérations simultanées est un verrou posé par le backend (Error acquiring the state lock), pas une comparaison de serial. Le garde-fou du serial vit ailleurs, sur terraform state push :

  • pousser un state de serial inférieur est refusé : cannot import state with serial 3 over newer state with serial 6 (sortie 1, state courant intact) ;
  • pousser un state de lineage différent est refusé, avec un message distinct : cannot import state with lineage "..." over unrelated state with lineage "...". C'est la protection contre l'écrasement par le state d'un autre projet.

Le verrouillage est automatique sur toute opération susceptible d'écrire le state, y compris avec le backend local (« using system APIs »). -lock=false est déconseillé ; terraform force-unlock existe pour un verrou resté bloqué, et affiche un avertissement.

Si le state est perdu, Terraform veut tout recréer. La réponse moderne n'est pas le terraform import en ligne de commande, « manuel et fastidieux », mais le bloc import déclaratif (Terraform 1.5+), appliqué par terraform apply :

import {
to = aws_s3_bucket.data
id = "mon-bucket-existant"
}

Combiné à terraform plan -generate-config-out=bucket.tf, il génère même la configuration à partir de l'objet importé. C'est la réponse actuelle au state perdu, et elle est revue à l'examen.

Le state ne se commite jamais. La règle usuelle couvre les states, backups et states de workspaces d'une seule ligne :

*.tfstate
*.tfstate.*
.terraform/

À l'inverse, .terraform.lock.hcl doit être versionné : c'est lui qui fige les versions de providers pour toute l'équipe.

  • Le state a trois rôles : mapping, dépendances, cache d'attributs.
  • serial compte les écritures, pas les apply ; le backup n'apparaît qu'à la première réécriture.
  • Un secret marqué sensitive reste en clair dans le state ; state show le masque, mais le fichier et show -json le révèlent. Seul ephemeral l'exclut.
  • La dérive ne se détecte qu'avec un refresh (plan = 2, -refresh=false = 0).
  • Les garde-fous vivent sur state push : serial inférieur et lineage différent sont refusés. La concurrence, elle, est gérée par le verrou.

Les questions ci-dessous portent sur les pièges du state : le secret qu'on croit protégé, la dérive qu'on croit détectée, et le rôle réel du serial. Chaque réponse donne la vérification correspondante.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn