Deux ingénieurs lancent terraform apply sur la même stack à quelques secondes d’intervalle. Les deux lisent le state, les deux planifient dessus, les deux le réécrivent. La seconde écriture écrase la première en silence, et le state décrit désormais une infrastructure qui ne correspond plus au réel. Personne n’a vu passer d’erreur. C’est précisément le scénario contre lequel le state local n’a jamais été conçu pour protéger, et la raison d’être des backends distants.
Pourquoi le state local ne tient pas en équipe
Terraform conserve un fichier de state qui associe chaque ressource de votre configuration à un objet réel chez votre fournisseur, avec les métadonnées et l’ordre des dépendances. Par défaut, ce fichier est terraform.tfstate, posé sur le poste de la dernière personne à avoir lancé un apply.
Ce défaut lâche dès qu’une deuxième personne arrive :
- Il n’est pas partagé. Le state de votre collègue ignore les ressources que vous avez créées. Son prochain plan veut les recréer.
- Il n’a aucun verrou. Rien n’empêche deux apply de tourner en même temps, et le cycle lecture-modification-écriture de Terraform n’est pas atomique entre deux machines.
- Il expose des secrets. Le state stocke les attributs des ressources en clair, y compris les mots de passe et clés générés, désormais éparpillés sur des postes et parfois dans Git.
Un backend distant règle les trois d’un coup : une copie qui fait autorité, un stockage à accès contrôlé, et un mécanisme de verrouillage.
Ce qu’apporte un backend distant
Le backend se déclare dans le bloc terraform. Le backend S3 sert de référence courante :
terraform {
backend "s3" {
bucket = "acme-tfstate"
key = "platform/prod/terraform.tfstate"
region = "eu-west-1"
encrypt = true
use_lockfile = true # verrouillage S3 natif, Terraform 1.10+
}
}
Le state vit maintenant dans S3, versionné et chiffré, et le verrou est géré par un fichier de lock écrit à côté, via les écritures conditionnelles de S3. Historiquement ce verrou reposait sur une table DynamoDB dédiée (clé primaire LockID) ; cette approche fonctionne toujours, mais le lock file natif a supprimé cette dépendance supplémentaire pour les nouvelles stacks.
Les autres backends déclinent la même idée autrement. Le backend GCS intègre le verrouillage nativement, à partir des numéros de génération d’objet, sans table annexe. HCP Terraform (ex-Terraform Cloud) prend en charge le stockage du state et le verrou pour vous, et ajoute par-dessus l’historique des runs et les politiques d’accès.
Comment le verrou fonctionne réellement
Avant toute opération susceptible d’écrire le state (apply, et plan quand il rafraîchit), Terraform acquiert un verrou. Ce verrou est un petit enregistrement qui retient qui l’a pris, pour quelle opération, quand, et sous quel identifiant. Tant qu’il est détenu, un second run refuse de démarrer :
$ terraform apply
╷
│ Error: Error acquiring the state lock
│
│ Lock Info:
│ ID: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
│ Operation: OperationTypeApply
│ Who: alice@build-runner-07
│ Created: 2026-07-24 09:14:22 UTC
╵
C’est le système qui fait exactement son travail. Le verrou est relâché automatiquement à la fin de l’opération, qu’elle réussisse ou qu’elle échoue proprement.
Le piège classique : le verrou fantôme
Le verrou n’est relâché proprement que si Terraform va jusqu’au bout de sa libération. Coupez le processus avec deux Ctrl-C, perdez le réseau en plein apply, ou faites évincer un runner de CI, et le verrou reste détenu sans propriétaire derrière. Chaque run suivant échoue alors sur le même Error acquiring the state lock, ce qui bloque toute l’équipe.
La porte de sortie est force-unlock, avec l’ID lu dans l’erreur :
terraform force-unlock a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
À manier comme une arme chargée. Forcer le déverrouillage alors qu’un apply tourne encore réellement réintroduit exactement la corruption par écriture concurrente que le verrou existait pour empêcher. Assurez-vous que le processus propriétaire est bien mort (vérifiez le job CI, interrogez la personne nommée dans Who) avant de déverrouiller. Ne câblez jamais force-unlock dans une automatisation, et ne dégainez pas -lock=false pour faire disparaître l’erreur : cela désactive le verrouillage pour de vrais apply.
La dérive, et pourquoi plan est votre détecteur
Le state part du principe qu’il est seul à modifier votre infrastructure. Quand quelqu’un édite une ressource à la main dans une console, ou qu’un autre outil y touche, le réel s’écarte de ce que le state enregistre. Cet écart, c’est la dérive (drift).
terraform plan est le détecteur : il rafraîchit le state à partir des attributs réels du fournisseur, puis compare la configuration, le state et la réalité. Un run en mode rafraîchissement seul isole la dérive pure des changements que vous voulez vraiment appliquer :
terraform plan -refresh-only
Si ce plan montre des différences sans aucune modification de configuration de votre côté, c’est que quelque chose a bougé hors de Terraform. Réconciliez de façon délibérée, soit en important et en codifiant le changement, soit en appliquant pour ramener le réel vers l’état déclaré. Faire tourner la détection de dérive de façon planifiée, comme une étape de votre pipeline CI/CD, transforme une divergence silencieuse en alerte, plutôt qu’en surprise lors du prochain déploiement sans rapport.
À retenir
Le state local est un défaut mono-joueur. Dès qu’une seconde personne ou un pipeline touche la même infrastructure, il faut un backend distant pour une source de vérité partagée et un verrou pour sérialiser les écritures. Le verrouillage n’est pas un obstacle à contourner : le flag -lock=false et le force-unlock réflexe sont la façon dont les équipes corrompent leur state. Et comme le state se croit seul acteur, planifiez plan pour attraper la dérive avant qu’elle ne vous attrape. C’est un socle de l’industrialisation de votre CI/CD posée sur de l’infrastructure as code qui tient sous charge.