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.
Ce que vous allez faire
Section intitulée « Ce que vous allez faire »- 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 destroylaisse 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.
| Poste | Durée mesurée | Ordre de grandeur |
|---|---|---|
| Build Packer, Instance et adresse | 68 secondes | moins d'un centime |
| Instance provisionnée par Terraform | quelques minutes | moins d'un centime |
| Image, snapshot et volume laissés derrière | indéfiniment | c'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.
Étape 1 : Packer construit l'image
Section intitulée « Étape 1 : Packer construit l'image »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.
-
É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}} -
Installez le plugin, puis construisez.
Fenêtre de terminal packer init socle.pkr.hcltime packer build -var "horodatage=$(date +%H%M%S)" socle.pkr.hclLa 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. -
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 jsonLa sortie doit afficher votre image avec son
root_volume.
Où Packer range-t-il ce qu'il produit ?
Section intitulée « Où Packer range-t-il ce qu'il produit ? »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 :
scw instance snapshot list zone=fr-par-1 -o jsonscw block snapshot list zone=fr-par-1 -o jsonscw block volume list zone=fr-par-1 -o jsonRelevé 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 :
| Famille | Ce qu'on y trouve | Nom relevé |
|---|---|---|
instance snapshot | rien | - |
block snapshot | le snapshot de l'image, 10 Go | snp-xenodochial-bartik |
block volume | un 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.
Étape 2 : Terraform provisionne sur cette image
Section intitulée « Étape 2 : Terraform provisionne sur cette image »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}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.
Pourquoi épingler le provider en 2.80.0 ?
Section intitulée « Pourquoi épingler le provider en 2.80.0 ? »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.
Étape 3 : Ansible vérifie depuis l'intérieur
Section intitulée « Étape 3 : Ansible vérifie depuis l'intérieur »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.changedLa 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 :
scw iam ssh-key list -o jsonSur 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.
scw iam ssh-key create name=lab-bob project-id="$PROJ" \ public-key="$(cat ~/.ssh/id_ed25519.pub)" -o jsonLa 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.
Étape 4 : détruire, puis regarder ce qui reste
Section intitulée « Étape 4 : détruire, puis regarder ce qui reste »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.
-
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. -
Recomptez les trois familles.
Fenêtre de terminal scw instance image list zone=fr-par-1 project-id="$PROJ" -o jsonscw block snapshot list zone=fr-par-1 project-id="$PROJ" -o jsonscw block volume list zone=fr-par-1 project-id="$PROJ" -o jsonLes 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.
-
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-1scaleway-sdk-go: precondition failed: , Snapshot is currently in use, deletion is not permitted. -
Reprenez dans le bon ordre, image puis snapshot puis volume.
Fenêtre de terminal scw instance image delete "$IMAGE" zone=fr-par-1scw block snapshot delete snapshot-id="$SNAP" zone=fr-par-1scw block volume delete volume-id="$VOL" zone=fr-par-1Les trois doivent répondre
has been successfully deleted.
Le tableau des dépendances de destruction
Section intitulée « Le tableau des dépendances de destruction »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 supprimez | Ce qui bloque, ou ce qui survit | Message exact | Ce que ça coûte |
|---|---|---|---|
| la stack Terraform | rien ne bloque, mais l'image, le snapshot et le volume survivent | Destroy complete! Resources: 5 destroyed. | trois objets au gigaoctet, indéfiniment |
| le snapshot avant l'image | l'image le verrouille | Snapshot is currently in use, deletion is not permitted. | rien, le refus protège |
| l'image | rien | Image has been successfully deleted. | - |
| le volume orphelin | rien ne le signale, references=[] | Volume has been successfully deleted. | 10 Go oubliés par build |
| le projet | le groupe de sécurité par défaut créé par Scaleway | resource is still in use, all resources are not deleted | rien, mais le projet reste |
Ce que ce lab a prouvé
Section intitulée « Ce que ce lab a prouvé »- 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, secondechanged=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 destroyne 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.
Limites, quotas et plafonds
Section intitulée « Limites, quotas et plafonds »Ces valeurs encadrent le lab. Chacune porte sa source, relevé daté ou version d'outil.
| Limite | Valeur | Source |
|---|---|---|
| Durée d'un build Packer minimal | 68 secondes (88 s à la première passe) | mesuré le 2026-09-11, DEV1-S, fr-par-1 |
| Taille du snapshot produit | 10 Go, celle du volume racine | scw block snapshot list, 2026-09-11 |
| Objets laissés par un build réussi | 3 : image, snapshot Block, volume Block | mesuré sur trois passes |
Durée d'un terraform destroy de 5 ressources | 8 secondes, code 0 | provider 2.80.0, 2026-09-11 |
| Versions de référence du volet | Packer 1.15.0 + plugin 1.5.0, provider 2.80.0, collection 0.5.0 | carte du volet |
| Versions du provider qui échouent à détruire une interface attachée | 2.81.0 et 2.82.0 ; rétabli en 2.83.0 le 14 septembre 2026 | leçon Terraform, mesures des 9 et 14 septembre 2026 |
Quel outil pour quelle étape de ce lab ?
Section intitulée « Quel outil pour quelle étape de ce lab ? »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.
| Besoin | Choix | Pourquoi pas un autre |
|---|---|---|
| Figer un paquet ou un réglage avant le démarrage | Packer | Ansible le ferait à chaque démarrage, en allongeant le temps de mise à l'échelle |
| Créer réseau, adresse, machine et les détruire ensemble | Terraform | Packer 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à | Ansible | Terraform recréerait la ressource au lieu de la corriger |
| Retrouver ce que personne ne gère, image ou volume orphelin | le CLI, par famille | aucun des trois outils ne les a dans son périmètre |
Le fil rouge sous l'angle Well-Architected
Section intitulée « Le fil rouge sous l'angle Well-Architected »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.
Pièges courants
Section intitulée « Pièges courants »Tous ces messages ont été obtenus en lab le 11 septembre 2026.
| Symptôme | Cause | Solution |
|---|---|---|
Permission denied (publickey) sur une machine qui démarre bien | les clés SSH appartiennent au projet, et le projet est neuf | scw iam ssh-key create project-id=<projet> public-key=… |
Snapshot is currently in use, deletion is not permitted. | l'image verrouille son snapshot | supprimer l'image d'abord |
scw instance snapshot list renvoie [] après un build | le snapshot vit dans block snapshot, pas dans la famille historique | chercher dans block snapshot et block volume |
Error: ... Can't delete a private network interface attached to a server | ré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 attribut | déclarer scaleway_instance_ip et lire son address |
resource is still in use, all resources are not deleted sur un projet vide | le groupe de sécurité par défaut subsiste | scw instance security-group delete <id> zone=fr-par-1 |
Antipatterns à éviter
Section intitulée « Antipatterns à éviter »Ces cinq erreurs ont un point commun : elles tiennent l'absence de message d'erreur pour une preuve que tout va bien.
| Antipattern | Conséquence | Discipline |
|---|---|---|
Considérer terraform destroy comme un nettoyage complet | les artefacts de build s'accumulent au gigaoctet | relister les familles que Terraform ne gère pas |
| Chercher les restes d'un build par leur nom | le snapshot a un nom généré, le volume celui de l'image source | chercher par famille, jamais par étiquette |
| Prendre la dernière version d'un provider par réflexe | une 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 porter | chaque démarrage recommence, la mise à l'échelle ralentit | figer dans l'image ce qui ne change pas |
| Empiler une seconde hypothèse sans valider la première | on corrige au hasard et on croit avoir compris | vérifier qu'un correctif change le symptôme avant d'en ajouter un autre |
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »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
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
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »- 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 snapshotest vide : tout est dansblock snapshotetblock volume, ce qui trompe les vieux scripts de nettoyage. - Aucun de ces objets ne porte le nom du build :
snp-xenodochial-bartikd'un côté,Ubuntu 24.04 Noble Numbat_sbs_volume_0de l'autre. terraform destroyne 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_ipsest un bloc : déclarerscaleway_instance_ipexplicitement est plus sûr, et rend visible une adresse facturée même détachée.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Container Registry : distribuer les images que vous construisez : la suite directe de l'image dorée, une fois qu'il faut la publier plutôt que la garder.
- Premier cluster Kapsule : la cible qui consomme ces images, et ce que Scaleway y injecte sans qu'on le demande.
- PostgreSQL en réseau privé : sauvegarder, perdre, restaurer : la brique que cette chaîne ne sait pas encore provisionner, et son plan de reprise.
Ressources externes
Section intitulée « Ressources externes »- Plugin Packer Scaleway : les sources, les options du builder et les notes de version.
- Provider Terraform Scaleway : la référence des ressources et de leurs attributs, version par version.
- Documentation Block Storage : le cycle de vie des volumes et des snapshots, et leur facturation au gigaoctet.