
Votre terraform apply détruit et recrée votre base de données à chaque
changement de version d’image — avec une coupure de service à la clé.
Ou vous lancez terraform destroy par erreur et perdez un volume de
données critique faute de protection.
Le bloc lifecycle existe pour contrôler précisément ces comportements.
Par défaut, Terraform détruit une ressource avant d’en créer une nouvelle
lorsqu’il doit la remplacer. Pour certaines ressources critiques — bases de
données, volumes persistants, certificats — ce comportement est inacceptable.
create_before_destroy l’inverse. prevent_destroy bloque toute
destruction accidentelle, même un terraform destroy explicite. ignore_changes
permet d’exclure des attributs qui changent hors de Terraform (taille de
disque redimensionné manuellement, tags ajoutés par une autre équipe). Ces
trois options couvrent la grande majorité des cas rencontrés en production.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »create_before_destroy: inverser l’ordre destruction/créationprevent_destroy: bloquer toute destruction accidentelleignore_changes: ignorer certains attributs lors des plansreplace_triggered_by: forcer le remplacement sur changement externe- Comportements réels capturés sur un lab KVM
Prérequis
Section intitulée « Prérequis »- Ressources et attributs (Déclarer des ressources)
depends_oncompris (depends_on Terraform)
L’idée derrière le lifecycle
Section intitulée « L’idée derrière le lifecycle »Pensez aux phases d’une ressource dans un cycle de vie d’objet :
# Cycle de vie d'un objetobject.create()object.update()object.delete()Par défaut, Terraform suit ce cycle : créer → mettre à jour → détruire. Mais pour certaines ressources critiques, ce cycle a des problèmes :
- Une base de données détruite puis recrée = perte de données
- Paramètre sensible modifié hors de Terraform devrait être ignoré
- Service dépendant doit rester disponible pendant la mise à jour
Le bloc lifecycle permet de modifier ce cycle : créer avant de détruire l’ancienne version, bloquer complètement la destruction, ou ignorer certains changements.
Syntaxe générale
Section intitulée « Syntaxe générale »resource "type" "nom" { # ...attributs...
lifecycle { create_before_destroy = true prevent_destroy = false ignore_changes = [attribut1, attribut2] replace_triggered_by = [autre_ressource.nom] }}create_before_destroy
Section intitulée « create_before_destroy »Le problème
Section intitulée « Le problème »Terraform remplace une ressource en faisant destroy → create. Pendant la destruction, la ressource n’existe plus. Pour des disques ou des bases de données, cette fenêtre est problématique.
La solution
Section intitulée « La solution »resource "libvirt_volume" "base" { name = "vm-base.qcow2" pool = "default" target = { format = { type = "qcow2" } } create = { content = { url = var.image_path } }
lifecycle { create_before_destroy = true }}Ordre d’exécution avec create_before_destroy = true :
- Nouveau volume créé
- Ancienne référence mise à jour
- Ancien volume détruit
prevent_destroy
Section intitulée « prevent_destroy »Protège une ressource contre toute destruction — que ce soit via terraform destroy ou via un plan qui remplacerait la ressource.
resource "libvirt_volume" "data" { name = "vm-data.qcow2" pool = "default" capacity = 1 capacity_unit = "GiB" target = { format = { type = "qcow2" } }
lifecycle { prevent_destroy = true }}Comportement réel
Section intitulée « Comportement réel »Tentative de destruction de ce volume :
╷│ Error: Instance cannot be destroyed││ on main.tf line 16:│ 16: resource "libvirt_volume" "data" {││ Resource libvirt_volume.data has lifecycle.prevent_destroy set, but the│ plan calls for this resource to be destroyed. To avoid this error and│ continue with the plan, either disable lifecycle.prevent_destroy or reduce│ the scope of the plan using the -target option.╵Terraform refuse le plan avant toute action. Pour détruire la ressource,
il faut d’abord passer prevent_destroy = false et appliquer.
ignore_changes
Section intitulée « ignore_changes »Certains attributs sont modifiés hors Terraform (l’opérateur change
la RAM via virsh, un autoscaler modifie le nombre d’instances, etc.).
Sans ignore_changes, le prochain terraform plan proposerait de
remettre la valeur définie dans le code.
resource "libvirt_domain" "vm" { name = "lab13-vm" memory = var.memory # 512 MiB dans le code memory_unit = "MiB" vcpu = 1 # ...
lifecycle { ignore_changes = [memory] }}Comportement réel
Section intitulée « Comportement réel »Avec memory=512 dans le state et memory=1024 dans la variable :
terraform plan -var="memory=1024"
No changes. Your infrastructure matches the configuration.Terraform ignore la différence sur memory. La VM n’est pas recréée.
ignore_changes = all
Section intitulée « ignore_changes = all »lifecycle { ignore_changes = all}Terraform ne planifie jamais de changements sur cette ressource après sa création. À utiliser avec beaucoup de précaution — les dérives ne seront plus détectées.
replace_triggered_by
Section intitulée « replace_triggered_by »Force le remplacement d’une ressource quand une autre ressource ou attribut change — même si les propres attributs de la ressource n’ont pas changé.
resource "libvirt_volume" "config" { name = "config-v2.qcow2" # ...}
resource "libvirt_domain" "vm" { name = "lab13-vm" memory = 512 # ...
lifecycle { # Remplacer la VM si le volume de config est recréé replace_triggered_by = [libvirt_volume.config] }}Dès que libvirt_volume.config est recréé (remplacé), libvirt_domain.vm
sera lui aussi remplacé au prochain apply.
Exemple : les trois arguments en pratique
Section intitulée « Exemple : les trois arguments en pratique »# create_before_destroy sur le disque systèmeresource "libvirt_volume" "base" { name = "${var.vm_name}-base.qcow2" pool = "default" target = { format = { type = "qcow2" } } create = { content = { url = var.image_path } }
lifecycle { create_before_destroy = true }}
# prevent_destroy sur le disque de donnéesresource "libvirt_volume" "data" { name = "${var.vm_name}-data.qcow2" pool = "default" capacity = 1 capacity_unit = "GiB" target = { format = { type = "qcow2" } }
lifecycle { prevent_destroy = true }}
# VM avec ignore_changes sur memoryresource "libvirt_domain" "vm" { name = var.vm_name type = "kvm" memory = var.memory 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" } }, { source = { file = { file = libvirt_volume.data.path } } target = { dev = "vdb", bus = "virtio" } } ] interfaces = [{ model = { type = "virtio" } source = { network = { network = "default" } } }] }
lifecycle { ignore_changes = [memory] }
depends_on = [libvirt_volume.base, libvirt_volume.data]}Résultats apply :
libvirt_volume.base: Creating...libvirt_volume.data: Creating...libvirt_volume.data: Creation complete after 0slibvirt_volume.base: Creation complete after 1slibvirt_domain.vm: Creating...libvirt_domain.vm: Creation complete after 0s [name=lab13-vm]
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.Récapitulatif
Section intitulée « Récapitulatif »| Argument | Effet | Cas d’usage typique |
|---|---|---|
create_before_destroy | Nouveau créé avant l’ancien détruit | Bases de données, LB, disques critiques |
prevent_destroy | Refuse tout plan qui détruirait la ressource | Données de production, volumes persistants |
ignore_changes | Ignore les diffs sur certains attributs | RAM gérée par autoscaler, tags externes |
replace_triggered_by | Remplace la ressource si une dépendance change | VM à rebooter quand la config change |
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause | Solution |
|---|---|---|
Instance cannot be destroyed | prevent_destroy = true actif | Passer à false, appliquer, puis détruire |
| Plan vide malgré une variable changée | ignore_changes inclut cet attribut | Vérifier la liste ignore_changes |
create_before_destroy conflit de nom | Le nouveau et l’ancien ont le même nom | Ajouter un suffixe de version dans le nom |
À retenir
Section intitulée « À retenir »create_before_destroyinverse l’ordre destroy/create — essentiel pour les ressources sans downtimeprevent_destroyest un garde-fou HCL — il bloque les plans Terraform, pas les suppressions directesignore_changes = [attr]est utile quand un outil externe gère cet attributreplace_triggered_bylie le cycle de vie d’une ressource à une autre- Ces arguments se combinent dans le même bloc
lifecycle {}