Aller au contenu
English
Cloud medium

Lab fil rouge IaC : Packer construit, Terraform provisionne, Ansible vérifie

Validé live le ·fr-par·Terraform 1.16.1, Packer 1.15.0, ansible-core 2.20.1

75 min de lecture

Les trois outils du volet ne se concurrencent pas, ils se relaient, et ce lab les fait travailler sur une même machine. Packer construit une image, Terraform provisionne une Instance à partir d'elle, Ansible se connecte et vérifie depuis l'intérieur que l'image utilisée est bien la bonne. Puis vient la partie qui fait le prix de ce lab : vous détruisez tout avec Terraform, proprement, code de retour zéro, et vous découvrez que trois objets ont survécu et continuent de se facturer. Aucun n'était dans l'état Terraform, aucun ne porte le nom de votre build, et deux vivent dans une famille de ressources où personne ne pense à regarder.

  • Construire une image avec Packer, et savoir où atterrissent ses objets.
  • Provisionner depuis cette image avec un provider délibérément épinglé.
  • Prouver depuis l'intérieur de la machine qu'elle tourne sur votre image.
  • Constater ce que terraform destroy laisse derrière lui.
  • Détruire dans l'ordre que les dépendances imposent.

Que va coûter ce lab, et pendant combien de temps ?

Section intitulée « Que va coûter ce lab, et pendant combien de temps ? »

Quelques centimes, à condition de finir le nettoyage. Le build Packer mobilise une Instance DEV1-S et une adresse publique pendant une minute et demie. Terraform en démarre une seconde, le temps des vérifications.

PosteDurée mesuréeOrdre de grandeur
Build Packer, Instance et adresse68 secondesmoins d'un centime
Instance provisionnée par Terraformquelques minutesmoins d'un centime
Image, snapshot et volume laissés derrièreindéfinimentc'est là que ça coûte

La dernière ligne est l'objet de ce lab. Les deux premières se terminent d'elles-mêmes ; la troisième ne se termine que si vous allez la chercher.

Le modèle reste minimal, mais il pose un marqueur daté dans l'image. C'est lui qui permettra, plus tard, de prouver depuis l'intérieur de la machine qu'elle démarre bien sur votre construction et non sur l'image publique.

  1. Écrivez le modèle, avec le plugin épinglé.

    packer {
    required_plugins {
    scaleway = {
    version = ">= 1.5.0"
    source = "github.com/scaleway/scaleway"
    }
    }
    }
    source "scaleway" "socle" {
    zone = "fr-par-1"
    commercial_type = "DEV1-S"
    image = "ubuntu_noble"
    ssh_username = "root"
    image_name = "socle-capstone-${var.horodatage}"
    server_name = "packer-capstone-${var.horodatage}"
    server_tags = ["packer", "capstone", "jetable"]
    }
    build {
    sources = ["source.scaleway.socle"]
    provisioner "shell" {
    inline = [
    "set -eux",
    "apt-get update -qq",
    "apt-get install -y --no-install-recommends ca-certificates curl",
    "printf 'socle-capstone %s\\n' '${var.horodatage}' > /etc/socle-version",
    "apt-get clean && rm -rf /var/lib/apt/lists/*",
    ]
    }
    post-processor "manifest" {
    output = "manifeste.json"
    strip_path = true
    }
    }
  2. Installez le plugin, puis construisez.

    Fenêtre de terminal
    packer init socle.pkr.hcl
    time packer build -var "horodatage=$(date +%H%M%S)" socle.pkr.hcl

    La sortie doit se terminer par An image was created: suivi du nom et de l'identifiant. Sur le lab du 11 septembre 2026, le build a pris 68 secondes.

  3. Récupérez l'identifiant depuis le manifeste, plutôt que dans le texte.

    Fenêtre de terminal
    IMAGE=$(jq -r '.builds[-1].artifact_id | split(":") | last' manifeste.json)
    scw instance image get "$IMAGE" zone=fr-par-1 -o json

    La sortie doit afficher votre image avec son root_volume.

Pas là où la logique le voudrait, et c'est le piège qui coûte le plus cher sur la durée. Un build réussi laisse trois objets derrière lui, répartis dans deux familles de ressources, et aucun ne porte le nom de votre build.

Regardez les trois familles, dans cet ordre :

Fenêtre de terminal
scw instance snapshot list zone=fr-par-1 -o json
scw block snapshot list zone=fr-par-1 -o json
scw block volume list zone=fr-par-1 -o json

Relevé le 11 septembre 2026, la première commande renvoie []. La famille historique des snapshots d'Instance est vide : un script de nettoyage écrit d'après une documentation ancienne cherche exactement au mauvais endroit et conclut, à tort, que tout est propre.

Les deux suivantes, elles, ne sont pas vides :

FamilleCe qu'on y trouveNom relevé
instance snapshotrien-
block snapshotle snapshot de l'image, 10 Gosnp-xenodochial-bartik
block volumeun volume orphelin, 10 Go, references=[]Ubuntu 24.04 Noble Numbat_sbs_volume_0

Les deux noms sont le vrai problème. Le snapshot porte un nom généré aléatoirement : sur trois passes successives, il s'est appelé snp-jovial-hopper, puis snp-eager-gagarin, puis snp-xenodochial-bartik. Le volume, lui, porte le nom de l'image source, pas celui de votre build. Chercher « capstone » ou « packer » dans les listes ne remonte donc rien, alors que trois objets vous attendent sur la facture. On ne les retrouve que par famille, jamais par étiquette, ce qui condamne toute recherche par nom.

Le code déclare un réseau privé, une adresse, une Instance qui démarre sur votre image, et une interface privée. Cette dernière n'est pas décorative : c'est elle qui va démontrer l'intérêt de l'épinglage.

terraform {
required_providers {
scaleway = {
source = "scaleway/scaleway"
version = "2.80.0"
}
}
}
resource "scaleway_instance_ip" "app" {
project_id = var.project_id
}
resource "scaleway_instance_server" "app" {
name = "capstone-app-${var.horodatage}"
type = "DEV1-S"
image = var.image_id
project_id = var.project_id
ip_id = scaleway_instance_ip.app.id
}
resource "scaleway_instance_private_nic" "app" {
server_id = scaleway_instance_server.app.id
private_network_id = scaleway_vpc_private_network.capstone.id
}
Fenêtre de terminal
terraform apply -auto-approve -var="image_id=$IMAGE" -var="project_id=$PROJ"

La sortie doit se terminer par Apply complete! et afficher l'adresse publique en sortie. Notez que l'adresse est déclarée explicitement plutôt que laissée à l'implicite : elle se facture même détachée, donc elle mérite d'exister dans le code.

Parce qu'un fil rouge dont l'enjeu est de tout détruire ne peut pas tourner sur une version qui échoue à détruire. Les versions 2.81.0 et 2.82.0 du provider ne savent pas supprimer une interface réseau privée attachée à un serveur : le destroy s'arrête sur Can't delete a private network interface attached to a server, erreur 412, et laisse tout en place. Ce lab a été joué le 11 septembre 2026 sur la 2.80.0, dernière version saine à cette date.

Le blocage est structurel : votre code déclare server_id dans l'interface, donc Terraform détruit l'interface avant le serveur, or c'est précisément ce que l'API refuse sur ces deux versions. La 2.83.0, publiée le 14 septembre 2026, détache l'interface avant de la supprimer et rétablit la destruction, validé le jour même sur la stack minimale de la leçon Terraform : elle convient aussi à ce lab.

Sur ce lab, avec le provider 2.80.0 et une interface privée bien attachée, le résultat est sans ambiguïté :

Destroy complete! Resources: 5 destroyed.

Code de retour 0, huit secondes, cinq ressources. L'épinglage n'est donc pas une précaution de principe, c'est ce qui sépare un démontage propre d'une facture qui court. Le détail de la régression, les deux contournements qui ne marchent pas et celui qui marche sont dans la leçon Terraform.

Ansible ne provisionne rien ici, il constate et il opère. C'est la frontière que le volet défend : Terraform possède la ressource, Ansible possède son contenu.

Le playbook lit le marqueur posé au build, contrôle qu'un paquet installé par Packer est bien présent, puis pose une configuration et rejoue la même tâche pour prouver l'idempotence.

- name: Le marqueur posé par Packer est-il présent ?
ansible.builtin.slurp:
src: /etc/socle-version
register: marqueur
- name: Opération jour 2, poser un fichier de configuration
ansible.builtin.copy:
dest: /etc/capstone.conf
content: "role=app\nprovisionne_par=terraform\nconfigure_par=ansible\n"
owner: root
group: root
mode: "0644"
register: pose
- name: Prouver l'idempotence
ansible.builtin.assert:
that:
- pose.changed
- not rejeu.changed

La sortie doit afficher ok=8 changed=1 unreachable=0 failed=0. Sur le lab, le message remonté par la tâche de contrôle était curl 8.5.0-2ubuntu10.13 était déjà dans l'image : la preuve que l'Instance démarre bien sur la construction de Packer, et non sur une image publique où ce paquet aurait été installé au démarrage.

Pourquoi la connexion SSH échoue-t-elle dans un projet neuf ?

Section intitulée « Pourquoi la connexion SSH échoue-t-elle dans un projet neuf ? »

Parce que les clés SSH de Scaleway appartiennent à un projet, et qu'un projet neuf n'en a aucune. C'est le piège le plus déroutant de ce lab, parce qu'il ne se déclenche que si vous suivez la bonne pratique du projet dédié.

Le symptôme est net et ne dit rien de sa cause :

Failed to connect to the host via ssh: root@198.51.100.20: Permission denied (publickey).

Tout le reste fonctionne. L'image est correcte, l'Instance démarre, Terraform n'a rien signalé. Le diagnostic tient en une commande :

Fenêtre de terminal
scw iam ssh-key list -o json

Sur le lab, la sortie affichait deux clés, toutes deux rattachées au projet par défaut, et aucune dans le projet fraîchement créé. Une Instance ne reçoit que les clés de son propre projet : celle du lab n'en recevait donc aucune.

Fenêtre de terminal
scw iam ssh-key create name=lab-bob project-id="$PROJ" \
public-key="$(cat ~/.ssh/id_ed25519.pub)" -o json

La sortie doit afficher le project_id du projet de lab et l'empreinte de votre clé. Après cet ajout, le playbook est passé du premier coup.

C'est ici que le lab prend tout son sens. La commande rend la main, annonce un succès complet, et pourtant trois objets survivent.

  1. Détruisez la stack Terraform.

    Fenêtre de terminal
    terraform destroy -auto-approve -var="image_id=$IMAGE" -var="project_id=$PROJ"

    La sortie doit afficher Destroy complete! Resources: 5 destroyed.

  2. Recomptez les trois familles.

    Fenêtre de terminal
    scw instance image list zone=fr-par-1 project-id="$PROJ" -o json
    scw block snapshot list zone=fr-par-1 project-id="$PROJ" -o json
    scw block volume list zone=fr-par-1 project-id="$PROJ" -o json

    Les trois répondent une ressource chacune. Terraform ne les a jamais connues : elles ne figuraient pas dans son état, donc il n'avait aucune raison de les toucher.

  3. Essayez volontairement le mauvais ordre, pour voir le refus.

    Les deux objets n'ayant pas de nom prévisible, on les retrouve par famille :

    Fenêtre de terminal
    SNAP=$(scw block snapshot list zone=fr-par-1 project-id="$PROJ" -o json \
    | jq -r '.snapshots[0].id')
    VOL=$(scw block volume list zone=fr-par-1 project-id="$PROJ" -o json \
    | jq -r '.volumes[0].id')
    scw block snapshot delete snapshot-id="$SNAP" zone=fr-par-1
    scaleway-sdk-go: precondition failed: , Snapshot is currently in use, deletion is not permitted.
  4. Reprenez dans le bon ordre, image puis snapshot puis volume.

    Fenêtre de terminal
    scw instance image delete "$IMAGE" zone=fr-par-1
    scw block snapshot delete snapshot-id="$SNAP" zone=fr-par-1
    scw block volume delete volume-id="$VOL" zone=fr-par-1

    Les trois doivent répondre has been successfully deleted.

Ce tableau est ce qu'il faut garder de ce lab. Chaque ligne a été provoquée volontairement le 11 septembre 2026, et la colonne de droite dit ce que vous payez si vous vous arrêtez trop tôt.

Vous supprimezCe qui bloque, ou ce qui survitMessage exactCe que ça coûte
la stack Terraformrien ne bloque, mais l'image, le snapshot et le volume surviventDestroy complete! Resources: 5 destroyed.trois objets au gigaoctet, indéfiniment
le snapshot avant l'imagel'image le verrouilleSnapshot is currently in use, deletion is not permitted.rien, le refus protège
l'imagerienImage has been successfully deleted.-
le volume orphelinrien ne le signale, references=[]Volume has been successfully deleted.10 Go oubliés par build
le projetle groupe de sécurité par défaut créé par Scalewayresource is still in use, all resources are not deletedrien, mais le projet reste
  • Les trois outils s'enchaînent sans recouvrement : Packer a posé un marqueur, Terraform a démarré la machine dessus, Ansible l'a lu depuis l'intérieur, avec ok=8 changed=1 failed=0.
  • L'image construite est bien celle qui démarre : curl 8.5.0-2ubuntu10.13 était déjà dans l'image, sans aucune installation au démarrage.
  • L'idempotence d'Ansible se vérifie, elle ne se suppose pas : première passe changed=true, seconde changed=false, assertion passante.
  • Le provider 2.80.0 détruit une interface privée attachée : cinq ressources, code 0, huit secondes, là où 2.81.0 et 2.82.0 échouent en 412.
  • terraform destroy ne supprime que ce qu'il a créé : après un démontage annoncé complet, une image, un snapshot Block et un volume Block restaient.
  • Ce que Packer laisse ne se retrouve pas par son nom : snapshot au nom généré, volume au nom de l'image source, aucun ne mentionnant le build.
  • Les clés SSH sont rattachées à un projet : passer à un projet dédié coupe l'accès SSH tant que la clé n'y est pas recréée.

Ces valeurs encadrent le lab. Chacune porte sa source, relevé daté ou version d'outil.

LimiteValeurSource
Durée d'un build Packer minimal68 secondes (88 s à la première passe)mesuré le 2026-09-11, DEV1-S, fr-par-1
Taille du snapshot produit10 Go, celle du volume racinescw block snapshot list, 2026-09-11
Objets laissés par un build réussi3 : image, snapshot Block, volume Blockmesuré sur trois passes
Durée d'un terraform destroy de 5 ressources8 secondes, code 0provider 2.80.0, 2026-09-11
Versions de référence du voletPacker 1.15.0 + plugin 1.5.0, provider 2.80.0, collection 0.5.0carte du volet
Versions du provider qui échouent à détruire une interface attachée2.81.0 et 2.82.0 ; rétabli en 2.83.0 le 14 septembre 2026leçon Terraform, mesures des 9 et 14 septembre 2026

Le tableau se lit par la colonne Besoin : partez de ce que vous cherchez à obtenir, et non de l'outil que vous maîtrisez le mieux.

BesoinChoixPourquoi pas un autre
Figer un paquet ou un réglage avant le démarragePackerAnsible le ferait à chaque démarrage, en allongeant le temps de mise à l'échelle
Créer réseau, adresse, machine et les détruire ensembleTerraformPacker ne connaît que l'image ; Ansible n'a pas d'état à rapprocher
Vérifier ou modifier ce qui vit dans une machine déjà làAnsibleTerraform recréerait la ressource au lieu de la corriger
Retrouver ce que personne ne gère, image ou volume orphelinle CLI, par familleaucun des trois outils ne les a dans son périmètre

Operational Excellence : qui possède quoi, et qui le sait ?

Section intitulée « Operational Excellence : qui possède quoi, et qui le sait ? »

La question clé : pour chaque ressource de votre compte, sauriez-vous dire quel outil la gère ?

Ce lab montre le trou : trois objets n'appartenaient à aucun des trois outils. Packer les a créés puis s'est retiré, Terraform ne les a jamais eus dans son état, Ansible ne gère pas d'infrastructure.

La discipline : tenir une frontière explicite entre ce qui est déclaré et ce qui est produit, et traiter tout ce qui est produit comme un déchet à ramasser volontairement.

Cost Optimization : que payez-vous après un démontage réussi ?

Section intitulée « Cost Optimization : que payez-vous après un démontage réussi ? »

La question clé : votre commande de destruction couvre-t-elle ce que votre chaîne de construction a produit ?

Non, par construction. Un destroy couvre l'état Terraform, rien de plus. Les images et snapshots s'accumulent build après build, au gigaoctet, sans jamais déclencher d'alerte.

La discipline : après chaque build, relister instance image, block snapshot et block volume, dans un projet dédié pour que la liste soit sans ambiguïté.

Security : que contient l'image que vous distribuez ?

Section intitulée « Security : que contient l'image que vous distribuez ? »

La question clé : une image construite automatiquement embarque-t-elle des traces du build lui-même ?

Une image capture l'état de la machine au moment du snapshot, clés temporaires et journaux compris. C'est la raison du nettoyage cloud-init clean et de la suppression de authorized_keys, même si, dans ce lab précis, ce n'était pas la cause de la panne SSH.

La discipline : terminer tout provisionnement d'image par un nettoyage des identités et des états, et considérer une image comme un artefact distribuable, donc inspectable.

Tous ces messages ont été obtenus en lab le 11 septembre 2026.

SymptômeCauseSolution
Permission denied (publickey) sur une machine qui démarre bienles clés SSH appartiennent au projet, et le projet est neufscw iam ssh-key create project-id=<projet> public-key=…
Snapshot is currently in use, deletion is not permitted.l'image verrouille son snapshotsupprimer l'image d'abord
scw instance snapshot list renvoie [] après un buildle snapshot vit dans block snapshot, pas dans la famille historiquechercher dans block snapshot et block volume
Error: ... Can't delete a private network interface attached to a serverrégression du provider en 2.81.0 et 2.82.0épingler 2.80.0 ou 2.83.0
This object has no argument ... named "public_ip"en 2.80.0, public_ips est un bloc, pas un attributdéclarer scaleway_instance_ip et lire son address
resource is still in use, all resources are not deleted sur un projet videle groupe de sécurité par défaut subsistescw instance security-group delete <id> zone=fr-par-1

Ces cinq erreurs ont un point commun : elles tiennent l'absence de message d'erreur pour une preuve que tout va bien.

AntipatternConséquenceDiscipline
Considérer terraform destroy comme un nettoyage completles artefacts de build s'accumulent au gigaoctetrelister les familles que Terraform ne gère pas
Chercher les restes d'un build par leur nomle snapshot a un nom généré, le volume celui de l'image sourcechercher par famille, jamais par étiquette
Prendre la dernière version d'un provider par réflexeune régression de destruction laisse des ressources facturéesépingler, et rejouer la destruction avant de remonter de version
Laisser Ansible installer ce que l'image pourrait porterchaque démarrage recommence, la mise à l'échelle ralentitfiger dans l'image ce qui ne change pas
Empiler une seconde hypothèse sans valider la premièreon corrige au hasard et on croit avoir comprisvérifier qu'un correctif change le symptôme avant d'en ajouter un autre

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • Les trois outils se relaient : Packer fige, Terraform déclare et détruit, Ansible exploite ce qui vit dans la machine.
  • Un build Packer réussi laisse trois objets : une image, un snapshot Block et un volume orphelin, mesuré sur trois passes.
  • La famille instance snapshot est vide : tout est dans block snapshot et block volume, ce qui trompe les vieux scripts de nettoyage.
  • Aucun de ces objets ne porte le nom du build : snp-xenodochial-bartik d'un côté, Ubuntu 24.04 Noble Numbat_sbs_volume_0 de l'autre.
  • terraform destroy ne supprime que son état : cinq ressources détruites, code 0, et trois objets toujours facturés.
  • L'image verrouille son snapshot : Snapshot is currently in use, deletion is not permitted. avant d'avoir supprimé l'image.
  • Le provider s'épingle sur 2.80.0 ou 2.83.0 : 2.81.0 et 2.82.0 échouent à détruire une interface privée attachée.
  • Les clés SSH sont liées au projet : un projet dédié n'hérite de rien, et SSH échoue sans que la cause apparaisse.
  • En 2.80.0, public_ips est un bloc : déclarer scaleway_instance_ip explicitement est plus sûr, et rend visible une adresse facturée même détachée.

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