Aller au contenu
Infrastructure as Code medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

depends_on Terraform : contrôler l'ordre de création

11 min de lecture

logo terraform

Votre terraform apply échoue à mi-chemin avec une erreur cryptique : la VM n’a pas pu démarrer car le réseau n’était pas encore prêt. Aucune référence entre les deux ressources n’existe dans le code — Terraform ne peut pas déduire l’ordre correct.

Terraform construit automatiquement un graphe de dépendances à partir des références entre ressources. Quand resource A utilise un attribut de resource B, Terraform sait que B doit exister avant A. Mais certaines dépendances sont comportementales, pas structurelles : un service doit être démarré, un réseau actif, un pare-feu appliqué — sans qu’aucune référence d’attribut ne l’exprime. C’est précisément pour ces cas qu’existe depends_on : forcer un ordre explicite là où le graphe implicite est aveugle.

  • Comment Terraform détermine l’ordre de création via les références
  • La différence entre dépendance implicite et explicite
  • Quand utiliser depends_on — et quand ne pas l’utiliser
  • Usage sur les ressources, les modules et les data sources
  • Lab KVM : réseau NAT créé avant la VM via depends_on

Analogie : imaginez un chantier de construction. Si le plombier a besoin d’eau courante (du réservoir), il ne peut pas commencer avant que le réservoir soit installé. Terraform voit la référence (« le chef plombier dit qu’il a besoin du réservoir ») et ordonne automatiquement les étapes. C’est une dépendance implicite.

Terraform crée automatiquement une dépendance implicite dès qu’une ressource référence un attribut d’une autre ressource.

resource "libvirt_volume" "base" {
name = "vm-base.qcow2"
# ...
}
resource "libvirt_domain" "vm" {
devices = {
disks = [{
source = { file = { file = libvirt_volume.base.path } }
# ↑ référence → Terraform sait que base doit exister avant vm
}]
}
}

Terraform lit cette référence et en déduit :

  • libvirt_volume.base → doit être créé avant libvirt_domain.vm
  • libvirt_domain.vm → doit être détruit avant libvirt_volume.base

C’est le cas le plus courant : pas besoin de depends_on.

Certaines dépendances ne passent pas par des attributs HCL. Elles sont comportementales, pas structurelles. Exemples :

  • Un réseau doit être opérationnel avant qu’une VM soit créée dedans
  • Un utilisateur IAM doit avoir ses permissions avant qu’un job soit lancé
  • Un script d’initialisation doit être terminé avant qu’une ressource soit configurée
resource "libvirt_network" "lab_net" {
name = "lab12-net"
forward = { mode = "nat" }
ips = [{ address = "10.99.12.1", netmask = "255.255.255.0" }]
}
resource "libvirt_domain" "vm" {
# ...
devices = {
interfaces = [{
source = { network = { network = libvirt_network.lab_net.name } }
# Référence au nom du réseau, mais Terraform ne sait pas que
# la ressource doit être PRÊTE (pas seulement son nom connu).
}]
}
depends_on = [libvirt_network.lab_net] # ← force l'attente complète
}
# main.tf — réseau NAT + VM avec depends_on
resource "libvirt_network" "lab_net" {
name = "lab12-net"
autostart = false
forward = { mode = "nat" }
ips = [{
address = "10.99.12.1"
netmask = "255.255.255.0"
dhcp = { ranges = [{ start = "10.99.12.100", end = "10.99.12.200" }] }
}]
}
resource "libvirt_volume" "base" {
name = "lab12-vm-base.qcow2"
pool = "default"
target = { format = { type = "qcow2" } }
create = { content = { url = "/home/bob/images/ubuntu-24.04-cloudimg.img" } }
}
resource "libvirt_domain" "vm" {
name = "lab12-vm"
type = "kvm"
memory = 512
memory_unit = "MiB"
vcpu = 1
os = { type = "hvm", type_arch = "x86_64", type_machine = "q35" }
devices = {
disks = [{
source = { file = { file = libvirt_volume.base.path } }
target = { dev = "vda", bus = "virtio" }
}]
interfaces = [{
model = { type = "virtio" }
source = { network = { network = libvirt_network.lab_net.name } }
}]
}
depends_on = [libvirt_network.lab_net]
# libvirt_volume.base n'est PAS ici : la référence à .path
# crée déjà une dépendance implicite — depends_on serait redondant.
}
libvirt_volume.base: Creating...
libvirt_network.lab_net: Creating...
libvirt_network.lab_net: Creation complete after 0s
libvirt_volume.base: Creation complete after 0s
libvirt_domain.vm: Creating...
libvirt_domain.vm: Creation complete after 0s [name=lab12-vm]
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
network_gateway = "10.99.12.1"
network_name = "lab12-net"
vm_name = "lab12-vm"

Terraform a créé libvirt_network et libvirt_volume en parallèle (deux ressources sans dépendance entre elles), puis libvirt_domain seulement après que les deux soient terminées.

Fenêtre de terminal
terraform graph | dot -Tpng > graph.png

Requiert graphviz. Chaque flèche représente une dépendance. depends_on ajoute des flèches en pointillés.

Sans graphviz, lire le plan suffit — Terraform affiche l’ordre d’exécution :

libvirt_network.lab_net: Creating... ← 1er (pas de déps)
libvirt_volume.base: Creating... ← 1er (en parallèle)
libvirt_domain.vm: Creating... ← après les deux précédents
module "database" {
source = "./modules/database"
}
module "application" {
source = "./modules/application"
depends_on = [module.database]
# Tout le module database est terminé avant que
# les ressources du module application soient créées.
}

Un data source qui dépend d’une ressource créée dans le même plan :

resource "aws_iam_role" "lambda" {
name = "lambda-role"
# ...
}
data "aws_iam_policy_document" "lambda" {
depends_on = [aws_iam_role.lambda]
# Attend que le rôle existe avant de le lire
statement {
actions = ["sts:AssumeRole"]
principals { type = "Service", identifiers = ["lambda.amazonaws.com"] }
}
}
## Quand NE PAS utiliser depends_on
| Situation | Solution correcte |
|-----------|------------------|
| Ressource B référence un attribut de A | Référence implicite (`A.attribut`) — `depends_on` inutile |
| Module B utilise des outputs du module A | `module.a.output_name` — dépendance implicite créée |
| Vous voulez un "ordre logique" sans vraie dépendance | Ne pas ajouter `depends_on` — Terraform s'en charge |
`depends_on` explicite **désactive le parallélisme** entre les ressources
concernées et peut produire des plans plus conservateurs (davantage de
valeurs en `known after apply`, remplacements évitables). Ajouter des
dépendances inutiles ralentit les apply et réduit la qualité du plan.
## Règle pratique
Poser la question : *"Si je supprimais `depends_on`, Terraform pourrait-il
tenter de créer la ressource avant que sa dépendance soit opérationnelle ?"*
- **Oui** → `depends_on` justifié
- **Non** → une référence d'attribut implicite suffit
## À retenir
1. Les **références** (`ressource_a.attribut`) créent des dépendances implicites automatiquement
2. `depends_on` est pour les dépendances **comportementales** — non visibles dans le HCL
3. Terraform crée les ressources indépendantes **en parallèle** — `depends_on` le désactive
4. Sur des modules : `depends_on = [module.nom]` attend la fin de toutes les ressources du module
5. Sur des data sources : utile quand la ressource source est créée dans le même plan
## Prochaines étapes
<CardGrid>
<LinkCard
title="lifecycle : contrôler le remplacement des ressources"
description="create_before_destroy, prevent_destroy, ignore_changes : maîtriser le cycle de vie des ressources."
href="/docs/infra-as-code/provisionnement/terraform/ecrire-code/lifecycle-terraform/"
/>
<LinkCard
title="Votre première infrastructure"
description="Créer une VM complète sur KVM avec Terraform : lab guidé de A à Z."
href="/docs/infra-as-code/provisionnement/terraform/premieres-infras/premiere-infrastructure/"
/>
</CardGrid>

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn