
Une data source Terraform lit une information qui existe déjà, sans jamais la
créer ni la modifier. C'est la brique qui vous évite de coder en dur une valeur
produite ailleurs : l'ID d'une image, le contenu d'un fichier, les outputs d'un
autre état. Vous la déclarez avec un bloc data, vous référencez son
résultat, et Terraform va le chercher pour vous.
Ce guide part de la base, écrire et utiliser un bloc data, puis aborde la
seule chose vraiment subtile du sujet : le moment où Terraform lit une data
source, tantôt pendant le plan, tantôt à l'apply. C'est ce timing qui explique
un (known after apply) inattendu, ou un plan qui bouge sans qu'une ligne de
HCL ait changé.
Tous les comportements décrits ont été vérifiés sur Terraform v1.15.4, plan
JSON à l'appui, avec les seuls providers local et random. Aucun compte cloud
n'est nécessaire.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Ce qu'est une data source et ce qui la distingue d'une resource
- Comment écrire un bloc
dataet utiliser sa valeur dans une ressource - Quand Terraform lit une data source : pendant le plan ou à l'apply
- Les trois causes exactes du report de lecture, et pourquoi
depends_onn'en fait pas partie - Les meta-arguments d'un bloc
data:count,for_each,provider,lifecycle - Comment prouver le moment de lecture avec
terraform show -json - L'avertissement de sécurité sur
terraform_remote_state
Prérequis
Section intitulée « Prérequis »- Le concept des trois briques (providers, resources et data sources)
- Ressources et attributs (déclarer des ressources Terraform)
- Dépendances explicites (depends_on en Terraform)
Qu'est-ce qu'une data source ?
Section intitulée « Qu'est-ce qu'une data source ? »Une data resource lit un objet ; une managed resource le crée, le modifie et le détruit. C'est toute la différence, et c'est le vocabulaire officiel de la documentation Terraform : managed resources d'un côté, data resources de l'autre. Une data source ne possède pas l'objet qu'elle interroge, elle en prend seulement une photo au moment de l'opération.
On s'en sert dès qu'une valeur vient d'ailleurs que de votre configuration : l'AMI la plus récente d'un cloud, le CIDR d'un réseau créé par une autre équipe, le contenu d'un fichier de version. Coder ces valeurs en dur les fige et les périme ; une data source les lit fraîches à chaque exécution.
Écrire un bloc data
Section intitulée « Écrire un bloc data »La syntaxe reprend celle d'une ressource, avec le mot-clé data et un
préfixe data. à l'adressage. Voici une data source qui lit un fichier
socle.txt déjà présent :
data "local_file" "socle" { filename = "${path.module}/socle.txt"}
output "socle_brut" { value = data.local_file.socle.content}Le bloc porte un type (local_file) et un nom (socle). Son résultat
s'adresse par data.<type>.<nom>.<attribut>, ici
data.local_file.socle.content. Une fois la configuration appliquée, la valeur
se lit directement :
terraform output -raw socle_brutapp_version=2.4.1zone=eu-west-3Aucune ressource n'a été créée pour cela : Terraform a simplement lu le fichier.
Utiliser une data source dans une ressource
Section intitulée « Utiliser une data source dans une ressource »L'intérêt vient de la référence : la valeur lue alimente une ressource gérée, exactement comme l'attribut d'une autre ressource.
resource "local_file" "manifeste" { filename = "${path.module}/manifeste.txt" content = "genere depuis : ${data.local_file.socle.content}"}Un terraform apply lit d'abord la data source, puis crée la ressource avec la
valeur obtenue :
local_file.manifeste: Creation complete after 0sApply complete! Resources: 1 added, 0 changed, 0 destroyed.Le fichier produit porte bien la donnée lue :
genere depuis : app_version=2.4.1zone=eu-west-3Cette référence crée aussi une dépendance implicite : Terraform sait qu'il
doit lire data.local_file.socle avant de construire local_file.manifeste.
Managed ou data : la distinction dans le state
Section intitulée « Managed ou data : la distinction dans le state »La différence n'est pas qu'un mot, elle est infalsifiable dans la sortie
machine. Le champ mode vaut managed ou data :
terraform show -json | jq -c '.values.root_module.resources[] | {address, mode}'{"address":"data.local_file.socle","mode":"data"}{"address":"local_file.manifeste","mode":"managed"}Recopier le contenu d'un fichier dans un local_file au lieu de le lire
produirait mode: managed, et cela se verrait. C'est aussi le vocabulaire que
vous retrouverez à l'examen.
Quand Terraform lit-il une data source ?
Section intitulée « Quand Terraform lit-il une data source ? »Voici le cœur du sujet, et la seule vraie subtilité. Terraform tente de lire les data sources pendant le plan, mais peut reporter certaines lectures à l'apply. La documentation le formule ainsi : « Terraform attempts to query data sources during planning, but may defer reads to the apply phase. »
Une croyance très répandue est fausse : depends_on sur un bloc data ne
force pas la lecture à l'apply. Le critère n'est pas l'existence d'une
dépendance, mais le fait que la ressource visée soit configurée pour changer
dans le plan courant.
Les trois causes exactes du report
Section intitulée « Les trois causes exactes du report »Le report n'arrive jamais au hasard. Il a trois causes, et trois seulement :
| Cause | Formulation officielle |
|---|---|
| Dépendance sur une ressource gérée en cours de changement | « depends directly or indirectly on a managed resource that is configured to change in the current plan » |
| Condition personnalisée dépendant d'une telle ressource | les precondition / postcondition du bloc, si elles référencent une ressource qui change |
| Argument dont la valeur n'est connue qu'à l'apply | « arguments whose values must be computed during the apply phase » |
Le mot décisif est « configured to change in the current plan ». Ce n'est pas « dépend d'une ressource », c'est « dépend d'une ressource qui bouge maintenant ».
La démonstration
Section intitulée « La démonstration »Trois data sources qui ne diffèrent que par ce qui alimente leur argument :
resource "random_pet" "empreinte" { length = 2
keepers = { revision = var.revision }}
resource "local_file" "rapport" { filename = "${path.module}/sortie/rapport-${random_pet.empreinte.id}.txt" content = "revision=${var.revision}\n"}
# 1. Argument constant : aucune dépendance.data "local_file" "catalogue" { filename = "${path.module}/catalogue.txt"}
# 2. L'argument RÉFÉRENCE l'attribut d'une ressource gérée.data "local_file" "rapport_relu" { filename = local_file.rapport.filename}
# 3. Argument constant, mais depends_on explicite.data "local_file" "catalogue_ordonne" { filename = "${path.module}/catalogue.txt" depends_on = [random_pet.empreinte]}Après un premier apply, plus rien ne change. Un nouveau plan lit les trois au
plan, y compris celle qui porte depends_on :
terraform plan -out=tf.planterraform show -json tf.plan | jq '.resource_changes[] | select(.mode == "data")'Sortie vide. Les trois figurent dans prior_state avec leurs valeurs réelles.
Faites maintenant bouger la ressource gérée :
terraform plan -var 'revision=2' -out=tf.planterraform show -json tf.plan | jq '.resource_changes[] | select(.mode == "data") | {address, actions: .change.actions}'{ "address": "data.local_file.rapport_relu", "actions": ["read"] }{ "address": "data.local_file.catalogue_ordonne", "actions": ["read"] }catalogue reste absente : son argument ne dépend de rien. Les deux autres sont
reportées, parce que la ressource dont elles dépendent est cette fois en
mouvement.
La documentation recommande d'ailleurs la sobriété sur ce meta-argument :
« You should only use depends_on as a last resort because it can cause Terraform
to create more conservative plans that replace more resources than necessary. »
Comment prouver le moment de lecture ?
Section intitulée « Comment prouver le moment de lecture ? »Par le plan converti en JSON, jamais par la sortie de terminal. L'opposition est nette, et c'est la seule preuve non ambiguë :
| Moment de lecture | Où la data source apparaît | Où elle n'apparaît pas |
|---|---|---|
| Plan | prior_state.values.root_module.resources, avec ses valeurs réelles | resource_changes |
| Apply | resource_changes, avec "mode": "data" et "actions": ["read"] | prior_state |
L'effet se propage aux outputs. Un output qui dérive d'une lecture reportée
porte after_unknown: true :
terraform show -json tf.plan | jq '.output_changes'{ "catalogue": { "after_unknown": false }, "rapport": { "after_unknown": true }}C'est l'origine exacte du (known after apply) que vous lisez dans le plan en
clair.
Quels meta-arguments un bloc data accepte-t-il ?
Section intitulée « Quels meta-arguments un bloc data accepte-t-il ? »Le bloc data n'est pas un bloc appauvri : il accepte count, for_each,
provider, depends_on et lifecycle, comme une ressource gérée. Les quatre
formes ci-dessous ont été appliquées avec succès sur Terraform v1.15.4.
Plusieurs instances avec for_each
Section intitulée « Plusieurs instances avec for_each »Le meta-argument for_each lit une famille de fichiers en une seule
déclaration :
data "local_file" "tous" { for_each = toset(["a.txt", "b.txt"]) filename = "${path.module}/${each.key}"}
output "tailles" { value = { for k, d in data.local_file.tous : k => length(d.content) }}L'adressage devient data.local_file.tous["a.txt"], visible tel quel dans
terraform state list.
Plusieurs instances avec count
Section intitulée « Plusieurs instances avec count »Quand l'index suffit, count fait le même travail par position :
data "local_file" "indexe" { count = 2 filename = "${path.module}/${count.index == 0 ? "a" : "b"}.txt"}Adressage data.local_file.indexe[0], comme pour une ressource gérée.
Une condition sur la donnée lue
Section intitulée « Une condition sur la donnée lue »Le bloc lifecycle porte precondition et postcondition : c'est le moyen de
faire échouer une configuration au plan quand une donnée externe n'est pas
conforme, plutôt que de laisser l'erreur se propager plus loin.
data "local_file" "controle" { filename = "${path.module}/catalogue.txt"
lifecycle { postcondition { condition = length(self.content) > 0 error_message = "Le catalogue est vide." } }}Le sujet des conditions est creusé dans valider les entrées d'une configuration Terraform et le bloc lifecycle Terraform.
Une configuration de provider non par défaut
Section intitulée « Une configuration de provider non par défaut »L'argument provider sur un bloc data cible un alias, indispensable dès que
vous lisez dans une seconde région ou un second compte :
provider "local" { alias = "secondaire"}
data "local_file" "via_alias" { provider = local.secondaire filename = "${path.module}/catalogue.txt"}Qu'est-ce qui provoque une dérive par la donnée externe ?
Section intitulée « Qu'est-ce qui provoque une dérive par la donnée externe ? »Le simple fait que la donnée lue change. C'est la conséquence pratique la
plus visible d'une data source, et la plus déroutante quand on ne l'a pas
anticipée. Modifiez le fichier lu, sans toucher un seul .tf :
echo "ajout=externe" >> catalogue.txtterraform plan -detailed-exitcodeecho $? # 2Aucun fichier .tf n'a bougé. Le code de retour 2 signifie pourtant que des
changements sont planifiés. Une data source est relue à chaque plan, et c'est
cette lecture fraîche, non une valeur conservée, qui alimente le diff des
ressources dépendantes.
C'est aussi pourquoi il est trompeur de dire que « Terraform stocke le résultat pour détecter les changements » : la valeur est bien écrite dans l'état pendant la phase de rafraîchissement, mais c'est la relecture qui fait foi.
Que deviennent les data sources lors d'un destroy ?
Section intitulée « Que deviennent les data sources lors d'un destroy ? »Aucune action de destruction ne les concerne, mais elles disparaissent quand même de l'état. La nuance compte, et elle est souvent mal comprise.
terraform plan -destroy -out=tf.planterraform show -json tf.plan | jq '[.resource_changes[] | select(.mode == "data")] | length'# 0Le plan de destruction ne contient aucune entrée mode: data, et le compteur
final les ignore : Terraform ne détruit pas ce qu'il n'a jamais créé. Mais après
l'apply de ce plan, terraform state list est vide. Les data resources sont
retirées de l'état en même temps que le reste. Il est donc faux de dire qu'une
data source « survit » à un destroy : elle n'est pas détruite, elle disparaît
sans action de destruction.
terraform_remote_state : l'avertissement à connaître
Section intitulée « terraform_remote_state : l'avertissement à connaître »terraform_remote_state lit les outputs d'un autre état Terraform. C'est
pratique pour partager une valeur entre configurations, et c'est la data source
la plus exposée en matière de sécurité.
data "terraform_remote_state" "reseau" { backend = "s3"
config = { bucket = "mon-bucket-tfstate" key = "reseau/terraform.tfstate" region = "eu-west-3" }}Deux points que la documentation officielle signale explicitement. L'accès aux outputs vaut accès à tout l'état : « Any user or server which has enough access to read the root module output values will also always have access to the full state snapshot data by direct network requests. » Donner accès à un output revient donc à donner accès au snapshot complet, mots de passe et clés compris s'ils y figurent.
Seuls les outputs racine sont exposés, jamais ceux des modules enfants. Un output enfoui dans un module n'est pas lisible par ce biais, mais il reste dans le snapshot. Pour partager des valeurs sans exposer un état entier, préférez un magasin dédié (Vault, SSM Parameter Store, Consul) ou les data sources du fournisseur.
Dépannage
Section intitulée « Dépannage »Ces symptômes reviennent souvent, et se lisent tous au moment du plan. Le tableau associe chacun à sa cause réelle, puis à la correction.
| Symptôme | Cause | Solution |
|---|---|---|
(known after apply) sur une valeur attendue | La data source dépend d'une ressource qui change dans ce plan | Normal. Un argument constant reste connu au plan : vérifiez si la dépendance est nécessaire |
no such file or directory au premier apply | Une data source à chemin constant est lue pendant le plan, donc avant toute création | Faites dépendre son argument de la ressource qui produit le fichier, ou pré-créez le fichier |
| Un plan annonce des changements sans modification de code | La donnée externe lue a changé | C'est la dérive par la donnée. plan -detailed-exitcode rend 2 |
depends_on ajouté, mais la lecture reste au plan | Ce n'est pas un critère de report | Comportement attendu. Seul un changement en attente reporte la lecture |
Une adresse apparaît en mode: managed alors qu'on attendait une lecture | Un resource a été déclaré là où un data était requis | Remplacez la ressource par un bloc data |
À retenir
Section intitulée « À retenir »- Une data source lit un objet existant sans le posséder ; une managed
resource le crée. Le champ
modedu state tranche :dataoumanaged. - On écrit un bloc
data "<type>" "<nom>"et on référence son résultat pardata.<type>.<nom>.<attribut>, ce qui crée une dépendance implicite. - Terraform lit une data source pendant le plan, sauf report à l'apply.
- Le report a trois causes ;
depends_onseul n'en est pas une. Le critère est que la ressource visée change dans le plan courant. - La preuve du moment de lecture est dans le plan JSON :
prior_statecontreresource_changesavecactions: ["read"], etafter_unknownsur les outputs. - Le bloc
dataacceptecount,for_each,provideretlifecycle. terraform_remote_stateexpose tout le snapshot à qui lit un output : préférez un magasin de secrets dédié.
FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »Les questions ci-dessous portent sur le point qui déroute le plus : le moment
où une data source est lue, au plan ou à l'apply, et ce qui en découle sur les
valeurs (known after apply). Chaque réponse donne la commande de vérification.
La nuance importe
depends_on ne reporte pas la lecture à chaque fois. Ce qui compte, c'est l'état de la ressource visée :| Situation | Moment de lecture |
|---|---|
| La ressource visée est stable | au plan, comme d'habitude |
| La ressource visée va changer dans ce plan | reportée à l'apply |
Ce qui déclenche le report
Un premierterraform apply, où la ressource n'existe pas encore, reporte la lecture :# data.local_file.lecture will be read during apply
Une fois l'infrastructure en place, le plan suivant lit la data source normalement :No changes. Your infrastructure matches the configuration.
Vérifié sur Terraform v1.15.4.Le plan JSON tranche
terraform plan -out=tfplan && terraform show -json tfplan | jq '.resource_changes[] | select(.mode == "data")'
| Résultat | Où la data source apparaît |
|---|---|
| Lue au plan | dans prior_state.values.root_module.resources, absente de resource_changes |
| Reportée à l'apply | dans resource_changes, avec "mode": "data" et "actions": ["read"] |
Pourquoi c'est utile
Une data source reportée rend(known after apply) toutes les valeurs qui en dérivent. Savoir la reconnaître explique la moitié des plans surprenants.Vérifié sur Terraform v1.15.4.La cause
Un output en(known after apply) dérive d'une data source reportée. La valeur n'existe pas encore au moment du plan, donc l'output non plus.Dans le plan JSON, cet output porte after_unknown: true.La correction
Rendre l'argument de la data source indépendant de toute ressource qui change dans le plan courant. Souvent, cela revient à retirer undepends_on inutile, ou à pointer une valeur stable plutôt qu'un attribut de ressource en création.Une fois la data source lue au plan, l'output retrouve une valeur connue.Vérifié sur Terraform v1.15.4.Les deux méta-arguments fonctionnent
data "local_file" "multi" {
for_each = toset(["a.txt", "b.txt"])
filename = each.value
}
L'adressage suit la même règle que pour une ressource :| Méta-argument | Adressage |
|---|---|
count |
data.<TYPE>.<NOM>[0] |
for_each |
data.<TYPE>.<NOM>["clé"] |
Les autres arguments admis
Un blocdata accepte aussi provider, depends_on, et un bloc lifecycle réduit à precondition et postcondition. Les règles de remplacement comme create_before_destroy n'ont pas de sens sur une lecture.Vérifié sur Terraform v1.15.4.Ce que montre le destroy
terraform destroy -auto-approve
Destroy complete! Resources: 0 destroyed.
Aucune data source n'apparaît dans le plan de destruction, et le compteur ne les inclut pas.La distinction
Une data source n'est pas un objet réel : c'est une lecture. Elle n'a rien à détruire. Après l'apply de destruction, elle disparaît simplement de l'état, sans action dédiée.C'est cohérent avec sa nature : Terraform ne gère pas son cycle de vie, il la relit à chaque plan.Vérifié sur Terraform v1.15.4.Le garde-fou
data "local_file" "config" {
filename = "${path.module}/config.json"
lifecycle {
postcondition {
condition = can(jsondecode(self.content))
error_message = "config.json n'est pas un JSON valide."
}
}
}
Pourquoi le placer là
Une data source introduit une donnée externe dans la configuration : un fichier, une réponse d'API, un état distant. Unepostcondition la valide avant qu'elle ne contamine le reste du plan.C'est préférable à laisser une donnée malformée provoquer une erreur obscure trois ressources plus loin.Vérifié sur Terraform v1.15.4.La démonstration
echo 'contenu modifié' > donnee.txt
terraform plan -detailed-exitcode
echo "exit code : $?"
exit code : 2
Le code 2 signale des changements détectés, sans qu'une seule ligne de HCL ait bougé.Ce que cela implique
La donnée externe fait partie de ce que Terraform compare. Une data source n'est pas un instantané figé : elle est relue à chaque plan, et tout écart déclenche une proposition de changement sur ce qui en dérive.Utile pour un pipeline CI qui déclenche un apply surexit code 2.Vérifié sur Terraform v1.15.4.Le risque
terraform_remote_state lit l'état entier d'une autre configuration, pas seulement ses outputs. Qui a accès à cet état accède à tout ce qu'il contient.Le marquage sensitive masque une valeur à l'affichage, mais ne chiffre rien dans l'état : une requête réseau directe sur le backend récupère le snapshot complet.La bonne pratique
Pour partager un secret entre deux configurations, passer par un magasin de secrets dédié : Vault, un gestionnaire de secrets cloud, une variable chiffrée de pipeline. Jamais par l'état partagé.terraform_remote_state reste utile pour partager des valeurs non sensibles : un identifiant de réseau, un ARN, un nom de bucket.Vérifié sur Terraform v1.15.4.Pour aller plus loin
Section intitulée « Pour aller plus loin »- Les fonctions Terraform : De quoi filtrer et recomposer les valeurs renvoyées par une data source.
- Conditions personnalisées : validation, precondition et check : Le moyen de valider ce qu'une data source renvoie avant de s'en servir.
- depends_on : les dépendances explicites : Le recours quand une data source doit être lue après la création d'une ressource.