Comment lire cette page. Dix étapes, une commande à la fois. Pour chacune : la commande, la sortie exacte de la machine du cours (Terraform 1.12.2), et ce qu'il faut regarder. Tape toi-même chaque commande. Les blocs « Pour bien comprendre » sont facultatifs. Les commandes
terraform …sont identiques partout ; quand une commande dépend du système, les deux versions sont données, l'une sous l'autre. Si le dossier de l'atelier 1 n'existe plus ou sistate listne répond paslocal_file.bonjour, refais l'atelier fondamental 1 d'abord (kit :https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr).
L'atelier 1 t'a fait créer une ressource et lire le state. Mais une infrastructure ne se crée pas une fois pour toutes : elle se modifie, quelqu'un la casse par mégarde, et un jour on la démonte. Ce sont les trois gestes de cet atelier. Tu vas d'abord supprimer bonjour.txt à la main, comme un collègue qui « nettoie » un dossier, et voir Terraform s'en apercevoir puis le recréer : c'est la dérive de la leçon 01. Tu vas ensuite changer une ligne de main.tf et découvrir que, pour le provider local, changer le contenu d'un fichier ne le modifie pas : il le remplace, avec un symbole que tu dois savoir reconnaître avant de dire yes. Enfin tu vas tout détruire, et vérifier que le relevé est vide et que le kit compte zéro.
Toujours l'architecte. Une dérive (drift), c'est un ouvrier qui a déplacé un mur sans le dire : le bâtiment ne correspond plus au relevé ; l'architecte s'en aperçoit quand il va voir sur place (Refreshing state...). Modifier sur place (~), c'est repeindre un mur : le mur reste le même. Remplacer (-/+), c'est démolir le mur et en reconstruire un au même endroit : il a un nouveau numéro dans le relevé. Détruire (-), c'est démolir sans reconstruire. Le devis dit toujours lequel des trois il prévoit ; toi, tu le lis avant de signer.
| Symbole dans le plan | Phrase dans le plan | Ce que Terraform fait | Dans cet atelier |
|---|---|---|---|
+ | will be created | crée un objet nouveau | étape 3 : le fichier supprimé à la main |
~ | will be updated in-place | change un attribut, l'objet reste | jamais pour content d'un local_file |
-/+ | must be replaced | détruit l'objet, puis en crée un autre | étape 5 : content change |
- | will be destroyed | supprime l'objet | étape 8 : destroy |
Le même terminal que pour l'atelier 1, dans le dossier travail/atelier-1 du kit lab-terraform. Et VS Code pour modifier main.tf. Les commandes terraform … sont identiques partout ; les commandes pour supprimer, lire ou lister un fichier sont données dans les deux versions.
Windows (PowerShell), depuis la racine du kit :
cd travail\atelier-1Linux, macOS, WSL 2, Git Bash :
cd travail/atelier-1Puis :
terraform state listlocal_file.bonjourCe que la commande demande : « Qu'est-ce que tu gères dans ce dossier ? »
À regarder : une ligne, local_file.bonjour. C'est l'état où l'atelier 1 t'a laissé. Si tu lis No state file was found! ou rien du tout, le dossier n'est pas le bon ou la ressource a été détruite : refais l'atelier 1 avant de continuer.
Windows (PowerShell) :
Remove-Item .\bonjour.txt
Get-ChildItemLinux, macOS, WSL 2, Git Bash :
rm bonjour.txt
lsCe que les commandes demandent : « Supprime bonjour.txt, puis montre-moi le dossier. »
À regarder : bonjour.txt n'est plus là ; main.tf et terraform.tfstate y sont toujours. Tu viens de faire ce qu'un collègue fait dans la console AWS quand il supprime un bucket « qui ne servait à rien » : le réel a changé, mais ni le code ni le relevé ne le savent encore.
terraform planlocal_file.bonjour: Refreshing state... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.bonjour will be created
+ resource "local_file" "bonjour" {
+ content = "Bonjour Terraform"
+ content_base64sha256 = (known after apply)
…
+ filename = "./bonjour.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.Ce que la commande demande : « Compare le code, le relevé et le réel, et dis-moi ce qui manque. »
À regarder : Refreshing state... [id=fd9aee55…] : Terraform est allé voir le fichier que son relevé mentionne, et ne l'a pas trouvé. Résultat : il le retire de son relevé et le devis dit will be created, Plan: 1 to add. Le même plan qu'à l'atelier 1, étape 5, alors que tu n'as pas touché au code. C'est ça, une dérive : le réel a bougé sans passer par Terraform, et Terraform l'a détecté au moment du plan, pas avant.
Refreshing state... du plan (et de l'apply) qui relit le réel. Un fichier supprimé il y a trois jours n'est découvert qu'au prochain plan.+ et pas -/+ ? L'objet n'existe plus du tout : il n'y a rien à détruire, seulement à créer. -/+ (étape 5) est pour un objet qui existe encore mais qu'il faut refaire.plan suivant propose de la remettre comme le code le dit. Le plan te le montre ; à toi de décider si c'est le code ou la console qui avait raison.terraform applylocal_file.bonjour: Refreshing state... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
…
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
local_file.bonjour: Creating...
local_file.bonjour: Creation complete after 0s [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.Ce que la commande demande : « Recalcule le devis, attends mon yes, puis fais-le. »
À regarder : bonjour.txt est de retour, et son id est le même qu'avant (fd9aee55…) : l'identifiant d'un local_file est l'empreinte de son contenu, et le contenu n'a pas changé. Vérifie avec Get-Content .\bonjour.txt ou cat bonjour.txt : Bonjour Terraform. Un script n'aurait pas su qu'il fallait réparer ; Terraform l'a su parce qu'il a un relevé et un code à comparer.
Dans VS Code, modifie la ligne content de main.tf pour obtenir :
resource "local_file" "bonjour" {
filename = "${path.module}/bonjour.txt"
content = "Bonjour Terraform, deuxième version"
}Enregistre, puis :
terraform planlocal_file.bonjour: Refreshing state... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# local_file.bonjour must be replaced
-/+ resource "local_file" "bonjour" {
~ content = "Bonjour Terraform" -> "Bonjour Terraform, deuxième version" # forces replacement
~ content_base64sha256 = "SvXkP5Iqo1rJKHgTVJnjanxfsNVGhb+r52mzUqZJYyg=" -> (known after apply)
~ content_base64sha512 = "r3+PevYNJRFsHMz4H4U1DK+lg89nhO9SLJZMAqqtzPKiIv3ilrmbx5sYh2JCW9o5EF0NXanYmuxMvI2QN4Z1Ag==" -> (known after apply)
~ content_md5 = "ff3a967c227a58691a3d34a931d3eeb5" -> (known after apply)
~ content_sha1 = "fd9aee5556589b4d797e6d49f8ec894e29e57713" -> (known after apply)
~ content_sha256 = "4af5e43f922aa35ac92878135499e36a7c5fb0d54685bfabe769b352a6496328" -> (known after apply)
~ content_sha512 = "af7f8f7af60d25116c1cccf81f85350cafa583cf6784ef522c964c02aaadccf2a222fde296b99bc79b188762425bda39105d0d5da9d89aec4cbc8d9037867502" -> (known after apply)
~ id = "fd9aee5556589b4d797e6d49f8ec894e29e57713" -> (known after apply)
# (3 unchanged attributes hidden)
}
Plan: 1 to add, 0 to change, 1 to destroy.Ce que la commande demande : la même qu'à l'étape 3. Mais cette fois c'est le code qui a changé, pas le réel.
À regarder, ligne par ligne. C'est le plan le plus important du module :
| Ligne | Ce qu'elle dit |
|---|---|
-/+ destroy and then create replacement | La légende : le symbole utilisé est -/+, « détruire puis créer un remplaçant ». |
# local_file.bonjour must be replaced | Pas will be updated in-place : la ressource doit être remplacée. |
-/+ resource "local_file" "bonjour" { | Le bloc entier porte le -/+. |
~ content = "Bonjour Terraform" -> "Bonjour Terraform, deuxième version" # forces replacement | Le ~ dit que la valeur change, de l'ancienne (->) à la nouvelle. Le commentaire # forces replacement dit que c'est cet attribut qui oblige à remplacer. |
~ content_md5 = "ff3a967c…" -> (known after apply) | Les empreintes changeront ; Terraform ne connaît pas encore les nouvelles. |
~ id = "fd9aee55…" -> (known after apply) | L'identifiant va changer. Ce sera un autre objet. |
# (3 unchanged attributes hidden) | filename, directory_permission, file_permission ne bougent pas ; Terraform les cache pour la lisibilité. |
Plan: 1 to add, 0 to change, 1 to destroy. | Une destruction et une création, zéro modification. |
C'est ta première réponse-livrable : garde cette sortie.
La différence essentielle entre
~et-/+. Le~devantcontentdit quoi change. Le-/+devantresourcedit comment Terraform va s'y prendre : démolir, puis reconstruire. Pour un fichier texte, tu ne verras pas la différence. Pour une base de données,-/+veut dire que les données disparaissent avec l'ancien objet. C'est le provider qui décide, attribut par attribut ; le providerlocala décidé quecontentforce le remplacement. Toi, tu lis les trois indices (must be replaced,# forces replacement,1 to destroy) avant de taperyes.
local remplace au lieu de modifier ? Parce qu'il ne sait pas « corriger » un fichier existant : il sait l'écrire ou l'effacer. L'id d'un local_file est d'ailleurs l'empreinte SHA-1 de son contenu ; changer le contenu change l'identifiant, et un objet dont l'identifiant change est, pour Terraform, un autre objet. Sur la machine du cours, file_permission force aussi le remplacement. D'autres providers font autrement : changer une étiquette (tag) sur une ressource AWS est un ~, changer son nom est souvent un -/+.apply en production, c'est ce qu'on va lire.terraform apply -replace=local_file.bonjour remplace la ressource même sans changement de code. Utile quand un objet est corrompu. Tu le verras au projet 08.terraform applylocal_file.bonjour: Refreshing state... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
…
Plan: 1 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
local_file.bonjour: Destroying... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
local_file.bonjour: Destruction complete after 0s
local_file.bonjour: Creating...
local_file.bonjour: Creation complete after 0s [id=bd2b5f8ccbb2cbc2b9bea872211549635a76b092]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.Ce que la commande demande : la même qu'à l'étape 4.
À regarder : quatre lignes d'action au lieu de deux, dans l'ordre du symbole -/+ : Destroying... puis Destruction complete (l'ancien, fd9aee55…), puis Creating... puis Creation complete (le nouveau, bd2b5f8c…). Et la dernière ligne : Apply complete! Resources: 1 added, 0 changed, 1 destroyed. Pas 1 changed : rien n'a été modifié, un objet a été détruit et un autre créé.
Windows (PowerShell) :
Get-Content .\bonjour.txtLinux, macOS, WSL 2, Git Bash :
cat bonjour.txtBonjour Terraform, deuxième versionPuis :
terraform state show local_file.bonjour# local_file.bonjour:
resource "local_file" "bonjour" {
content = "Bonjour Terraform, deuxième version"
content_base64sha256 = "hg9L75JxH+5SDk+ZOADZinGONkirydetS0TFjN3uZ3o="
content_base64sha512 = "EjT8mRVQU2JQ+Em/rnbRKYfe7E2UqIWw8yQMMtaatimH5XCKg4ZomW4PI5yO5jhujdSrU04vX17HE2kaqEVSkQ=="
content_md5 = "9a1a1355b48e28ad727200e450b851e1"
content_sha1 = "bd2b5f8ccbb2cbc2b9bea872211549635a76b092"
content_sha256 = "860f4bef92711fee520e4f993800d98a718e3648abc9d7ad4b44c58cddee677a"
content_sha512 = "1234fc991550536250f849bfae76d12987deec4d94a885b0f3240c32d69ab62987e5708a838668996e0f239c8ee6386e8dd4ab534e2f5f5ec713691aa8455291"
directory_permission = "0777"
file_permission = "0777"
filename = "./bonjour.txt"
id = "bd2b5f8ccbb2cbc2b9bea872211549635a76b092"
}Ce que les commandes demandent : « Montre-moi le fichier réel, puis ce que le relevé en dit. »
À regarder : le fichier contient la deuxième version. Dans le relevé, content est la nouvelle valeur, toutes les empreintes ont changé, et l'id est bd2b5f8c…, celui affiché par Creation complete à l'étape 6. Compare avec le state show de l'atelier 1, étape 9 : même adresse local_file.bonjour, mais un autre objet. Le code, le relevé et le réel sont de nouveau d'accord : un terraform plan maintenant dirait No changes.
terraform destroylocal_file.bonjour: Refreshing state... [id=bd2b5f8ccbb2cbc2b9bea872211549635a76b092]
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# local_file.bonjour will be destroyed
- resource "local_file" "bonjour" {
- content = "Bonjour Terraform, deuxième version" -> null
- content_base64sha256 = "hg9L75JxH+5SDk+ZOADZinGONkirydetS0TFjN3uZ3o=" -> null
…
- filename = "./bonjour.txt" -> null
- id = "bd2b5f8ccbb2cbc2b9bea872211549635a76b092" -> null
}
Plan: 0 to add, 0 to change, 1 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
local_file.bonjour: Destroying... [id=bd2b5f8ccbb2cbc2b9bea872211549635a76b092]
local_file.bonjour: Destruction complete after 0s
Destroy complete! Resources: 1 destroyed.Ce que la commande demande : « Détruis tout ce que tu gères ici ; montre-moi le devis et attends mon yes. »
À regarder : le troisième symbole, - seul, et chaque attribut qui passe à -> null (« plus rien »). La question est plus grave qu'à l'apply : Do you really want to destroy all resources? et There is no undo. Tape yes. La phrase à attendre : Destroy complete! Resources: 1 destroyed. Le fichier a disparu du dossier.
terraform state listPuis, pour voir ce que répond Terraform quand on lui demande une ressource qu'il ne gère plus :
terraform state show local_file.bonjourNo instance found for the given address!
This command requires that the address references one specific instance.
To view the available instances, use "terraform state list". Please modify
the address to reference a specific instance.Ce que les commandes demandent : « Liste ce que tu gères », puis « montre-moi local_file.bonjour ».
À regarder : state list ne répond rien : pas une ligne, pas d'erreur, un relevé vide. C'est différent du No state file was found! de l'atelier 1 avant apply : ici le fichier terraform.tfstate existe, mais sa liste de ressources est vide. Et state show répond No instance found for the given address! : l'adresse est correcte, mais plus rien n'y correspond, et Terraform te renvoie vers state list. C'est ta deuxième réponse-livrable : la sortie vide de terraform state list.
Windows (PowerShell) :
Get-ChildItem -ForceLinux, macOS, WSL 2, Git Bash :
ls -latotal 17
drwxr-xr-x 1 rehou 197609 0 Sep 15 14:57 .
drwxr-xr-x 1 rehou 197609 0 Sep 15 14:57 ..
drwxr-xr-x 1 rehou 197609 0 Sep 15 14:57 .terraform
-rw-r--r-- 1 rehou 197609 1228 Sep 15 14:57 .terraform.lock.hcl
-rw-r--r-- 1 rehou 197609 130 Sep 15 14:57 main.tf
-rw-r--r-- 1 rehou 197609 181 Sep 15 14:57 terraform.tfstate
-rw-r--r-- 1 rehou 197609 1653 Sep 15 14:57 terraform.tfstate.backup(Listing ls -la de la machine du cours ; Get-ChildItem -Force affiche les mêmes cinq noms : .terraform, .terraform.lock.hcl, main.tf, terraform.tfstate, terraform.tfstate.backup.)
Ce que la commande demande : « Montre-moi tout le dossier, fichiers cachés compris. »
À regarder : bonjour.txt n'est plus là. main.tf est intact : détruire les ressources ne touche jamais au code. terraform.tfstate est encore là mais minuscule (181 octets), et un terraform.tfstate.backup est apparu : la copie du relevé d'avant le destroy, que Terraform garde par prudence. .terraform/ et .terraform.lock.hcl restent aussi : si tu relances apply, pas besoin de refaire init.
Ouvre le state pour le voir vide, sans le modifier (Get-Content .\terraform.tfstate ou cat terraform.tfstate) :
{
"version": 4,
"terraform_version": "1.12.2",
"serial": 6,
"lineage": "69fb219e-a88c-a97f-5aed-a33e7c34ff69",
"outputs": {},
"resources": [],
"check_results": null
}"resources": [] : rien. Le "serial" compte les écritures dans le state (six ici : deux apply à l'atelier 1, puis réparation, remplacement et destruction) ; le "lineage" est l'identifiant de ce state, différent chez toi.
Remonte à la racine du kit et lance le compteur :
Windows (PowerShell) :
cd ..\..
.\labo.ps1 etatLinux, macOS, WSL 2, Git Bash :
cd ../..
./labo.sh etattravail atelier-1 : aucune ressource
Ressources encore gérées : 0 (0 attendu à la fin d'une séance).Réponse attendue : aucune ressource et Ressources encore gérées : 0. C'est la fin de séance propre que chaque projet du cours exigera, quand les ressources coûteront de l'argent.
bonjour.txt à la main et vu plan proposer will be created sans que le code ait changé : une dérive, détectée au Refreshing state....apply a recréé le fichier avec le même id.content et lu les trois indices du remplacement : must be replaced, # forces replacement, Plan: 1 to add, 0 to change, 1 to destroy.apply a affiché Destroying... puis Creating..., et Resources: 1 added, 0 changed, 1 destroyed. ; l'id a changé.destroy a demandé yes avec There is no undo. et répondu Destroy complete! Resources: 1 destroyed.state list ne rend rien ; state show rend No instance found for the given address! ; terraform.tfstate contient "resources": [].etat rend Ressources encore gérées : 0.state list répond No state file was found! → Tu n'es pas dans travail/atelier-1, ou l'atelier 1 n'a pas été fait jusqu'à l'apply. Vérifie le dossier (Get-ChildItem -Force ou ls -la doit montrer terraform.tfstate), sinon refais l'atelier 1.state list ne répond rien → La ressource a déjà été détruite (un destroy à la fin de l'atelier 1). Relance terraform apply, yes, puis reprends à l'étape 2.No changes. → Le fichier n'a pas été supprimé (faute de frappe dans Remove-Item ou rm, ou mauvais dossier). Vérifie avec Get-ChildItem ou ls, supprime-le, relance plan.No changes. → main.tf n'a pas été enregistré après la modification (VS Code : point blanc dans l'onglet). Ctrl+S puis relance.Error: Unterminated template string → Le guillemet fermant de la nouvelle valeur content a sauté pendant l'édition. Remets-le.# forces replacement n'est pas content → Tu as modifié un autre argument en même temps (par exemple file_permission = "0644"). Pour un local_file, celui-là aussi force le remplacement (vérifié : ~ file_permission = "0777" -> "0644" # forces replacement). Remets le fichier comme à l'étape 5, seule la ligne content doit changer.Apply cancelled. → Autre chose que yes a été tapé. Relance, tape yes en entier.destroy dit No changes. No objects need to be destroyed. puis Destroy complete! Resources: 0 destroyed. → Il n'y a déjà plus rien dans le state (Either you have not created any objects yet or the existing objects were already deleted outside of Terraform.). Passe à l'étape 9.destroy, tu crois que le state n'a pas été nettoyé parce que terraform.tfstate existe encore → C'est normal. Ouvre-le : "resources": []. Le fichier reste, vide, avec un .backup à côté.etat répond Ressources encore gérées : 1 → Le destroy a été annulé ou fait dans un autre dossier. Reviens dans travail/atelier-1, terraform destroy, yes, relance etat.