
terraform state list affiche les instances que Terraform gère, une par
ligne, sous la forme de leur adresse. C'est la commande de départ de tout
travail sur le state : cette adresse est le seul nom qu'une instance possède, et
c'est elle que réclament state show, state mv, state rm, import et
-replace. Ce guide montre comment lire ces adresses, comment filtrer
nativement, y compris dans les modules, et comment compter les ressources gérées
sans se tromper d'une unité.
Tous les comportements de ce guide ont été exécutés sur Terraform v1.15.4,
avec les providers local et random : aucun hyperviseur ni compte cloud n'est
nécessaire pour les reproduire.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Ressource contre instance, la distinction qui explique toute la sortie
- Les quatre formes d'adresses : simple,
count,for_each, dans un module - Filtrer nativement par ressource, par instance et par module
-id: retrouver une adresse quand on ne connaît que l'identifiant réel- Compter les ressources gérées sur la sortie JSON, modules inclus
Prérequis
Section intitulée « Prérequis »- Terraform 1.11 ou plus récent (installer Terraform). La branche stable courante est la 1.15.x.
- Le rôle du state (comprendre le state) et un projet déjà appliqué.
Une ressource, plusieurs instances
Section intitulée « Une ressource, plusieurs instances »C'est la distinction à poser avant tout le reste, et c'est le vocabulaire de la
documentation officielle. Une ressource est ce que vous déclarez : un bloc
resource. Une instance est ce que Terraform crée et enregistre dans le
state. Un bloc en count = 3 est donc une ressource et trois instances,
donc trois lignes dans la sortie.
C'est pour cela qu'une commande dont le nom parle de « lister les ressources » affiche en réalité des instances, et qu'un filtre sur une ressource peut rendre plusieurs lignes.
Lister toutes les instances
Section intitulée « Lister toutes les instances »Le projet de démonstration mélange les quatre cas de figure : une ressource
simple, une ressource en count, une ressource en for_each, un module, et une
data source de chaque côté.
# main.tf : les quatre formes d'adresses en un seul projetresource "random_pet" "noeud" { count = 3 length = 2}
resource "random_pet" "zone" { for_each = toset(["eu-west", "us-east"]) length = 2}
resource "local_file" "rapport" { filename = "${path.root}/rapport.txt" content = "noeuds=${length(random_pet.noeud)}\n"}
data "local_file" "lecture" { filename = local_file.rapport.filename depends_on = [local_file.rapport]}
module "reseau" { source = "./modules/reseau"}Le module reseau déclare de son côté deux random_pet en for_each, un
local_file et une data source local_file : c'est lui qui produit les adresses
préfixées, et surtout la data source dans un module, qui réserve une surprise
au moment de compter.
Après terraform apply :
terraform state listdata.local_file.lecturelocal_file.rapportrandom_pet.noeud[0]random_pet.noeud[1]random_pet.noeud[2]random_pet.zone["eu-west"]random_pet.zone["us-east"]module.reseau.data.local_file.relecturemodule.reseau.local_file.cartemodule.reseau.random_pet.sous_reseau["a"]module.reseau.random_pet.sous_reseau["b"]Onze lignes alors que le projet ne déclare que sept blocs resource ou
data : la sortie compte des instances.
Les quatre formes d'adresses
Section intitulée « Les quatre formes d'adresses »Chaque ligne est une adresse, et il n'y a que quatre formes à connaître. Les retenir, c'est pouvoir écrire une adresse de mémoire au lieu de la copier.
| Déclaration | Adresse d'une instance | Ce qui identifie l'instance |
|---|---|---|
| ressource simple | local_file.rapport | rien, elle est unique |
count | random_pet.noeud[0] | l'index, entre crochets |
for_each | random_pet.zone["eu-west"] | la clé, entre crochets et guillemets |
| dans un module | module.reseau.random_pet.sous_reseau["a"] | le préfixe module.<nom>. |
| data source | data.local_file.lecture | le préfixe data. |
L'ordre de la sortie suit la profondeur des modules
Section intitulée « L'ordre de la sortie suit la profondeur des modules »La liste n'est pas triée alphabétiquement de bout en bout. La documentation est explicite : la sortie est ordonnée « according to module depth order followed alphabetically ». Les instances de la configuration racine sortent donc en premier, puis celles des modules, du moins profond au plus profond.
Dans la sortie ci-dessus, module.reseau.data.local_file.relecture apparaît
après random_pet.zone["us-east"], ce qu'un tri purement alphabétique
interdirait. En pratique : le bas d'une longue liste est toujours la partie
modularisée, ce qui est commode pour repérer d'un coup d'œil ce qui vient des
modules.
Filtrer nativement, sans grep
Section intitulée « Filtrer nativement, sans grep »L'argument d'adresse n'est pas une vérification d'existence, c'est un filtre, et la page officielle parle de « patterns » en « resource addressing format ». Trois usages en découlent.
-
Toutes les instances d'une ressource, en donnant l'adresse sans index :
Fenêtre de terminal terraform state list random_pet.noeudrandom_pet.noeud[0]random_pet.noeud[1]random_pet.noeud[2]Une adresse sans index désigne la famille, pas une instance unique.
-
Une instance précise, en donnant l'index ou la clé :
Fenêtre de terminal terraform state list 'random_pet.zone["eu-west"]'random_pet.zone["eu-west"] -
Tout le contenu d'un module, avec une adresse de module :
Fenêtre de terminal terraform state list module.reseaumodule.reseau.data.local_file.relecturemodule.reseau.local_file.cartemodule.reseau.random_pet.sous_reseau["a"]module.reseau.random_pet.sous_reseau["b"]module.<nom>.module.<enfant>fonctionne de même, et descend dans les sous-modules.
Plusieurs adresses se cumulent sur la même ligne de commande. La sortie suit alors l'ordre des arguments, et non l'ordre trié : utile quand on veut comparer deux familles côte à côte.
Retrouver une adresse avec -id
Section intitulée « Retrouver une adresse avec -id »Le besoin courant est l'inverse de la liste : vous avez un identifiant réel, un
chemin, un UUID, un ARN, et vous cherchez quelle instance le porte. L'option
-id filtre sur la valeur de l'attribut id :
terraform state list -id=proud-marmosetrandom_pet.noeud[0]C'est le geste qui évite de lire cinquante lignes pour en trouver une. -id se
combine avec une adresse, et l'on obtient l'intersection des deux filtres :
terraform state list -id=proud-marmoset random_pet.zone ne rend rien si cet
identifiant appartient à une autre famille.
Les diagnostics, et une asymétrie à connaître
Section intitulée « Les diagnostics, et une asymétrie à connaître »Une adresse qui ne correspond à rien produit une erreur et un code de retour 1. Il y a quatre diagnostics distincts, et savoir lequel on a sous les yeux fait gagner du temps :
| Ce que vous passez | Diagnostic | Ce qu'il faut corriger |
|---|---|---|
random_pet.absent | Unknown resource | la ressource n'est pas au state |
random_pet.zone["nope"] | Unknown resource instance | la ressource existe, pas cette clé |
module.absent | Unknown module | le nom du module |
random_pet | Invalid address | il manque le nom : une adresse n'est jamais réduite au type |
Un -id sans correspondance se comporte au contraire tout autrement :
aucune erreur, sortie vide, code de retour 0. Vérifié sur 1.15.4.
Cette asymétrie décide de la façon dont on scripte la commande. Un filtre par
adresse se teste sur son code de retour ; un filtre par -id se teste sur le
fait que sa sortie soit vide ou non, puisque son code de retour vaut 0 dans
les deux cas.
# Un -id sans correspondance ne fait PAS echouer un scriptterraform state list -id=inexistant > /dev/null 2>&1; echo $? # 0Compter les ressources gérées, sans se tromper
Section intitulée « Compter les ressources gérées, sans se tromper »Voici le piège qui fausse des inventaires entiers. La recette qui circule est celle-ci :
# ❌ Faux des qu'un module contient une data sourceterraform state list | grep -v ^data | wc -lElle repose sur l'idée qu'une data source commence toujours par data. C'est vrai
à la racine, et faux dans un module : l'adresse y est
module.reseau.data.local_file.relecture, qui commence par module.. Le grep
la laisse donc passer et la compte comme une ressource gérée.
Sur le projet de ce guide, la recette annonce 10 ressources gérées là où il y en a 9. L'écart est de un par data source de module, et il grandit avec la modularisation, c'est-à-dire précisément sur les projets où le comptage compte.
Le comptage juste se fait sur la sortie structurée, qui distingue
mode: "managed" de mode: "data" et expose la hiérarchie
root_module / child_modules :
terraform show -json | jq '[.values.root_module | .. | .resources? // empty | .[] | select(.mode == "managed")] | length'9terraform state identities, la voie des identités
Section intitulée « terraform state identities, la voie des identités »La 1.15 expose une sous-commande voisine que l'on rencontre en cherchant à relier une adresse de state à l'objet réel :
terraform state identities -jsonElle exige le drapeau -json et le dit franchement si on l'oublie :
The terraform state identitiescommand requires the-json flag. Elle rend un
objet vide ({}) quand les providers du projet n'exposent pas d'identité de
ressource, ce qui est le cas de random et local. À connaître pour ne pas la
confondre avec -id, qui filtre, lui, sur un attribut du state.
Dépannage
Section intitulée « Dépannage »Le réflexe utile est de toujours partir d'une liste sans filtre, puis de resserrer : la plupart des erreurs sont des adresses mal écrites, pas des ressources absentes.
| Symptôme | Cause probable | Solution |
|---|---|---|
No state file was found! | Aucun apply, ou state ailleurs | Lancer terraform apply, vérifier le backend |
Unknown resource | Faute de frappe sur le nom | Relister sans filtre, puis copier l'adresse |
Unknown resource instance | La ressource existe, la clé ou l'index non | Lister la famille : terraform state list random_pet.zone |
Invalid address | Adresse réduite au type | Ajouter le nom, ou filtrer un module : module.reseau |
no matches found (avant Terraform) | zsh a interprété les crochets | Quoter : 'random_pet.noeud[0]' |
| Un comptage qui ne colle pas | La recette au grep compte une data source de module | Compter sur show -json avec mode == "managed" |
Liste vide après destroy | Comportement normal | Rien à corriger |
Mettre en pratique
Section intitulée « Mettre en pratique »Le lab terraform state list publie l'identifiant de trois instances tirées au
sort, dans un projet qui mélange count, for_each et un module local, puis
demande leurs adresses exactes. Il fait aussi mesurer l'écart entre la recette
au grep et le vrai comptage. Il se joue hors ligne, sans compte cloud.
À retenir
Section intitulée « À retenir »- La commande liste des instances, pas des ressources : un bloc en
count = 3donne trois lignes. - Quatre formes d'adresses : simple,
[index]pourcount,["clé"]pourfor_each, préfixemodule.<nom>.dans un module. - L'argument d'adresse est un filtre : sans index il rend toutes les
instances d'une ressource, et
module.<nom>est un filtre valide qui descend dans les sous-modules. - La sortie est triée par profondeur de module, puis alphabétiquement : la racine d'abord, les modules ensuite.
- Une adresse qui ne correspond à rien échoue (code 1, quatre diagnostics) ;
un
-idsans correspondance rend 0 et une sortie vide. - Sous
zsh, quoter toute adresse à crochets. - Compter les ressources gérées avec
grep -v ^dataest faux dès qu'un module a une data source : compter surshow -jsonetmode == "managed".
FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »Les questions ci-dessous portent sur ce qui bloque le plus souvent : l'adresse qui rend plusieurs lignes, le module qu'on croyait impossible à filtrer, et le comptage qui se trompe d'une unité. Chaque réponse donne la commande de vérification.
Une ressource, plusieurs instances
Une ressource est ce que vous declarez (un blocresource), une instance est ce que Terraform cree et enregistre dans le state. Un bloc en count = 3 donne donc trois lignes :random_pet.noeud[0]
random_pet.noeud[1]
random_pet.noeud[2]
C'est la meme raison qui fait qu'un filtre par adresse sans index rend toute la famille : terraform state list random_pet.noeud affiche les trois instances, ce n'est pas un filtre unitaire.Une adresse de module est un filtre valide
Pas besoin degrep : l'adresse d'un module filtre nativement.terraform state list module.reseau
module.reseau.data.local_file.relecture
module.reseau.local_file.carte
module.reseau.random_pet.sous_reseau["a"]
module.reseau.random_pet.sous_reseau["b"]
Verifie sur Terraform 1.15.4, code de retour 0. La forme module.reseau.module.enfant descend dans les sous-modules, et module.reseau.random_pet.sous_reseau restreint a une ressource du module.L'option -id fait le chemin inverse
Vous avez un chemin, un UUID ou un ARN, mais pas l'adresse Terraform :terraform state list -id=proud-marmoset
random_pet.noeud[0]
L'option se combine avec une adresse, et l'on obtient alors l'intersection des deux filtres. Attention a la difference de comportement : un -id sans correspondance ne produit aucune erreur, il rend une sortie vide et un code 0.Le grep rate les data sources des modules
La recette suppose qu'une data source commence toujours pardata. C'est vrai a la racine, faux dans un module : l'adresse y est module.reseau.data.local_file.relecture.Mesure sur Terraform 1.15.4, projet a 11 instances dont 2 data sources :- la recette au
grepannonce 10 ressources gerees ; - le vrai comptage sur
mode == "managed"en donne 9.
terraform show -json | jq '[.values.root_module
| .. | .resources? // empty | .[]
| select(.mode == "managed")] | length'
Quatre diagnostics, quatre causes
Une adresse qui ne correspond a rien echoue avec un code de retour 1 et un message qui dit precisement ce qui manque :| Adresse fournie | Diagnostic | A corriger |
|---|---|---|
random_pet.absent |
Unknown resource |
la ressource n'est pas au state |
random_pet.zone["nope"] |
Unknown resource instance |
la cle ou l'index |
module.absent |
Unknown module |
le nom du module |
random_pet |
Invalid address |
il manque le nom apres le type |
Le shell mange l'adresse avant Terraform
Sous zsh, les crochets sont un motif de nom de fichier. La commande echoue donc avant d'atteindre Terraform :terraform state list random_pet.noeud[0]
# zsh: no matches found: random_pet.noeud[0]
La correction tient en deux guillemets simples, et elle ne coute rien quand ils sont inutiles :terraform state list 'random_pet.noeud[0]'
terraform state list 'random_pet.zone["eu-west"]'
L'ordre est la profondeur de module, puis l'alphabet
La documentation officielle est explicite : la sortie est ordonnee « according to module depth order followed alphabetically ».Consequence visible :module.reseau.data.local_file.relecture apparait apres random_pet.zone["us-east"], ce qu'un tri purement alphabetique interdirait. En pratique, le bas d'une longue liste est toujours la partie modularisee, ce qui permet de reperer d'un coup d'oeil ce qui vient des modules.Pour aller plus loin
Section intitulée « Pour aller plus loin »- Sauvegarder et restaurer le state : proteger l'inventaire que vous venez de lire.
- terraform state rm : retirer une instance du state sans détruire l'objet réel.
- Diagnostiquer le state : quand la liste ne correspond plus à la réalité.
- terraform state list : la référence officielle : la syntaxe, les patterns et l'option
-state. - Format JSON du state :
mode,root_moduleetchild_modules, la base d'un comptage juste.