Aller au contenu
Infrastructure as Code medium

Sauvegarder et restaurer le state Terraform

30 min de lecture

logo terraform

Un state perdu, tronqué ou amputé ne détruit rien : l'infrastructure continue de tourner, ce sont les enregistrements qui manquent. La réparation consiste donc à remettre le bon state, jamais à relancer un apply qui recréerait ce qui existe déjà. Ce guide s'adresse à qui doit mener cette réparation : sauvegarder avec terraform state pull, choisir la bonne sauvegarde, restaurer avec terraform state push, et prouver que l'opération a réussi autrement qu'en lisant une sortie à l'œil.

Toutes les affirmations mesurables de ce guide, messages d'erreur, codes de sortie et valeurs de serial, ont été rejouées sur Terraform v1.15.4 avec le provider hashicorp/local, sur un state local.

  • Sauvegarder un state, et ce que la sauvegarde ne contient pas
  • Reconnaître les deux filets que Terraform pose de lui-même
  • Restaurer avec state push, et la vraie portée de -force
  • Choisir entre plusieurs sauvegardes candidates
  • Prouver la réussite par le code de sortie, pas par la lecture
  • Terraform 1.11 ou plus récent (installer Terraform).
  • Savoir ce que contient un state, en particulier les champs serial et lineage : comprendre le state les définit, ce guide les met en œuvre.
  • Sur un backend partagé, savoir lever un verrou resté en place : verrouiller le state. La procédure officielle de reprise place terraform force-unlock en étape 1, avant toute sauvegarde, parce qu'une restauration fait souvent suite à un apply interrompu.

Les exemples reposent sur une configuration jetable, sans cloud :

terraform {
# Version epinglee : les messages d'erreur cites plus bas sont ceux de la
# branche 1.15.x, ils ont evolue au fil des versions.
required_version = ">= 1.7"
required_providers {
local = { source = "hashicorp/local", version = ">= 2.5" }
}
}
# Un fichier sur le disque : il rend toute recreation immediatement visible.
resource "local_file" "note" {
filename = "${path.root}/note.txt"
content = "note\n"
}

Le state, et lui seul. terraform state pull écrit sur la sortie standard le state courant, quel que soit le backend : c'est le geste de sauvegarde de référence, y compris sur un backend distant où le fichier n'est pas accessible directement.

Fenêtre de terminal
terraform state pull > sauvegarde-2026-07-28.json

Deux, et ils ne couvrent pas les mêmes opérations. C'est la source de malentendu la plus fréquente sur ce sujet.

Le premier est terraform.tfstate.backup, propre au backend local. Il contient l'état d'avant la dernière écriture, n'existe pas après le premier apply, et n'est écrit qu'une fois par exécution.

Le second passe inaperçu. Une commande de manipulation du state ne touche pas à ce fichier : elle dépose un fichier horodaté.

Fenêtre de terminal
terraform state rm local_file.note
ls terraform.tfstate*
terraform.tfstate
terraform.tfstate.1785262538.backup

Vérifié sur 1.15.4 : après ce state rm, terraform.tfstate.backup n'a pas bougé, et c'est le fichier horodaté qui porte l'état complet d'avant la commande. Le filet qui vous sauvera après une manipulation de state n'est donc pas celui que vous surveillez. Comme ces fichiers sont presque toujours gitignorés, un nettoyage de répertoire les emporte sans bruit.

L'option -backup redirige ce fichier vers le chemin de votre choix, ce qui permet de l'archiver ailleurs que dans le répertoire de travail :

Fenêtre de terminal
terraform state rm -backup=filet-avant-retrait.json local_file.note

La commande fait le chemin inverse du pull et écrit un state complet à la place du courant. Elle oppose deux refus, et ces refus sont exactement ce qui protège l'infrastructure.

Le garde-fou du serial empêche d'écraser des écritures plus récentes :

Failed to write state: cannot import state with serial 3 over newer state with serial 5

Le garde-fou du lineage empêche d'écraser un projet avec le state d'un autre :

Failed to write state: cannot import state with lineage "7f3c1a90-2b48-4e5d-9c17-6ad0e2b4915f" over unrelated state with lineage "88580bd8-9e4c-4a0f-b71e-2f0d3c6ab145"

Un troisième cas se produit quand deux states portent le même serial mais un contenu différent, typiquement après une édition manuelle :

Failed to write state: cannot overwrite existing state with serial 1 with a different state that has the same serial

Ces trois refus sortent en code 1. À l'inverse, pousser exactement ce qu'un pull vient de rendre est accepté en silence, en code 0 : lineage, serial et contenu coïncident, il n'y a rien à signaler.

Pourquoi -force est plus dangereux qu'il n'en a l'air

Section intitulée « Pourquoi -force est plus dangereux qu'il n'en a l'air »

Parce qu'il neutralise les deux garde-fous d'un coup, pas seulement celui du serial. La documentation le formule sans détour, et l'avertissement est explicite : « Both of these safety checks can be disabled with the -force flag. This is not recommended. »

Fenêtre de terminal
terraform state push -force sauvegarde-2026-07-28.json

Une restauration légitime tombe forcément sur le refus du serial, puisqu'elle remet un état antérieur : -force est donc inévitable, et c'est précisément ce qui rend l'option piégeuse. Il n'y a ni confirmation ni question, et le contrôle de lineage tombe en même temps. Vérifiez le lineage avant de forcer, jamais après :

Fenêtre de terminal
terraform state pull | jq -r .lineage
jq -r .lineage sauvegarde-2026-07-28.json

Deux valeurs identiques, et la sauvegarde appartient bien à ce projet. Deux valeurs différentes, et vous vous apprêtiez à écraser une infrastructure avec le state d'une autre.

Celui de la sauvegarde, augmenté de un. Pas celui du state remplacé, ce qui surprend et porte des conséquences concrètes. Mesuré sur 1.15.4 :

Fichierserial
le state courant, avant15
la sauvegarde poussée1
le state courant, après2

La bonne nouvelle, c'est qu'un push incrémente toujours : le state restauré porte donc un serial supérieur à celui de la sauvegarde. Une copie de fichier posée à la main par-dessus terraform.tfstate laisserait le serial identique : le contournement se lit dans le fichier longtemps après.

La mauvaise concerne les backends partagés. Votre state restauré peut porter un serial très inférieur à celui que d'autres postes ont déjà vu. Un collègue dont le cache connaît le serial 15 se retrouve devant un state à 2, et son prochain push légitime écrasera votre réparation. Prévenez l'équipe avant que quiconque ne pousse.

Par le code de sortie, jamais par la lecture d'un message. Deux contrôles suffisent, et ils s'automatisent :

  1. Le plan ne propose plus rien

    Fenêtre de terminal
    terraform plan -detailed-exitcode
    echo $?

    0 signifie convergence, 2 un changement en attente, 1 une erreur. Un code 2 après une restauration est le symptôme d'une sauvegarde périmée : ses attributs ne décrivent plus le réel, et Terraform propose un remplacement, local_file.note must be replaced.

  2. Le state porte les ressources attendues

    Fenêtre de terminal
    terraform show -json | jq '[.values.root_module.resources[]
    | select(.mode == "managed") | .address]'

    Le comptage se fait sur la sortie structurée, jamais sur state list, dont la mise en forme s'adresse à un humain.

terraform state push lit aussi son entrée sur stdin quand on lui passe un tiret, ce qui autorise une réparation chirurgicale en une ligne :

Fenêtre de terminal
terraform state pull | jq '.serial = 42' | terraform state push -

La documentation précise que ces données sont « loaded completely into memory and verified prior to being written » : la vérification a lieu avant l'écriture. Deux précautions accompagnent cette forme. Le fichier doit être en UTF-8 sans BOM, cause numéro un des restaurations qui échouent sous Windows, où il faut passer par Set-Content. Et sur HCP Terraform ou le backend remote, l'option -ignore-remote-version existe pour passer outre le contrôle de version de l'espace de travail.

Le réflexe qui évite la majorité de ces situations : photographier le state avant toute écriture, puisqu'un push ne se rejoue pas.

SymptômeCause probableSolution
cannot import state with serial N over newer state with serial MLa sauvegarde est antérieure au state courantNormal pour une restauration : vérifier le lineage, puis -force
cannot import state with lineage ... over unrelated stateLa sauvegarde vient d'un autre projetNe pas forcer : chercher la sauvegarde du bon lineage
cannot overwrite existing state with serial N with a different state that has the same serialDeux states au même serial, au contenu différentRepartir d'un state pull frais plutôt que d'un fichier édité
Le plan sort en 2 après la restaurationSauvegarde périméeReprendre une sauvegarde postérieure au dernier apply
Le state restauré porte un serial très basComportement normal du pushPrévenir l'équipe avant tout nouveau push
Aucun terraform.tfstate.backup après un state rmLes commandes state écrivent un fichier horodatéChercher terraform.tfstate.<epoch>.backup
La restauration échoue sous WindowsFichier encodé en UTF-8 avec BOMRéécrire avec Set-Content, sans BOM

Le lab sauvegarder et restaurer le state remet un projet dont le state a été amputé par un state rm malencontreux, trois sauvegardes candidates dont une seule est restaurable, et une infrastructure parfaitement intacte. Les tests comparent lineage et serial, exigent la photo préalable de l'état endommagé, et vérifient la date de modification des fichiers : le raccourci terraform apply, qui recrée l'objet manquant au lieu de réparer le state, y échoue. Il se joue hors ligne.

  • terraform state pull sauvegarde le state sur tous les backends, mais son champ terraform_version est réécrit à la version locale.
  • Le backup terraform.tfstate.backup est local, absent après le premier apply, et non mis à jour par les commandes state.
  • Ces commandes déposent un backup horodaté, terraform.tfstate.<epoch>.backup, que -backup=chemin redirige. Mesuré sur 1.15.4, -backup=- ne le désactive pas.
  • state push refuse un serial plus ancien, un lineage étranger, et un contenu différent à serial égal. Les trois sortent en code 1.
  • -force neutralise les deux garde-fous, lineage compris : contrôlez ce champ avant de forcer.
  • Après un push, le serial vaut celui de la sauvegarde plus un. Un serial inchangé signe une copie de fichier, pas une restauration.
  • La preuve d'une restauration réussie est plan -detailed-exitcode à 0, doublée d'un comptage sur terraform show -json.
  • terraform state push - lit le state sur stdin, en UTF-8 sans BOM.

Les questions ci-dessous portent sur ce qui bloque le plus souvent : le push refusé, le choix entre plusieurs sauvegardes, et la portée exacte de -force.

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