Atelier fondamental 1 — Terraform : un bloc, un plan, un state

Pratique guidée16 min
Durée
20 min
Module
1/7
Prérequis
Terraform installé (terraform version répond) et le kit cloné dans lab-terraform (projet 01, annexe A.1 à A.4 ou B.1 à B.4)
Tu vas construire
un projet Terraform réduit à l'os, un seul bloc de trois lignes, un fichier bonjour.txt créé par Terraform, et un state que tu vas lire
Livrable
la sortie de terraform state show local_file.bonjour de l'étape 9

Comment lire cette page. Dix étapes, une commande à la fois. Pour chacune : la commande à taper, la sortie exacte de la machine du cours (Terraform 1.12.2), et ce qu'il faut regarder dedans. Tape toi-même chaque commande (pas de copier-coller) : c'est en écrivant init, plan, apply, state que les mots rentrent. Les blocs « Pour bien comprendre » sont facultatifs ; ouvre-les si une étape te laisse un doute. Les commandes terraform … sont identiques sous Windows, Linux et macOS ; quand une commande dépend du système (créer, lire, lister un fichier), les deux versions sont données, l'une sous l'autre. Si Terraform n'est pas installé ou si le kit n'est pas cloné, retourne au projet 01, section En bref (kit : https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr).

Objectif

Le projet 01 t'a fait dérouler treize commandes sur un main.tf de quinze lignes, avec un bloc terraform, un bloc provider et un bloc resource. Tu as vu les phrases défiler, mais quinze lignes, c'est déjà beaucoup pour savoir ce qui est indispensable. Ici, tu repars de zéro avec trois lignes utiles : un seul bloc resource, sans rien autour. Tu vas voir Terraform refuser de travailler sans init, puis lui faire écrire son devis, l'exécuter, et lire ce qu'il a noté dans son relevé. À la fin, tu sais ce qu'est un bloc, un plan et un state parce que tu les as fabriqués toi-même, l'un après l'autre.

Le vocabulaire en une image

Reprends l'architecte de la leçon 01. Un bloc resource est une ligne du plan de l'architecte : « ici, un fichier, avec ce nom et ce contenu ». Le plan (la commande) est le devis : ce que Terraform ferait pour que le réel ressemble au dessin. Le state est le relevé : ce que Terraform a effectivement construit, sous quel identifiant, avec quelles valeurs. Trois objets, trois commandes pour les voir : le bloc se lit dans main.tf, le devis avec terraform plan, le relevé avec terraform state show.

TerraformCe que c'estDans cet atelier
bloc resource "type" "nom"une chose à gérer, décrite dans un fichier .tfresource "local_file" "bonjour"
type de ressourcece que c'est, fourni par un providerlocal_file (un fichier sur le disque)
nom localcomment tu l'appelles dans ton codebonjour
adressetype . nom, pour en parler ailleurslocal_file.bonjour
argumentune valeur que tu écris dans le blocfilename, content
attributune valeur que le provider calculeid, content_md5
planle devis : ce qui manque entre le code et le réelPlan: 1 to add, 0 to change, 0 to destroy.
statele relevé : ce que Terraform a construitterraform.tfstate, lu par state list et state show

Où taper

Un terminal (PowerShell sous Windows ; bash ou zsh sous Linux, macOS, WSL 2 ou Git Bash), ouvert à la racine du kit lab-terraform, celle qui contient labo.ps1 et labo.sh. Et VS Code pour écrire le fichier. Les commandes terraform … s'écrivent exactement de la même façon partout ; seules les commandes pour créer, lire ou lister un fichier changent, et elles sont données dans les deux versions à chaque fois.

Étape 1 — Créer un dossier vide

Windows (PowerShell) :

powershell
.\labo.ps1 nouveau atelier-1
cd travail\atelier-1

Linux, macOS, WSL 2, Git Bash :

bash
./labo.sh nouveau atelier-1
cd travail/atelier-1
text
Dossier travail/atelier-1 créé (ignoré par Git). Tapez :
  cd travail/atelier-1

Ce que la commande demande : crée-moi un dossier de travail vide, travail/atelier-1, que Git ignorera (sous Windows le script écrit travail\atelier-1, avec la barre oblique inverse).

À regarder : le dossier est vide. Rien n'est encore un projet Terraform : c'est main.tf, puis init, qui vont le rendre tel.

Étape 2 — Écrire un bloc, trois lignes

Dans VS Code (code . depuis le dossier), crée un fichier main.tf et tape exactement :

hcl
resource "local_file" "bonjour" {
  filename = "${path.module}/bonjour.txt"
  content  = "Bonjour Terraform"
}

Ce que le fichier demande : « Terraform, gère pour moi un fichier sur le disque (local_file), que j'appelle bonjour ; il s'appelle bonjour.txt, dans ce dossier, et il contient Bonjour Terraform. »

À regarder : il n'y a ni bloc terraform, ni bloc provider. Rien que la ressource. Terraform va déduire tout le reste du mot local_file. Vérifie que le fichier est bien là et bien nommé :

Windows (PowerShell) :

powershell
Get-ChildItem
Get-Content .\main.tf

Linux, macOS, WSL 2, Git Bash :

bash
ls -la
cat main.tf

Une seule entrée, main.tf, et les quatre lignes du fichier. Si tu vois main.tf.txt, renomme-le.

Pour bien comprendre
  • "local_file" puis "bonjour" : le type, puis le nom. Le type est imposé par le provider (local_file existe ; local_fichier n'existe pas, tu aurais Error: Invalid resource type). Le nom est à toi : bonjour, message, mon_fichier. Ensemble, ils forment l'adresse local_file.bonjour que tu retrouveras dans le plan et dans le state.
  • filename et content sont des arguments : des valeurs que tu donnes. Pour local_file, content est obligatoire (sans lui : Error: Invalid Attribute Combination), filename aussi.
  • ${path.module} vaut « le dossier où se trouve ce fichier .tf » ; Terraform l'affichera ./bonjour.txt. Sans lui, filename = "bonjour.txt" marcherait aussi ici ; le projet 01 explique pourquoi on prend l'habitude de l'écrire.
  • Les espaces avant = sont alignés (filename =, content =) : c'est le style de terraform fmt. Ça ne change rien au sens.

Étape 3 — Demander un plan trop tôt

text
terraform plan
text
Error: Inconsistent dependency lock file

The following dependency selections recorded in the lock file are
inconsistent with the current configuration:
  - provider registry.terraform.io/hashicorp/local: required by this configuration but no version is selected

To make the initial dependency selections that will initialize the dependency
lock file, run:
  terraform init

Ce que la commande demande : « Dis-moi ce que tu ferais pour que le réel ressemble à mon code. »

À regarder : Terraform refuse, et explique. Il a lu local_file, en a déduit qu'il lui faut le provider registry.terraform.io/hashicorp/local, et constate qu'il ne l'a pas (no version is selected). La dernière ligne est la solution : terraform init. C'est l'erreur que tout débutant rencontre une fois ; maintenant tu sais la lire.

Étape 4 — Ouvrir le chantier

text
terraform init
text
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/local...
- Installing hashicorp/local v2.9.1...
- Installed hashicorp/local v2.9.1 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

Ce que la commande demande : « Télécharge les providers que mon code réclame et prépare ce dossier. »

À regarder : Finding latest version of hashicorp/local... : sans bloc required_providers, Terraform prend la version la plus récente (v2.9.1 sur la machine du cours ; chez toi, peut-être plus). Dans le projet 01, la même ligne disait Finding hashicorp/local versions matching "~> 2.5"... parce que le code fixait une contrainte. Puis la phrase à attendre : Terraform has been successfully initialized!

Liste le dossier, avec les fichiers cachés :

Windows (PowerShell) :

powershell
Get-ChildItem -Force

Linux, macOS, WSL 2, Git Bash :

bash
ls -la

Trois entrées : .terraform (un dossier, le provider téléchargé), .terraform.lock.hcl (la version choisie, notée) et ton main.tf. Pas encore de bonjour.txt, pas encore de terraform.tfstate : init n'a rien construit.

Pour bien comprendre
  • init ne touche jamais à tes ressources. Tu peux le relancer autant de fois que tu veux. Il ne fait que préparer le dossier.
  • .terraform/ est lourd et se retélécharge (environ 18 Mo ici) : il ne va jamais dans Git. .terraform.lock.hcl est léger et précieux : il va dans Git, pour que ton collègue obtienne la même version du provider.
  • Pourquoi « latest » est un piège en équipe. Aujourd'hui v2.9.1 ; dans six mois, v3.0.0 avec un comportement différent. Le bloc required_providers du projet 01 (version = "~> 2.5") évite la surprise. Pour un atelier de vingt minutes, on s'en passe.

Étape 5 — Lire le devis

text
terraform plan
text
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)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./bonjour.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.

Ce que la commande demande : la même qu'à l'étape 3. Cette fois Terraform a son provider, et il répond.

À regarder, ligne par ligne, parce que c'est la sortie que tu liras le plus souvent de toute ta vie avec Terraform :

LigneCe qu'elle dit
+ createLa légende du devis : un seul symbole utilisé, +, créer.
# local_file.bonjour will be createdL'adresse de ta ressource et son sort. Ce n'est pas encore fait : will be.
+ resource "local_file" "bonjour" {Ton bloc, recopié avec un + devant : tout est nouveau.
+ content = "Bonjour Terraform"Un argument que tu as écrit. Terraform en connaît la valeur.
+ content_md5 = (known after apply)Un attribut que tu n'as pas écrit et que le provider calculera après avoir créé le fichier : son empreinte. Terraform ne peut pas la connaître avant.
+ directory_permission = "0777"Un attribut que tu n'as pas écrit et dont le provider a une valeur par défaut.
+ filename = "./bonjour.txt"Ton ${path.module}/bonjour.txt, résolu : . est le dossier courant.
+ id = (known after apply)L'identifiant de la ressource, connu seulement après création.
Plan: 1 to add, 0 to change, 0 to destroy.La ligne à lire en premier. Une chose à ajouter, rien à changer, rien à détruire.

Rien n'a été créé. Vérifie : bonjour.txt n'existe toujours pas dans le dossier.

Pour bien comprendre
  • Deux lignes que tu as écrites, neuf que tu n'as pas écrites. Le plan montre tout ce que le provider sait d'un local_file, pas seulement ce que tu lui as donné. Apprends à repérer tes arguments et à survoler les attributs, sauf la ligne Plan:.
  • (known after apply) est la phrase la plus importante à comprendre pour la suite : quand une ressource B utilise l'id d'une ressource A pas encore créée, B affiche (known after apply) pour cette valeur, et Terraform sait qu'il doit créer A d'abord. C'est le graphe de dépendances de la leçon 02, vu depuis le plan.
  • Note: You didn't use the -out option : ce devis n'est pas enregistré dans un fichier ; apply en recalculera un identique. -out sert quand un pipeline doit appliquer exactement le plan qui a été relu, plus loin dans le cours.

Étape 6 — Faire exécuter le devis

text
terraform apply
text
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"

      + filename             = "./bonjour.txt"
      + id                   = (known after apply)
    }

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, montre-le-moi, attends mon accord, puis fais-le. »

À regarder : le même plan qu'à l'étape 5, puis la question. Tape yes, en entier, puis Entrée. Ensuite Creating..., Creation complete after 0s [id=fd9aee55…] : l'id qui était (known after apply) est maintenant connu. Et la phrase à attendre : Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Les trois nombres sont ceux du plan.

Si tu tapes y au lieu de yes, Terraform répond Apply cancelled. et ne fait rien. Essaie une fois, pour l'avoir vu ; puis relance avec yes.

Étape 7 — Lire le fichier, lister le dossier

Windows (PowerShell) :

powershell
Get-Content .\bonjour.txt
Get-ChildItem -Force

Linux, macOS, WSL 2, Git Bash :

bash
cat bonjour.txt
ls -la
text
Bonjour Terraform
text
total 14
drwxr-xr-x 1 rehou 197609    0 sept. 15 14:55 .
drwxr-xr-x 1 rehou 197609    0 sept. 15 14:55 ..
drwxr-xr-x 1 rehou 197609    0 sept. 15 14:55 .terraform
-rw-r--r-- 1 rehou 197609 1228 sept. 15 14:55 .terraform.lock.hcl
-rw-r--r-- 1 rehou 197609   17 sept. 15 14:55 bonjour.txt
-rw-r--r-- 1 rehou 197609  111 sept. 15 14:55 main.tf
-rw-r--r-- 1 rehou 197609 1634 sept. 15 14:55 terraform.tfstate

(Listing ls -la de la machine du cours ; Get-ChildItem -Force affiche les mêmes cinq noms, avec d'autres colonnes.)

Ce que les commandes demandent : « Montre-moi le contenu de bonjour.txt, puis tout ce qu'il y a dans le dossier, fichiers cachés compris. »

À regarder : Bonjour Terraform, 17 octets, exactement ton content, sans retour à la ligne final (sous bash, ton invite peut apparaître collée au texte : c'est normal). Et deux nouveautés dans le dossier depuis l'étape 4 : bonjour.txt, la ressource, et terraform.tfstate, le state. C'est apply qui a créé les deux, en même temps : le fichier sur le disque, et la note « je l'ai créé » dans le relevé.

Étape 8 — Lire le relevé : la liste

text
terraform state list
text
local_file.bonjour

Ce que la commande demande : « Liste tout ce que tu gères dans ce dossier. »

À regarder : une ligne, une ressource, désignée par son adresse local_file.bonjour, la même que dans le plan. C'est la preuve que Terraform a relié ton bloc au fichier réel. Avant l'étape 6, la même commande aurait répondu No state file was found! : il n'y avait pas encore de relevé.

Étape 9 — Lire le relevé : le détail

text
terraform state show local_file.bonjour
text
# local_file.bonjour:
resource "local_file" "bonjour" {
    content              = "Bonjour Terraform"
    content_base64sha256 = "SvXkP5Iqo1rJKHgTVJnjanxfsNVGhb+r52mzUqZJYyg="
    content_base64sha512 = "r3+PevYNJRFsHMz4H4U1DK+lg89nhO9SLJZMAqqtzPKiIv3ilrmbx5sYh2JCW9o5EF0NXanYmuxMvI2QN4Z1Ag=="
    content_md5          = "ff3a967c227a58691a3d34a931d3eeb5"
    content_sha1         = "fd9aee5556589b4d797e6d49f8ec894e29e57713"
    content_sha256       = "4af5e43f922aa35ac92878135499e36a7c5fb0d54685bfabe769b352a6496328"
    content_sha512       = "af7f8f7af60d25116c1cccf81f85350cafa583cf6784ef522c964c02aaadccf2a222fde296b99bc79b188762425bda39105d0d5da9d89aec4cbc8d9037867502"
    directory_permission = "0777"
    file_permission      = "0777"
    filename             = "./bonjour.txt"
    id                   = "fd9aee5556589b4d797e6d49f8ec894e29e57713"
}

Ce que la commande demande : « Montre-moi tout ce que tu sais de local_file.bonjour. »

À regarder : la même forme que le plan de l'étape 5, mais sans aucun + et sans aucun (known after apply) : tout est connu, tout est noté. L'id est l'empreinte SHA-1 du contenu (fd9aee55…), identique à content_sha1 et à l'[id=…] affiché par apply. Tes deux arguments (filename, content) sont là, avec les neuf attributs calculés par le provider. C'est ta réponse-livrable : garde-la.

Pour bien comprendre
  • Le state est un fichier JSON, terraform.tfstate, dans ton dossier. Ouvre-le dans VS Code pour le lire : une liste "resources" avec un élément, et dedans exactement les valeurs que state show affiche. Ne le modifie jamais à la main : une virgule déplacée et Terraform ne reconnaît plus ce qu'il a construit.
  • Pourquoi Terraform a besoin de ce relevé. Si tu supprimes terraform.tfstate, bonjour.txt reste sur le disque, mais Terraform ne sait plus qu'il est à lui : au prochain plan, il proposera de le créer à nouveau. Le state est la seule mémoire de Terraform.
  • Le state peut contenir des secrets. Ici, le texte d'un fichier. Ailleurs, un mot de passe de base de données passé en argument. Il ne va pas dans Git, il ne se partage pas par message.

Étape 10 — Redemander un plan : rien à faire

text
terraform plan
text
local_file.bonjour: Refreshing state... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration
and found no differences, so no changes are needed.

Ce que la commande demande : la même qu'aux étapes 3 et 5.

À regarder : deux nouveautés par rapport à l'étape 5. Refreshing state... [id=…] : avant de comparer, Terraform est allé relire le fichier réel pour vérifier qu'il correspond encore au relevé. Puis No changes. Your infrastructure matches the configuration. : le code, le relevé et le réel disent la même chose, le devis est vide. C'est l'idempotence de la leçon 01, dans le terminal : relancer ne fait rien de plus.

Ne détruis rien. L'atelier fondamental 2 repart exactement de ce dossier, avec ce fichier et ce state. Si tu ne l'enchaînes pas tout de suite, laisse le dossier tel quel : rien n'est facturé, rien ne bouge. (Si tu veux vraiment tout effacer maintenant : terraform destroy, yes, et tu referas l'atelier 1 avant le 2.)

Vérification finale

Remonte à la racine du kit et lance le compteur :

Windows (PowerShell) :

powershell
cd ..\..
.\labo.ps1 etat

Linux, macOS, WSL 2, Git Bash :

bash
cd ../..
./labo.sh etat
text
travail atelier-1 : 1 ressource dans le state
Ressources encore gérées : 1 (0 attendu à la fin d'une séance).

Réponse attendue : 1 ressource dans le state. Le (0 attendu à la fin d'une séance) est le rappel du kit ; ici il est normal d'être à 1, puisque l'atelier 2 va détruire cette ressource.

  • Tu as écrit un main.tf d'un seul bloc, sans terraform {} ni provider {}, et il a suffi.
  • Tu as vu Error: Inconsistent dependency lock file et tu sais que la solution est dans la dernière ligne.
  • init a créé .terraform/ et .terraform.lock.hcl, et rien d'autre.
  • Tu sais lire un plan : + create, will be created, (known after apply), Plan: 1 to add, 0 to change, 0 to destroy.
  • apply a demandé yes, créé bonjour.txt et terraform.tfstate.
  • state list rend local_file.bonjour ; state show rend le bloc complet avec un id de quarante caractères.
  • Un deuxième plan rend No changes.
  • Tu as gardé la sortie de terraform state show local_file.bonjour (étape 9) comme livrable.

Si ça coince

Afficher les cas fréquents
  • terraform : Le terme «terraform» n'est pas reconnu… ou bash: terraform: command not found → Terraform n'est pas installé ou pas dans le PATH. Projet 01, annexe A.1 ou B.1.
  • .\labo.ps1 ou ./labo.sh : fichier introuvable → Tu n'es pas à la racine du kit. cd vers le dossier lab-terraform (celui qui contient labo.ps1, labo.sh, projets).
  • Le dossier travail\atelier-1 existe déjà. Tapez : → Tu as déjà lancé nouveau atelier-1. Fais le cd indiqué ; si le dossier contient un ancien essai, supprime-le et relance nouveau.
  • Error: Invalid resource typedoes not support resource type "local_fichier" → Le type est local_file, en anglais, avec un tiret bas.
  • Error: Unsupported argumentDid you mean "content"? → Faute de frappe dans un nom d'argument ; Terraform propose le bon.
  • Error: Unclosed configuration block → L'accolade fermante } de la dernière ligne manque.
  • Error: Unterminated template string → Un guillemet " manque en fin de valeur.
  • terraform plan répond Error: No configuration files → Le fichier ne s'appelle pas main.tf (souvent main.tf.txt), ou tu n'es pas dans travail/atelier-1. Vérifie avec Get-ChildItem ou ls -la.
  • Apply cancelled. → Tu as tapé autre chose que yes. Relance, tape yes en entier.
  • terraform state list répond No state file was found! → Aucun apply n'a réussi dans ce dossier. Reprends à l'étape 6.
  • terraform state show répond No instance found for the given address! → L'adresse est mal tapée ; terraform state list te donne la bonne (local_file.bonjour).