Le state est le fichier le plus important de ton projet Terraform — et aussi le plus dangereux. C’est la mémoire qui connecte ton code à l’infrastructure réelle. Sans lui, Terraform est aveugle. Ce chapitre explique comment il fonctionne, pourquoi il faut le protéger, et comment le gérer correctement en équipe.
Le state, c’est quoi exactement ?
Le fichier terraform.tfstate est un JSON qui mappe chaque ressource déclarée dans ton code HCL à l’objet réel dans le cloud. Quand tu lances terraform plan, voici ce qui se passe :
- Terraform lit ta configuration
.tf - Il lit le state actuel
- Il interroge l’API du provider pour l’état réel
- Il calcule le diff entre les trois
Sans ce mapping, Terraform ne peut pas savoir si un serveur existe déjà, s’il a changé, ou s’il faut le créer. Le state contient aussi des données sensibles : IDs, adresses IP, et parfois des mots de passe ou clés API.
💡 Analogie : le state, c’est la base de données interne de Terraform. Ton code .tf décrit l’état souhaité. L’API cloud est l’état réel. Le state est le pont entre les deux.
State local vs state distant
Le backend local — simple mais risqué
Par défaut, le state vit dans un fichier terraform.tfstate à la racine du projet. Zéro configuration, parfait pour un lab solo.
Les problèmes arrivent vite :
- Pas de partage — ton collègue n’a pas le fichier
- Pas de locking — deux
applysimultanés = corruption garantie - Pas de backup — un
rmaccidentel = tu perds la correspondance code ↔ infra
Le backend distant — le standard en équipe
Un backend distant stocke le state sur un service partagé. Le plus courant est S3 avec DynamoDB pour le locking :
terraform {
backend "s3" {
bucket = "mon-projet-terraform-state"
key = "prod/infra/terraform.tfstate"
region = "eu-west-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
La table DynamoDB assure le locking — un seul apply à la fois peut écrire dans le state. Crée-la avec hash_key = "LockID" et billing_mode = "PAY_PER_REQUEST".
🔥 Le problème de la poule et l’œuf : le bucket S3 et la table DynamoDB doivent exister avant de configurer le backend. Crée-les dans un projet Terraform séparé avec un state local. C’est la convention standard.
Pour migrer du local au distant, lance terraform init -migrate-state après avoir ajouté le bloc backend. Terraform copie le state et supprime le fichier local.
Le locking : éviter les catastrophes
Le locking empêche deux personnes d’écrire dans le state en même temps. Sans lui, un apply peut écraser les changements d’un autre — corruption silencieuse.
Quand le lock est actif et qu’un second apply tente de s’exécuter :
Error: Error acquiring the state lock
Lock Info:
ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Operation: OperationTypeApply
Who: dany@workstation
Created: 2026-03-19 22:00:00 UTC
En cas de crash qui laisse un lock orphelin :
terraform force-unlock a1b2c3d4-e5f6-7890-abcd-ef1234567890
⚠️ N’utilise force-unlock que si tu es certain qu’aucune opération n’est en cours. Sinon, tu crées exactement le problème que le lock est censé prévenir.
Workspaces : un state par environnement
Les workspaces permettent d’avoir plusieurs states distincts avec la même configuration. Chaque workspace a son propre fichier state.
Les commandes essentielles : terraform workspace new staging (créer), workspace select prod (basculer), workspace list (lister).
Dans ta config, terraform.workspace retourne le nom du workspace actif :
locals {
config = {
dev = { type = "cx22", count = 1 }
staging = { type = "cx22", count = 2 }
prod = { type = "cx32", count = 3 }
}
env = local.config[terraform.workspace]
}
resource "hcloud_server" "app" {
count = local.env.count
name = "app-${terraform.workspace}-${count.index}"
server_type = local.env.type
image = "ubuntu-24.04"
}
💡 Quand utiliser les workspaces : environnements quasi identiques avec peu de divergence. Quand ne PAS les utiliser : environnements très différents (la prod a un WAF, pas le dev) — préfère des répertoires séparés.
Import : intégrer des ressources existantes
Tu as des serveurs créés manuellement ou par un autre outil ? terraform import les rattache au state sans les recréer.
Méthode classique (CLI)
- Écris le bloc
resourcecorrespondant dans ta config - Importe avec la commande CLI
- Ajuste les attributs jusqu’à ce que
planmontre zéro changement
terraform import hcloud_server.legacy 58712345
terraform plan # Ajuste main.tf jusqu'à "No changes"
Méthode moderne (Terraform 1.5+)
Le bloc import est déclaratif et peut générer le code automatiquement :
import {
to = hcloud_server.legacy
id = "58712345"
}
# Puis : terraform plan -generate-config-out=generated.tf
# Terraform génère le bloc resource correspondant
Tu n’as plus qu’à nettoyer le code généré et l’intégrer. C’est un gain de temps énorme pour importer 50 ressources d’un coup.
Cas entreprise : migration d’une infra manuelle
Une startup a 15 serveurs créés à la main sur Hetzner. Aucune documentation, des configs qui driftent, et un SRE qui est le seul à connaître l’infra (le fameux “bus factor” de 1).
Le plan de migration :
- Inventaire — lister toutes les ressources via l’API Hetzner (
hcloud server list) - Squelette — écrire les blocs
resourcecorrespondants - Import —
terraform importpour chaque ressource - Ajustement — boucle
plan→ correction jusqu’à zéro diff - Backend distant — migrer le state vers S3 avec locking
- CI/CD — pipeline GitLab qui exécute
plansur chaque MR
🎯 Résultat : en 2 semaines, toute l’infra est sous Terraform. Le bus factor passe de 1 à 5. Les changements sont reviewés, versionnés, et réversibles. Le SRE dort mieux.
Les pièges du state
⚠️ Commiter le state dans Git — Le piège numéro 1. Le state contient des données sensibles. Ajoute toujours *.tfstate et *.tfstate.backup au .gitignore.
⚠️ Modifier le state à la main — Ne modifie jamais le JSON directement. Utilise les commandes dédiées :
terraform state mv hcloud_server.old hcloud_server.new # Renommer
terraform state rm hcloud_server.temp # Retirer du state
terraform state pull > backup.tfstate # Backup
terraform state push backup.tfstate # Restaurer
⚠️ Drift silencieux — Si quelqu’un modifie une ressource dans la console web, le state ne le sait pas. Au prochain plan, Terraform peut écraser le changement manuel. Lance terraform plan régulièrement (ou en cron) pour détecter les drifts.
⚠️ State partagé sans locking — Sur un backend S3 compatible (Infomaniak, MinIO) sans DynamoDB, il n’y a pas de locking natif. Compense avec une CI/CD qui sérialise les apply (un seul pipeline à la fois).
Résumé
Le state est le cœur de Terraform — le fichier qui connecte ton code à la réalité :
- State local = fichier
.tfstateen local, OK pour le prototypage solo - State distant = S3, GCS, Terraform Cloud — le standard en équipe
- Locking = DynamoDB ou équivalent — empêche les écritures concurrentes
- Workspaces = un state par environnement avec la même config
- Import = rattacher des ressources existantes au state (CLI ou bloc
import) - Règles d’or = ne jamais commiter le state, ne jamais le modifier à la main, toujours activer le locking
Dans le prochain chapitre, on structure le code avec les modules Terraform — le mécanisme de réutilisation qui transforme des scripts en bibliothèque d’infrastructure.
🖥️ Pratique sur ton propre serveur
Pour suivre Terraform en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.
Contenu réservé aux abonnés
Ce chapitre fait partie de la formation complète. Abonne-toi pour débloquer tous les contenus.
Débloquer pour 29 CHF/moisLe chapitre 1 de chaque formation est gratuit.
Série pas encore débloquée
Termine la série prérequise d'abord pour accéder à ce contenu.
Aller à la série prérequiseSérie : Terraform
5 / 6Sur cette page
Articles liés
Init, Plan, Apply : le workflow Terraform
Maîtrise le workflow Terraform complet : init, plan, apply, destroy. Puis découvre les resources, data sources, le state et la structure de projet.
Pourquoi l'IaC ? Introduction à Terraform
Découvre l'Infrastructure as Code, installe Terraform, apprends la syntaxe HCL, les providers et réalise ton premier déploiement sur Hetzner.
Variables et types de données
Maîtrise les variables d'entrée Terraform, les différents types (string, number, list, map, object), l'assignation de valeurs et les outputs.