Projet 01 — Terraform local

Pratique guidée46 min
Durée
60 à 90 min (dont 30 min de préparation du poste la première fois)
Module
1/7
Tu vas construire
un premier projet Terraform complet sur ta machine, un fichier message.txt créé, modifié puis détruit par Terraform, et un poste prêt pour les projets cloud (Terraform, AWS CLI, compte AWS de laboratoire)
Livrable
le dossier projet-01-local avec son main.tf, la sortie de terraform state list avant et après destroy, et trois lignes à toi sur le rôle de terraform.tfstate
Coût
aucun, le provider local n'appelle aucune API cloud

Comment lire cette page. Chaque section est repliée sous son titre : clique sur « Afficher … » pour l'ouvrir, referme-la quand tu as fini. Ordre de lecture : Objectif, puis En bref (toutes les commandes, à copier dans l'ordre), puis Le code (le fichier main.tf expliqué ligne par ligne), puis Terraform, dans le terminal (T1 à T13, une commande à la fois avec sa vraie sortie). Le pas à pas détaillé, avec l'installation des outils, la préparation du compte AWS et la sortie attendue de chaque geste, est en annexe : annexe A pour Windows (PowerShell), annexe B pour Linux, macOS, WSL 2 et Git Bash. Ouvre une seule annexe, celle de ton système. L'annexe C, commune, regroupe les cas où ça coince.

Objectif

Tu arrives dans une équipe qui gère toute son infrastructure avec Terraform. Avant de te laisser toucher au compte AWS, ta cheffe te demande une chose : « Montre-moi que tu maîtrises le cycle complet sur ta machine. Un fichier, c'est suffisant. Je veux voir le plan avant l'apply, le state après, la modification détectée, et un dossier propre à la fin. » C'est exactement ce projet. Le provider local écrit un fichier texte sur ton disque au lieu de créer un serveur, mais les commandes, les phrases affichées et les fichiers produits sont les mêmes que pour un bucket S3 ou un dépôt GitHub. Tout ce que tu lis ici, tu le reliras dans les huit projets suivants.

Les dix étapes de ce schéma sont détaillées, avec la sortie attendue de chaque commande, dans l'annexe A (Windows) ou l'annexe B (Linux, macOS, WSL 2, Git Bash) en bas de page. Les commandes Terraform elles-mêmes, identiques sur tous les systèmes, sont expliquées une par une dans Terraform, dans le terminal.

En bref : les commandes du projet

Afficher les commandes

Kit du cours : https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr

Tu clones le kit dans un dossier lab-terraform, tu vérifies tes outils, tu crées un dossier de travail vide, tu y écris main.tf toi-même, puis tu déroules le cycle : init, fmt, validate, plan, apply, vérification, state, modification, destroy. À la fin, terraform state list n'affiche plus rien et etat dit Ressources encore gérées : 0 (0 attendu à la fin d'une séance). Le kit contient le corrigé dans projets/01-terraform-local/, à ouvrir seulement si tu bloques : le but est d'écrire le fichier à la main.

Windows (PowerShell)

powershell
git clone https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr.git lab-terraform
cd lab-terraform
dir                                  # explorer : labo.ps1, labo.sh, README.md, projets\
.\labo.ps1 prerequis                 # attendu : Prérequis : 4 outils requis présents sur 4.
.\labo.ps1 nouveau projet-01-local   # crée travail\projet-01-local
cd travail\projet-01-local
code .                               # VS Code : créer main.tf, coller le code de la section « Le code »
terraform init                       # attendu : Terraform has been successfully initialized!
terraform fmt
terraform validate                   # attendu : Success! The configuration is valid.
terraform plan                       # attendu : Plan: 1 to add, 0 to change, 0 to destroy.
terraform apply                      # répondre yes ; attendu : Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Get-Content .\message.txt            # attendu : Bonjour, ce fichier a été créé avec Terraform.
Get-ChildItem -Force                 # .terraform, .terraform.lock.hcl, main.tf, message.txt, terraform.tfstate
terraform state list                 # attendu : local_file.message
terraform state show local_file.message
# modifier la ligne content dans main.tf (voir T10), puis :
terraform plan                       # attendu : Plan: 1 to add, 0 to change, 1 to destroy.
terraform apply                      # répondre yes ; attendu : Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
Get-Content .\message.txt            # attendu : Deuxième version du fichier créée avec Terraform.
terraform destroy                    # répondre yes ; attendu : Destroy complete! Resources: 1 destroyed.
terraform state list                 # attendu : rien
cd ..\..
.\labo.ps1 etat                      # attendu : Ressources encore gérées : 0 (0 attendu à la fin d'une séance).

Si PowerShell refuse .\labo.ps1 (« l'exécution de scripts est désactivée sur ce système ») : Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, réponds O, relance.

Linux, macOS, WSL 2, Git Bash

bash
git clone https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr.git lab-terraform
cd lab-terraform
ls                                   # explorer : labo.ps1, labo.sh, README.md, projets/
./labo.sh prerequis                  # attendu : Prérequis : 4 outils requis présents sur 4.
./labo.sh nouveau projet-01-local    # crée travail/projet-01-local
cd travail/projet-01-local
code .                               # VS Code : créer main.tf, coller le code de la section « Le code »
terraform init                       # attendu : Terraform has been successfully initialized!
terraform fmt
terraform validate                   # attendu : Success! The configuration is valid.
terraform plan                       # attendu : Plan: 1 to add, 0 to change, 0 to destroy.
terraform apply                      # répondre yes ; attendu : Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
cat message.txt                      # attendu : Bonjour, ce fichier a été créé avec Terraform.
ls -la                               # .terraform, .terraform.lock.hcl, main.tf, message.txt, terraform.tfstate
terraform state list                 # attendu : local_file.message
terraform state show local_file.message
# modifier la ligne content dans main.tf (voir T10), puis :
terraform plan                       # attendu : Plan: 1 to add, 0 to change, 1 to destroy.
terraform apply                      # répondre yes ; attendu : Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
cat message.txt                      # attendu : Deuxième version du fichier créée avec Terraform.
terraform destroy                    # répondre yes ; attendu : Destroy complete! Resources: 1 destroyed.
terraform state list                 # attendu : rien
cd ../..
./labo.sh etat                       # attendu : Ressources encore gérées : 0 (0 attendu à la fin d'une séance).

Si ./labo.sh répond Permission denied : chmod +x labo.sh, une seule fois.

Le code : fichier par fichier

Afficher le fichier main.tf et son explication ligne par ligne

Le projet tient dans un seul fichier, main.tf, à créer dans travail/projet-01-local/. Tape-le ou colle-le exactement ainsi (c'est mot pour mot le corrigé du kit, projets/01-terraform-local/main.tf) :

hcl
terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}

provider "local" {}

resource "local_file" "message" {
  filename = "${path.module}/message.txt"
  content  = "Bonjour, ce fichier a été créé avec Terraform."
}

Trois blocs, du plus général au plus concret.

LigneCe qu'elle dit à Terraform
terraform { … }Les réglages de Terraform lui-même pour ce dossier.
required_providers { local = { … } }« Ce projet a besoin d'un plugin appelé local. »
source = "hashicorp/local"Où le télécharger : le provider local publié par HashiCorp sur le Terraform Registry.
version = "~> 2.5"Quelle version accepter : 2.5 ou plus récente, mais pas 3.0. Sur la machine du cours, init a choisi v2.9.1.
provider "local" {}« Active ce provider. » Les accolades sont vides parce que le provider local n'a rien à configurer (pas de région, pas de compte).
resource "local_file" "message" { … }« Gère un objet de type local_file, que j'appelle message dans mon code. » Le type vient du provider (local_ + file) ; le nom est à toi. Ensemble ils forment l'adresse local_file.message.
filename = "${path.module}/message.txt"Où écrire le fichier. path.module est le dossier qui contient ce main.tf ; Terraform l'affichera ./message.txt.
content = "Bonjour, ce fichier a été créé avec Terraform."Le texte à écrire dedans. C'est cette ligne que tu changeras à l'étape T10.
Pour bien comprendre : pourquoi `${path.module}` plutôt que `message.txt` tout court ?

Terraform lance les commandes depuis le dossier courant. Si tu écris filename = "message.txt", le fichier est créé là où tu tapes terraform apply, ce qui est en général le bon dossier, mais pas toujours (quand le code est appelé comme module depuis un autre dossier, au module 5). path.module désigne toujours le dossier du fichier .tf, quel que soit l'endroit d'où Terraform est lancé. La syntaxe ${…} insère la valeur d'une expression dans une chaîne de caractères : "${path.module}/message.txt" devient "./message.txt".

Pour bien comprendre : et si je retire le bloc `terraform { required_providers … }` ?

Ça marche quand même : Terraform déduit du type local_file qu'il lui faut le provider hashicorp/local, et init affiche Finding latest version of hashicorp/local... au lieu de Finding hashicorp/local versions matching "~> 2.5".... C'est ce que font les deux ateliers fondamentaux de ce module, pour aller au plus court. Mais sans contrainte de version, deux collègues qui lancent init à six mois d'écart peuvent obtenir deux versions différentes du provider. Dans un vrai projet, on écrit toujours le bloc.

Terraform, dans le terminal

Afficher les 13 commandes Terraform (T1 à T13)

Les commandes de cette section se tapent dans le dossier travail/projet-01-local, une fois main.tf écrit. Elles sont identiques sous Windows, Linux et macOS : Terraform est le même programme partout. Les sorties sont celles de la machine du cours (Terraform 1.12.2, Windows 11), capturées telles quelles ; seuls les identifiants (id=ff6b93dc…) et les empreintes changent d'une machine à l'autre. Les commandes qui, elles, dépendent du système (lire le fichier, lister le dossier) sont dans ton annexe.

Une image à garder pour toute la section : la leçon 01 t'a présenté l'architecte. Chaque commande ci-dessous est un de ses gestes. init ouvre le chantier, plan écrit le devis, apply le fait exécuter, state relit le relevé, destroy démonte tout.

La règle : une seule nouveauté par commande. On commence par la commande qui ne prend aucun paramètre et ne touche à rien.

T1. Quelle version de Terraform ?

text
terraform version
text
Terraform v1.12.2
on windows_amd64

Ce que la commande demande : « Terraform, quelle version es-tu, et sur quel système ? » Aucun paramètre, aucun fichier lu, rien de modifié. Si cette commande échoue (terraform : Le terme «terraform» n'est pas reconnu… ou command not found), rien d'autre ne marchera : c'est l'installation ou le PATH, voir l'annexe A.1 ou B.1. Sur la machine du cours, Terraform ajoute parfois deux lignes Your version of Terraform is out of date! : c'est une information, pas une erreur.

L'équivalent connu : aws --version, git --version, python --version. Même geste, même but.

T2. Ouvrir le chantier

text
terraform init
text
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/local versions matching "~> 2.5"...
- 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 : « Lis mon main.tf, télécharge les providers qu'il réclame, et prépare ce dossier pour travailler. »

Une seule nouveauté : le dossier devient un projet Terraform. Lis la sortie ligne par ligne :

LigneCe qui s'est passé
Initializing the backend...Le backend est l'endroit où le state sera rangé. Rien n'est précisé dans main.tf, donc ce sera un fichier local, terraform.tfstate.
Finding hashicorp/local versions matching "~> 2.5"...Terraform a lu ta contrainte de version et cherche sur le Registry.
Installing hashicorp/local v2.9.1...Il a choisi la plus récente qui respecte ~> 2.5, et la télécharge dans .terraform/.
(signed by HashiCorp)Le plugin est signé : tu as bien le vrai provider.
Terraform has created a lock file .terraform.lock.hclLa version choisie est notée dans un fichier à garder dans Git : le prochain init, chez un collègue, prendra la même.
Terraform has been successfully initialized!La phrase à attendre.

Deux choses sont apparues dans ton dossier : le répertoire caché .terraform/ (le provider téléchargé, environ 18 Mo sur la machine du cours, à ne jamais mettre dans Git) et le fichier .terraform.lock.hcl (22 lignes, à mettre dans Git). Tu les verras dans l'annexe, étape A.6 ou B.6.

L'équivalent connu : npm install ou pip install -r requirements.txt. On lit une liste de dépendances, on télécharge, on verrouille les versions (package-lock.json).

Pour bien comprendre : que se passe-t-il si j'oublie `init` ?

Terraform refuse de continuer et te dit quoi faire. terraform plan sans init répond :

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

et terraform validate sans init :

text
Error: Missing required provider

This configuration requires provider registry.terraform.io/hashicorp/local,
but that provider isn't available. You may be able to install it
automatically by running:
  terraform init

Dans les deux cas, la dernière ligne est la solution. init se relance sans risque autant de fois que tu veux ; il ne touche jamais à tes ressources.

T3. Mettre le code au propre

text
terraform fmt
text

Ce que la commande demande : « Réaligne les espaces et l'indentation de mes fichiers .tf selon le style officiel. »

Une seule nouveauté : la sortie vide. fmt affiche le nom de chaque fichier qu'il a modifié ; si tu as collé le code tel quel, il n'a rien à corriger et n'affiche rien. Ce n'est pas une erreur, c'est la meilleure réponse possible. Si tu avais mal indenté une ligne, il aurait affiché main.tf et réécrit le fichier.

L'équivalent connu : prettier --write, black, gofmt. Le kit vérifie ce point avec terraform fmt -check -recursive, qui ne modifie rien et sort en erreur (code 3) si un fichier n'est pas au format.

T4. Vérifier la syntaxe

text
terraform validate
text
Success! The configuration is valid.

Ce que la commande demande : « Relis mes blocs. Les noms d'arguments existent-ils, les accolades sont-elles fermées, les références pointent-elles vers quelque chose ? »

Une seule nouveauté : Terraform consulte le provider pour connaître les arguments valides d'un local_file. C'est pourquoi validate exige init avant. Il ne lit pas le state, ne contacte aucune API, ne crée rien. La phrase à attendre : Success! The configuration is valid.

Ce qu'il attrape : contenu au lieu de content (An argument named "contenu" is not expected here. Did you mean "content"?), une accolade oubliée (Error: Unclosed configuration block), un guillemet non fermé (Error: Unterminated template string), un type inexistant (The provider hashicorp/local does not support resource type "local_fichier".). L'annexe C les reprend un par un.

Ce qu'il n'attrape pas : un chemin qui n'existe pas, un nom de bucket déjà pris, une mauvaise architecture. validate dit « c'est du HCL correct », pas « c'est une bonne idée ».

T5. Écrire 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.message will be created
  + resource "local_file" "message" {
      + content              = "Bonjour, ce fichier a été créé avec 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             = "./message.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 : « Compare ce que mon code décrit avec ce que tu connais déjà, et dis-moi ce que tu ferais, sans le faire. »

Une seule nouveauté : le devis. Rien n'a été créé ; message.txt n'existe toujours pas. Lis-le de haut en bas :

LigneComment la lire
+ createLa légende : dans ce plan, il n'y a que des créations.
# local_file.message will be createdL'adresse de la ressource (type . nom) et son sort.
+ resource "local_file" "message" {Le bloc entier est précédé d'un + : tout est nouveau.
+ content = "Bonjour, …"Un attribut que tu as écrit : Terraform connaît sa valeur.
+ content_md5 = (known after apply)Un attribut que le provider calculera après la création : l'empreinte du contenu. Terraform ne peut pas la deviner avant.
+ directory_permission = "0777"Un attribut que tu n'as pas écrit : le provider a une valeur par défaut.
+ filename = "./message.txt"Ton ${path.module}/message.txt, résolu.
Plan: 1 to add, 0 to change, 0 to destroy.La ligne à lire en premier, toujours. Une création, aucune modification, aucune destruction.
Note: You didn't use the -out option…Information : ce devis n'est pas enregistré ; apply en recalculera un. Sans importance ici.

L'équivalent connu : git diff avant git commit, ou le mode « simulation » (--dry-run, -WhatIf) d'un script. La différence : un script --dry-run te montre ce qu'il va faire ; terraform plan te montre ce qui manque entre le code et le réel, après avoir relu le réel.

Pour bien comprendre : pourquoi tant de lignes pour un fichier texte ?

Le provider local expose, pour un local_file, tout ce qu'il sait calculer : six empreintes du contenu (md5, sha1, sha256, sha512, et deux versions encodées en base64), les permissions, l'identifiant. Tu n'en as écrit que deux (filename, content) ; les autres sont des attributs que le provider remplit. C'est la même chose pour un bucket S3 : tu écris trois lignes, le plan en affiche trente (ARN, région, identifiant du propriétaire…). Apprends à repérer les tiennes et à survoler les autres, sauf la dernière ligne Plan: qui ne se survole jamais.

T6. 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.message will be created
  + resource "local_file" "message" {
      + content              = "Bonjour, ce fichier a été créé avec Terraform."

      + filename             = "./message.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.message: Creating...
local_file.message: Creation complete after 0s [id=ff6b93dc92e7e1d2ba4f9dad3cc16e03ac649b45]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Ce que la commande demande : « Recalcule le devis, montre-le-moi, et si je réponds yes, fais-le. »

Une seule nouveauté : la question. Terraform s'arrête sur Enter a value: et attend. Tape yes en entier, puis Entrée. y, Y, oui ou une ligne vide donnent Apply cancelled. et rien n'est touché : c'est une protection, pas un bug. Ensuite trois lignes : Creating..., Creation complete after 0s [id=…] (l'identifiant que le plan annonçait (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.

Maintenant, message.txt existe dans ton dossier, et un nouveau fichier est apparu à côté : terraform.tfstate. Va le vérifier avec ton annexe (A.6 ou B.6) avant de continuer : le fichier contient bien Bonjour, ce fichier a été créé avec Terraform.

L'équivalent connu : un script qui écrit le fichier, Set-Content ou echo > message.txt. La différence : le script écrit et oublie ; Terraform écrit et note dans son relevé qu'il a écrit, sous quel identifiant, avec quel contenu.

T7. Relire le relevé : la liste

text
terraform state list
text
local_file.message

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

Une seule nouveauté : le state se lit. Une ligne, une ressource : l'adresse local_file.message, la même que dans le plan. Cette ligne prouve que Terraform a relié le bloc de ton code au fichier réel : c'est ce qui lui permettra de le modifier ou de le détruire plus tard sans que tu aies à lui redire lequel.

Avant apply, la même commande répondait No state file was found! : le relevé n'existait pas encore. Après destroy (T12), elle ne répondra plus rien du tout.

L'équivalent connu : docker ps, kubectl get pods, aws s3 ls. « Qu'est-ce qui tourne, qu'est-ce qui existe ? » Sauf que state list ne va pas voir le réel : il lit son carnet.

T8. Relire le relevé : le détail

text
terraform state show local_file.message
text
# local_file.message:
resource "local_file" "message" {
    content              = "Bonjour, ce fichier a été créé avec Terraform."
    content_base64sha256 = "G+tDdGue1Lx5hU3LpqBWf3y3haa0ggckj6t/9bj5ocs="
    content_base64sha512 = "oB7gPsGHBDNnXe9Hzx4zQ3/nAF1kM9wlfDV/OdMjPybCPurPeN4P8myOoZxFg3dwvs+xLBRJxo4Q3H0qGS8l5g=="
    content_md5          = "6daf774f0eb6d3da439c871afec7cf90"
    content_sha1         = "ff6b93dc92e7e1d2ba4f9dad3cc16e03ac649b45"
    content_sha256       = "1beb43746b9ed4bc79854dcba6a0567f7cb785a6b48207248fab7ff5b8f9a1cb"
    content_sha512       = "a01ee03ec1870433675def47cf1e33437fe7005d6433dc257c357f39d3233f26c23eeacf78de0ff26c8ea19c45837770becfb12c1449c68e10dc7d2a192f25e6"
    directory_permission = "0777"
    file_permission      = "0777"
    filename             = "./message.txt"
    id                   = "ff6b93dc92e7e1d2ba4f9dad3cc16e03ac649b45"
}

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

Une seule nouveauté : la commande prend un paramètre, l'adresse lue en T7. Compare avec le plan de T5 : toutes les valeurs (known after apply) sont maintenant remplies. L'id est l'empreinte SHA-1 du contenu, la même valeur que content_sha1 et que l'[id=…] affiché par apply. C'est le relevé de l'architecte, pièce par pièce.

Si tu te trompes d'adresse (local_file.mesage), Terraform répond No instance found for the given address! et te renvoie vers terraform state list.

Pour bien comprendre : ce que `terraform.tfstate` contient, et pourquoi on ne l'édite jamais

Le fichier terraform.tfstate est du JSON : un numéro de série ("serial"), la version de Terraform, et une liste "resources" où chaque ressource a exactement les attributs que state show t'affiche. Tu peux l'ouvrir pour le lire. Tu ne le modifies jamais à la main : une virgule déplacée, et Terraform ne reconnaît plus ce qu'il a construit, ou croit gérer quelque chose qui n'existe plus. Pour agir sur le state, il y a des commandes (terraform state rm, terraform state mv, terraform import) que tu verras au module 4.

Deux autres règles, dès aujourd'hui. Le state peut contenir des secrets (ici, le texte du fichier ; ailleurs, un mot de passe de base de données) : il ne se partage pas par message ni ne se commite dans Git. Et il est la mémoire de Terraform : si tu le supprimes, Terraform croit que rien n'existe et propose de tout recréer, alors que les ressources sont toujours là et, dans le cloud, toujours facturées.

T9. Demander les sorties d'un projet qui n'en a pas

text
terraform output
text
Warning: No outputs found

The state file either has no outputs defined, or all the defined outputs are
empty. Please define an output in your configuration with the `output`
keyword and run `terraform refresh` for it to become available. If you are
using interpolation, please verify the interpolated value is not empty. You
can use the `terraform console` command to assist.

Ce que la commande demande : « Affiche les valeurs que le code a choisi d'exposer avec des blocs output. »

Une seule nouveauté : un avertissement (Warning), pas une erreur. Ton main.tf n'a aucun bloc output, donc Terraform n'a rien à afficher et te le dit. Tu écriras tes premiers outputs au projet 02 ; ici, la commande sert à voir la différence entre « ça a échoué » (Error:) et « il n'y a rien à montrer » (Warning:).

T10. Modifier le code et relire le devis

Ouvre main.tf, remplace la ligne content par celle-ci, enregistre :

hcl
  content  = "Deuxième version du fichier créée avec Terraform."

Puis :

text
terraform plan
text
local_file.message: Refreshing state... [id=ff6b93dc92e7e1d2ba4f9dad3cc16e03ac649b45]

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.message must be replaced
-/+ resource "local_file" "message" {
      ~ content              = "Bonjour, ce fichier a été créé avec Terraform." -> "Deuxième version du fichier créée avec Terraform." # forces replacement
      ~ content_base64sha256 = "G+tDdGue1Lx5hU3LpqBWf3y3haa0ggckj6t/9bj5ocs=" -> (known after apply)
      ~ content_base64sha512 = "oB7gPsGHBDNnXe9Hzx4zQ3/nAF1kM9wlfDV/OdMjPybCPurPeN4P8myOoZxFg3dwvs+xLBRJxo4Q3H0qGS8l5g==" -> (known after apply)
      ~ content_md5          = "6daf774f0eb6d3da439c871afec7cf90" -> (known after apply)
      ~ content_sha1         = "ff6b93dc92e7e1d2ba4f9dad3cc16e03ac649b45" -> (known after apply)
      ~ content_sha256       = "1beb43746b9ed4bc79854dcba6a0567f7cb785a6b48207248fab7ff5b8f9a1cb" -> (known after apply)
      ~ content_sha512       = "a01ee03ec1870433675def47cf1e33437fe7005d6433dc257c357f39d3233f26c23eeacf78de0ff26c8ea19c45837770becfb12c1449c68e10dc7d2a192f25e6" -> (known after apply)
      ~ id                   = "ff6b93dc92e7e1d2ba4f9dad3cc16e03ac649b45" -> (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 chose qu'en T5. Mais cette fois Terraform a un relevé à comparer.

Une seule nouveauté : le symbole -/+, « détruire puis recréer ». Trois indices te le disent, et il faut savoir les repérer :

IndiceCe qu'il dit
must be replacedle titre # local_file.message must be replacedCe n'est pas will be updated in-place.
# forces replacementau bout de la ligne ~ content = "…" -> "…"C'est cet attribut qui oblige à remplacer. Le ~ devant dit que la valeur change ; le commentaire dit que ce changement ne peut pas se faire sur place.
Plan: 1 to add, 0 to change, 1 to destroy.la dernière ligneUne destruction et une création, zéro modification.

Regarde aussi la première ligne, nouvelle : Refreshing state... [id=…]. Avant de comparer, Terraform est allé relire le fichier réel pour vérifier qu'il correspond encore à son relevé. C'est là qu'il détecterait une dérive (leçon 01), si tu avais modifié message.txt à la main.

La différence essentielle entre ~ et -/+. Pour un fichier texte, être remplacé ou modifié sur place revient au même : à la fin, le contenu est le nouveau. Pour une base de données, un disque ou un utilisateur, -/+ veut dire perte de l'objet et de son identifiant : les données du disque, le mot de passe de l'utilisateur, l'adresse IP du serveur. C'est le provider qui décide, attribut par attribut, ce qui force un remplacement ; toi, tu le lis dans le plan avant de dire yes. Le provider local remplace le fichier dès que content change : jamais de ~ seul pour cet attribut.

T11. Appliquer le remplacement

text
terraform apply
text

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.message: Destroying... [id=ff6b93dc92e7e1d2ba4f9dad3cc16e03ac649b45]
local_file.message: Destruction complete after 0s
local_file.message: Creating...
local_file.message: Creation complete after 0s [id=1df9196ac94445f0943c35b75c8c2e1bdf3e0cd6]

Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

Ce que la commande demande : la même chose qu'en T6.

Rien de neuf dans la commande ; tout est dans la sortie. Quatre lignes d'action au lieu de deux : Destroying..., Destruction complete, puis Creating..., Creation complete. L'ordre est celui du symbole -/+ : le moins d'abord, le plus ensuite. Et l'identifiant a changé (ff6b93dc… avant, 1df9196a… après) : c'est un autre objet, pas le même modifié. Vérifie le nouveau contenu avec ton annexe (A.8 ou B.8) : Deuxième version du fichier créée avec Terraform.

Bilan de T5 à T11 : tu as vu les deux devis que tu reliras toute ta carrière : + (créer) et -/+ (remplacer). Le troisième, ~ (modifier sur place), n'existe pas pour le content d'un local_file ; tu le rencontreras dans les projets cloud, par exemple quand une étiquette (tag) change sur une ressource AWS.

T12. Tout démonter

text
terraform destroy
text
local_file.message: Refreshing state... [id=1df9196ac94445f0943c35b75c8c2e1bdf3e0cd6]

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.message will be destroyed
  - resource "local_file" "message" {
      - content              = "Deuxième version du fichier créée avec Terraform." -> null
      - content_base64sha256 = "36Hz+L76pjLbvY4LIzPfw+UsqNneFneSfFGjtfRLxkI=" -> null

      - filename             = "./message.txt" -> null
      - id                   = "1df9196ac94445f0943c35b75c8c2e1bdf3e0cd6" -> 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.message: Destroying... [id=1df9196ac94445f0943c35b75c8c2e1bdf3e0cd6]
local_file.message: Destruction complete after 0s

Destroy complete! Resources: 1 destroyed.

Ce que la commande demande : « Détruis tout ce que tu gères dans ce dossier, et montre-moi le devis d'abord. »

Une seule nouveauté : le symbole - seul, et une question plus grave : Do you really want to destroy all resources? … There is no undo. Chaque attribut passe à -> null : il n'y aura plus rien. La phrase à attendre : Destroy complete! Resources: 1 destroyed. Le fichier message.txt a disparu de ton dossier.

C'est la commande qui clôt chaque séance de ce cours. Ici elle efface un fichier texte ; au projet 03 elle supprimera un bucket qui, sinon, resterait facturé.

T13. Vérifier que le relevé est vide

text
terraform state list
text

Ce que la commande demande : la même qu'en T7.

Rien de neuf, et une sortie vide : Terraform ne gère plus rien dans ce dossier. Le fichier terraform.tfstate existe toujours, mais sa liste "resources" est [], et un terraform.tfstate.backup est apparu à côté (la version d'avant le destroy, que Terraform garde par prudence). Ton annexe (A.9 et A.10, ou B.9 et B.10) te fait lister le dossier et lancer etat pour la preuve finale : Ressources encore gérées : 0 (0 attendu à la fin d'une séance).

Bilan de T1 à T13 : init une fois, puis fmt, validate, plan, apply, state list, state show, un plan et un apply de modification, destroy, state list. Treize commandes, cinq phrases à reconnaître : successfully initialized!, The configuration is valid., Plan: N to add, N to change, N to destroy., Apply complete!, Destroy complete!.

Le message à faire passer. Un script PowerShell ou bash aurait écrit message.txt en une ligne, et l'aurait supprimé en une autre. Ce que le script ne fait pas et que tu as vu Terraform faire : annoncer avant d'agir (plan), demander un accord explicite (yes), noter ce qu'il a créé (state list), relire le réel avant chaque décision (Refreshing state...), distinguer modifier de remplacer (-/+), et savoir tout défaire sans qu'on lui redise quoi (destroy).

La console AWS, dans le navigateur : préparer le compte pour les projets suivants

Afficher la préparation du compte AWS (groupe, utilisateur IAM, Access Key)

Le projet 01 ne touche pas à AWS. Mais le projet 03 crée un vrai bucket S3, et tu auras besoin d'un compte prêt. Cette section se fait dans le navigateur, elle est identique sous Windows, Linux et macOS, et tu peux la faire avant ou après le cycle Terraform de ce projet. Les commandes aws configure et aws sts get-caller-identity qui l'accompagnent sont dans ton annexe (A.2 ou B.2).

Si tu travailles dans AWS Academy, AWS Educate, un sandbox fourni par l'école ou un compte temporaire de laboratoire : utilise les identifiants fournis par la plateforme et passe directement à l'étape A.2 ou B.2 (cas « Academy »). Ne crée pas d'utilisateur IAM si ton enseignant te donne déjà des accès temporaires.

Ouvrir la console AWS

  1. Ouvre le navigateur et va sur https://aws.amazon.com/.
  2. Clique sur Sign in to the Console.
  3. Connecte-toi avec le compte AWS du laboratoire.
  4. Vérifie la région en haut à droite. Pour le Canada, choisis ca-central-1 si le laboratoire n'en impose pas une autre.

Ne pas travailler avec le compte root

Le compte root sert uniquement à créer le compte, gérer la facturation et faire les opérations sensibles. Active la MFA (authentification à deux facteurs) sur le compte root, puis crée un utilisateur séparé pour le travail de laboratoire, ci-dessous.

Créer un groupe administrateur pour le laboratoire

  1. Dans la barre de recherche AWS, tape IAM et ouvre le service IAM.
  2. Menu de gauche : User groupsCreate group.
  3. Nom du groupe : terraform-admins.
  4. Dans la recherche des permissions, tape AdministratorAccess, coche la politique AdministratorAccess.
  5. Create group.

Pour un laboratoire seulement. AdministratorAccess donne un accès complet à tous les services et ressources du compte. C'est pratique pour apprendre ; ce n'est pas un modèle de sécurité pour la production, où l'on donne à Terraform les seules permissions dont il a besoin (module 4).

Créer un utilisateur IAM pour le laboratoire

  1. Dans IAM : UsersCreate user.
  2. Nom : terraform-admin.
  3. Si tu veux aussi te connecter à la console avec cet utilisateur, coche Provide user access to the AWS Management Console et choisis un mot de passe temporaire ou généré. Coche User must create a new password at next sign-in si tu donnes le compte à quelqu'un.
  4. NextAdd user to group → coche terraform-adminsNextCreate user.

Créer une Access Key pour AWS CLI

  1. Dans IAM : Users → clique sur terraform-admin.
  2. Onglet Security credentials, section Access keysCreate access key.
  3. Choisis Command Line Interface (CLI), coche la confirmation si AWS affiche une recommandation, Next.
  4. Description tag value : aws-cli-terraform-labCreate access key.
  5. Télécharge le fichier .csv ou copie Access key et Secret access key dans un gestionnaire de mots de passe. La Secret access key ne sera plus jamais affichée.

À ne jamais faire. Ne colle jamais une Access Key, une Secret Access Key, un token de session, un fichier .env ou un terraform.tfstate dans GitHub, Word, Teams, Discord, Slack, un ticket ou un courriel. Ces éléments donnent accès au compte. Si une clé fuit : IAM → l'utilisateur → Security credentialsDeactivate puis Delete, et on en crée une autre.

Défi bonus (optionnel)

Avant de relancer terraform destroy une deuxième fois sur un dossier déjà détruit, écris sur papier ce que la dernière ligne du plan va dire. Puis lance-le. Ensuite, recrée le fichier avec terraform apply, ouvre message.txt dans VS Code, ajoute un mot à la main, enregistre, et tape terraform plan : que dit la ligne Refreshing state..., quel symbole apparaît, et comment appelle-t-on cet écart dans la leçon 01 ? Termine par destroy : le dossier doit revenir à Ressources encore gérées : 0.

Annexe A — Pas à pas détaillé sous Windows (PowerShell)

Afficher le pas à pas Windows (A.0 à A.10)

Toutes les commandes de cette annexe se tapent dans PowerShell (Windows Terminal ou PowerShell 7), avec .\labo.ps1 …. Les sorties reproduites sont celles de la machine du cours, sous Windows 11 et Terraform 1.12.2.

A.0 — Avant de commencer

  • Avoir lu les leçons 01 et 02 du module.
  • Git installé (git --version répond), VS Code installé avec la commande code disponible dans PowerShell (sinon : VS Code → Ctrl+Shift+P → « Shell Command: Install 'code' command in PATH », ou réinstalle en cochant « Add to PATH »).
  • Si PowerShell refuse d'exécuter .\labo.ps1 (« l'exécution de scripts est désactivée sur ce système ») : Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, réponds O, relance. Une seule fois par machine.
  • À la fin, .\labo.ps1 etat doit afficher Ressources encore gérées : 0 (0 attendu à la fin d'une séance).

A.1 — Installer Terraform

Le plus simple, avec le gestionnaire de paquets de Windows :

powershell
winget install Hashicorp.Terraform

Ferme PowerShell, rouvre-le, puis :

powershell
terraform version

Point de contrôle :

text
Terraform v1.12.2
on windows_amd64

Ta version peut être plus récente ; tout ce qui est 1.6 ou plus convient. Si Terraform ajoute Your version of Terraform is out of date!, c'est une information, pas une erreur.

Sans winget, avec le binaire officiel : va sur https://developer.hashicorp.com/terraform/install, choisis Windows, télécharge la version AMD64, décompresse le .zip, crée le dossier C:\terraform, déplace terraform.exe dedans, puis ajoute C:\terraform au PATH : menu Démarrer → « variables d'environnement » → Modifier les variables d'environnement systèmeVariables d'environnement → dans Variables système, sélectionne PathModifierNouveauC:\terraformOK. Ferme toutes les fenêtres PowerShell, rouvre, retape terraform version.

Si tu vois autre chose : terraform : Le terme «terraform» n'est pas reconnu comme nom d'applet de commande… → Terraform n'est pas dans le PATH, ou PowerShell n'a pas été redémarré après l'installation. Annexe C.

A.2 — Installer et configurer AWS CLI version 2 (pour les projets suivants)

Le projet 01 n'utilise pas AWS ; cette étape prépare le projet 03. Tu peux la faire maintenant ou la remettre à plus tard.

  1. Va sur https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html, télécharge le MSI Windows 64-bit d'AWS CLI version 2.
  2. Lance le .msi : Next, accepte la licence, garde le dossier par défaut, Install.
  3. Ferme puis rouvre PowerShell.
powershell
aws --version

Point de contrôle : une version qui commence par aws-cli/2 (sur la machine du cours : aws-cli/2.22.18). Si tu vois aws-cli/1, c'est l'ancienne version : désinstalle-la.

Puis configure les clés créées dans la section La console AWS, dans le navigateur :

powershell
aws configure
text
AWS Access Key ID [None]: TON_ACCESS_KEY_ID
AWS Secret Access Key [None]: TA_SECRET_ACCESS_KEY
Default region name [None]: ca-central-1
Default output format [None]: json

Vérifie que la CLI parle bien à AWS :

powershell
aws sts get-caller-identity

Exemple de réponse (les valeurs sont celles de ton compte) :

json
{
    "UserId": "AIDA...",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/terraform-admin"
}

Cas AWS Academy ou sandbox temporaire : ouvre le laboratoire, clique sur AWS Details ou Credentials, copie Access Key, Secret Key et Session Token (obligatoire avec des accès temporaires), puis :

powershell
aws configure set aws_access_key_id TON_ACCESS_KEY_ID
aws configure set aws_secret_access_key TA_SECRET_ACCESS_KEY
aws configure set aws_session_token TON_SESSION_TOKEN
aws configure set region ca-central-1
aws configure set output json
aws sts get-caller-identity

A.3 — Cloner le kit et vérifier les prérequis

Place-toi dans le dossier où tu ranges tes projets (par exemple C:\Users\<toi>\Documents), puis :

powershell
git clone https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr.git lab-terraform
cd lab-terraform
dir

Point de contrôle : dir liste projets, labo.ps1, labo.sh, README.md (et .gitattributes, .gitignore avec dir -Force). Puis :

powershell
.\labo.ps1 prerequis
text
OK      terraform 1.12.2
OK      aws 2.22.18
OK      git 2.49.0.windows.1
OK      code 1.135.0
OK      gh 2.81.0 (optionnel)
ABSENT  az (optionnel, projet 08B seulement)
OK      gcloud 544.0.0 (optionnel)
Prérequis : 4 outils requis présents sur 4.

Les quatre outils requis sont terraform, aws, git et code. gh, az et gcloud sont optionnels et peuvent être ABSENT sans conséquence avant le module 3. Si aws est ABSENT parce que tu as remis A.2 à plus tard, la dernière ligne dit 3 outils requis présents sur 4 : tu peux quand même continuer ce projet.

A.4 — Créer le dossier de travail et l'ouvrir dans VS Code

powershell
.\labo.ps1 nouveau projet-01-local
text
Dossier travail\projet-01-local créé (ignoré par Git). Tapez :
  cd travail\projet-01-local

Fais ce que le script te dit, puis ouvre le dossier dans VS Code :

powershell
cd travail\projet-01-local
code .

Le dossier travail\ est ignoré par Git : tu peux y écrire librement sans salir le kit. Le corrigé reste dans projets\01-terraform-local\.

Si code . ne fait rien : ouvre VS Code à la main, FileOpen Folder, et choisis lab-terraform\travail\projet-01-local.

A.5 — Créer main.tf

Dans VS Code : icône New File dans l'explorateur de gauche, nomme le fichier main.tf (attention : pas main.tf.txt, VS Code n'ajoute pas d'extension mais le Bloc-notes Windows le ferait), colle le code de la section Le code : fichier par fichier, enregistre avec Ctrl+S.

Vérifie depuis PowerShell :

powershell
Get-Content .\main.tf

Point de contrôle : les quinze lignes du fichier, de terraform { à }. Puis :

powershell
Get-ChildItem

Une seule entrée : main.tf. Si tu vois main.tf.txt, renomme : Rename-Item main.tf.txt main.tf.

A.6 — Initialiser, formater, valider, planifier, appliquer

Déroule T2 à T6 de la section Terraform, dans le terminal :

powershell
terraform init
terraform fmt
terraform validate
terraform plan
terraform apply

Réponds yes à la question de apply. Points de contrôle, dans l'ordre : Terraform has been successfully initialized!, rien pour fmt, Success! The configuration is valid., Plan: 1 to add, 0 to change, 0 to destroy., Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Vérifie ce qui est apparu dans le dossier :

powershell
Get-ChildItem -Force
text
.terraform
.terraform.lock.hcl
main.tf
message.txt
terraform.tfstate

(-Force affiche aussi les entrées cachées, celles dont le nom commence par un point ; sans lui, tu ne verrais que main.tf, message.txt et terraform.tfstate.) Cinq entrées :

EntréeQui l'a crééeCe que c'est
.terraforminitDossier caché, le provider téléchargé. Jamais dans Git.
.terraform.lock.hclinitLes versions de providers choisies. Dans Git.
main.tftoiLe code.
message.txtapplyLa ressource.
terraform.tfstateapplyLe state, le relevé. Jamais dans Git, jamais partagé.

Lis le fichier créé :

powershell
Get-Content .\message.txt
text
Bonjour, ce fichier a été créé avec Terraform.

Tu peux aussi le vérifier dans l'explorateur Windows : ouvre le dossier lab-terraform\travail\projet-01-local, double-clique sur message.txt.

A.7 — Lire le state

Déroule T7, T8 et T9 :

powershell
terraform state list
terraform state show local_file.message
terraform output

Points de contrôle : local_file.message ; le bloc complet avec filename = "./message.txt" et un id de quarante caractères ; Warning: No outputs found.

Si tu es curieux, regarde le state lui-même, sans le modifier :

powershell
Get-Content .\terraform.tfstate

Du JSON avec "serial", "terraform_version": "1.12.2" et une liste "resources" contenant local_file.message. Referme-le. On ne l'édite jamais à la main.

A.8 — Modifier le contenu et observer le remplacement

Dans VS Code, remplace la ligne content de main.tf par :

hcl
  content  = "Deuxième version du fichier créée avec Terraform."

Enregistre, puis déroule T10 et T11 :

powershell
terraform plan
terraform apply

Réponds yes. Points de contrôle : # local_file.message must be replaced, # forces replacement au bout de la ligne content, Plan: 1 to add, 0 to change, 1 to destroy., puis Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

powershell
Get-Content .\message.txt
text
Deuxième version du fichier créée avec Terraform.

A.9 — Détruire et vérifier

Déroule T12 et T13 :

powershell
terraform destroy
terraform state list

Réponds yes. Points de contrôle : Destroy complete! Resources: 1 destroyed., puis une sortie vide pour state list.

powershell
Get-ChildItem -Force
text
.terraform
.terraform.lock.hcl
main.tf
terraform.tfstate
terraform.tfstate.backup

message.txt a disparu. terraform.tfstate est toujours là mais ne contient plus de ressource ; terraform.tfstate.backup est la copie d'avant le destroy. .terraform et .terraform.lock.hcl restent : c'est normal, ils te serviront si tu relances apply.

A.10 — Le compteur du kit

Remonte à la racine du kit et lance etat :

powershell
cd ..\..
.\labo.ps1 etat
text
travail projet-01-local : aucune ressource
Ressources encore gérées : 0 (0 attendu à la fin d'une séance).

Si tu avais oublié le destroy, la sortie dirait travail projet-01-local : 1 ressource dans le state puis Ressources encore gérées : 1 (0 attendu à la fin d'une séance). : c'est le garde-fou que tu lanceras à la fin de chaque séance, quand les ressources seront facturées.

Pour recommencer à zéro, plutôt que de nettoyer le dossier : .\labo.ps1 nouveau projet-01-reprise, et tu repars d'un dossier vide.

Annexe B — Pas à pas détaillé sous Linux, macOS, WSL 2 et Git Bash

Afficher le pas à pas Linux, macOS, WSL 2 et Git Bash (B.0 à B.10)

Toutes les commandes de cette annexe se tapent dans un terminal bash (ou zsh sur macOS), avec ./labo.sh …. Les sorties Terraform sont celles de la machine du cours (Terraform 1.12.2) ; les sorties ls -la ont été capturées sous Git Bash, les colonnes de droits, de propriétaire et de date diffèrent chez toi.

B.0 — Avant de commencer

  • Avoir lu les leçons 01 et 02 du module.
  • Git installé (git --version répond), VS Code installé avec la commande code disponible dans le terminal (sur macOS : VS Code → Cmd+Shift+P → « Shell Command: Install 'code' command in PATH »).
  • Si ./labo.sh répond Permission denied : chmod +x labo.sh, une seule fois.
  • À la fin, ./labo.sh etat doit afficher Ressources encore gérées : 0 (0 attendu à la fin d'une séance).

B.1 — Installer Terraform

Suis la page officielle https://developer.hashicorp.com/terraform/install pour ton système. En résumé :

  • Ubuntu, Debian, WSL 2 Ubuntu : ajoute le dépôt HashiCorp (apt avec la clé GPG, commandes copiées depuis la page officielle), puis sudo apt-get install terraform.
  • macOS avec Homebrew : brew tap hashicorp/tap puis brew install hashicorp/tap/terraform.
  • Toute distribution, binaire : télécharge le .zip Linux AMD64 (ou ARM64), unzip, puis sudo mv terraform /usr/local/bin/.
  • Git Bash sous Windows : installe Terraform comme en annexe A.1 (winget), Git Bash le voit dans le PATH.

Ferme le terminal, rouvre-le, puis :

bash
terraform version

Point de contrôle : Terraform v1.12.2 (ou plus récent) suivi de on linux_amd64, on darwin_arm64 ou on windows_amd64 selon ta machine. Tout ce qui est 1.6 ou plus convient.

Si tu vois autre chose : bash: terraform: command not found → le binaire n'est pas dans un dossier du PATH (echo $PATH), ou le terminal n'a pas été rouvert. Annexe C.

B.2 — Installer et configurer AWS CLI version 2 (pour les projets suivants)

Le projet 01 n'utilise pas AWS ; cette étape prépare le projet 03. Tu peux la faire maintenant ou la remettre à plus tard.

Suis https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html pour ton système (paquet .zip avec ./aws/install sous Linux, .pkg sous macOS). Puis :

bash
aws --version

Point de contrôle : une version qui commence par aws-cli/2. Si tu vois aws-cli/1, c'est l'ancienne version : désinstalle-la.

Configure les clés créées dans la section La console AWS, dans le navigateur :

bash
aws configure
text
AWS Access Key ID [None]: TON_ACCESS_KEY_ID
AWS Secret Access Key [None]: TA_SECRET_ACCESS_KEY
Default region name [None]: ca-central-1
Default output format [None]: json

Vérifie que la CLI parle bien à AWS :

bash
aws sts get-caller-identity

Exemple de réponse (les valeurs sont celles de ton compte) :

json
{
    "UserId": "AIDA...",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/terraform-admin"
}

Cas AWS Academy ou sandbox temporaire : ouvre le laboratoire, clique sur AWS Details ou Credentials, copie Access Key, Secret Key et Session Token (obligatoire avec des accès temporaires), puis :

bash
aws configure set aws_access_key_id TON_ACCESS_KEY_ID
aws configure set aws_secret_access_key TA_SECRET_ACCESS_KEY
aws configure set aws_session_token TON_SESSION_TOKEN
aws configure set region ca-central-1
aws configure set output json
aws sts get-caller-identity

B.3 — Cloner le kit et vérifier les prérequis

Place-toi dans le dossier où tu ranges tes projets (par exemple ~/projets), puis :

bash
git clone https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr.git lab-terraform
cd lab-terraform
ls

Point de contrôle : ls liste labo.ps1 labo.sh projets README.md (et .gitattributes, .gitignore avec ls -a). Puis :

bash
./labo.sh prerequis
text
OK  terraform 1.12.2
OK  aws 2.22.18
OK  git 2.49.0.windows.1
OK  code 1.135.0
OK  gh 2.81.0 (optionnel)
ABSENT  az (optionnel, projet 08B seulement)
OK  gcloud 544.0.0 (optionnel)
Prérequis : 4 outils requis présents sur 4.

(Sortie capturée sous Git Bash, d'où le git 2.49.0.windows.1 ; sous Linux tu liras ta version de Git.) Les quatre outils requis sont terraform, aws, git et code. gh, az et gcloud sont optionnels et peuvent être ABSENT sans conséquence avant le module 3. Si aws est ABSENT parce que tu as remis B.2 à plus tard, la dernière ligne dit 3 outils requis présents sur 4 : tu peux quand même continuer ce projet.

B.4 — Créer le dossier de travail et l'ouvrir dans VS Code

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

Fais ce que le script te dit, puis ouvre le dossier dans VS Code :

bash
cd travail/projet-01-local
code .

Le dossier travail/ est ignoré par Git : tu peux y écrire librement sans salir le kit. Le corrigé reste dans projets/01-terraform-local/.

Si code . ne fait rien : ouvre VS Code à la main, FileOpen Folder, et choisis lab-terraform/travail/projet-01-local.

B.5 — Créer main.tf

Dans VS Code : icône New File dans l'explorateur de gauche, nomme le fichier main.tf, colle le code de la section Le code : fichier par fichier, enregistre avec Ctrl+S (Cmd+S sur macOS).

Troisième chemin, sans éditeur, si tu préfères le terminal :

bash
cat > main.tf <<'EOF'
terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}

provider "local" {}

resource "local_file" "message" {
  filename = "${path.module}/message.txt"
  content  = "Bonjour, ce fichier a été créé avec Terraform."
}
EOF

(Les apostrophes autour de 'EOF' empêchent bash d'interpréter ${path.module} : sans elles, la ligne filename serait vidée.) Vérifie :

bash
cat main.tf
ls -la
text
total 1
drwxr-xr-x 1 rehou 197609   0 sept. 15 14:47 .
drwxr-xr-x 1 rehou 197609   0 sept. 15 14:47 ..
-rw-r--r-- 1 rehou 197609 286 sept. 15 14:47 main.tf

Point de contrôle : les quinze lignes du fichier, et une seule entrée dans le dossier, main.tf, d'environ 286 octets.

B.6 — Initialiser, formater, valider, planifier, appliquer

Déroule T2 à T6 de la section Terraform, dans le terminal :

bash
terraform init
terraform fmt
terraform validate
terraform plan
terraform apply

Réponds yes à la question de apply. Points de contrôle, dans l'ordre : Terraform has been successfully initialized!, rien pour fmt, Success! The configuration is valid., Plan: 1 to add, 0 to change, 0 to destroy., Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Vérifie ce qui est apparu dans le dossier :

bash
ls -la
text
total 14
drwxr-xr-x 1 rehou 197609    0 sept. 15 14:47 .
drwxr-xr-x 1 rehou 197609    0 sept. 15 14:47 ..
drwxr-xr-x 1 rehou 197609    0 sept. 15 14:47 .terraform
-rw-r--r-- 1 rehou 197609 1257 sept. 15 14:47 .terraform.lock.hcl
-rw-r--r-- 1 rehou 197609  286 sept. 15 14:47 main.tf
-rw-r--r-- 1 rehou 197609   50 sept. 15 14:47 message.txt
-rw-r--r-- 1 rehou 197609 1667 sept. 15 14:47 terraform.tfstate

(-a affiche aussi les entrées cachées, celles dont le nom commence par un point ; -l donne le détail.) Cinq entrées en plus de . et .. :

EntréeQui l'a crééeCe que c'est
.terraforminitDossier caché, le provider téléchargé. Jamais dans Git.
.terraform.lock.hclinitLes versions de providers choisies. Dans Git.
main.tftoiLe code.
message.txtapplyLa ressource. 50 octets : le texte, sans retour à la ligne final.
terraform.tfstateapplyLe state, le relevé. Jamais dans Git, jamais partagé.

Lis le fichier créé :

bash
cat message.txt
text
Bonjour, ce fichier a été créé avec Terraform.

(Le fichier n'a pas de retour à la ligne final : ton invite de commande peut apparaître collée à la fin de la phrase. C'est normal.)

B.7 — Lire le state

Déroule T7, T8 et T9 :

bash
terraform state list
terraform state show local_file.message
terraform output

Points de contrôle : local_file.message ; le bloc complet avec filename = "./message.txt" et un id de quarante caractères ; Warning: No outputs found.

Si tu es curieux, regarde le state lui-même, sans le modifier :

bash
cat terraform.tfstate

Du JSON avec "serial", "terraform_version": "1.12.2" et une liste "resources" contenant local_file.message. Referme-le. On ne l'édite jamais à la main.

B.8 — Modifier le contenu et observer le remplacement

Dans VS Code, remplace la ligne content de main.tf par :

hcl
  content  = "Deuxième version du fichier créée avec Terraform."

Enregistre, puis déroule T10 et T11 :

bash
terraform plan
terraform apply

Réponds yes. Points de contrôle : # local_file.message must be replaced, # forces replacement au bout de la ligne content, Plan: 1 to add, 0 to change, 1 to destroy., puis Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

bash
cat message.txt
text
Deuxième version du fichier créée avec Terraform.

B.9 — Détruire et vérifier

Déroule T12 et T13 :

bash
terraform destroy
terraform state list

Réponds yes. Points de contrôle : Destroy complete! Resources: 1 destroyed., puis une sortie vide pour state list.

bash
ls -la
text
total 17
drwxr-xr-x 1 rehou 197609    0 sept. 15 14:47 .
drwxr-xr-x 1 rehou 197609    0 sept. 15 14:47 ..
drwxr-xr-x 1 rehou 197609    0 sept. 15 14:47 .terraform
-rw-r--r-- 1 rehou 197609 1257 sept. 15 14:47 .terraform.lock.hcl
-rw-r--r-- 1 rehou 197609  286 sept. 15 14:47 main.tf
-rw-r--r-- 1 rehou 197609  181 sept. 15 14:47 terraform.tfstate
-rw-r--r-- 1 rehou 197609 1667 sept. 15 14:47 terraform.tfstate.backup

message.txt a disparu. terraform.tfstate est passé de 1667 à 181 octets : il ne contient plus de ressource ; terraform.tfstate.backup (1667 octets) est la copie d'avant le destroy. .terraform et .terraform.lock.hcl restent : c'est normal, ils te serviront si tu relances apply.

bash
cat terraform.tfstate
json
{
  "version": 4,
  "terraform_version": "1.12.2",
  "serial": 3,
  "lineage": "9ddb3372-803f-0964-7665-8413b8f65ee1",
  "outputs": {},
  "resources": [],
  "check_results": null
}

"resources": [] : le relevé est vide. Ton lineage (l'identifiant unique de ce state) sera différent.

B.10 — Le compteur du kit

Remonte à la racine du kit et lance etat :

bash
cd ../..
./labo.sh etat
text
travail projet-01-local : aucune ressource
Ressources encore gérées : 0 (0 attendu à la fin d'une séance).

Si tu avais oublié le destroy, la sortie dirait travail projet-01-local : 1 ressource dans le state puis Ressources encore gérées : 1 (0 attendu à la fin d'une séance). : c'est le garde-fou que tu lanceras à la fin de chaque séance, quand les ressources seront facturées.

Pour recommencer à zéro, plutôt que de nettoyer le dossier : ./labo.sh nouveau projet-01-reprise, et tu repars d'un dossier vide.

Annexe C — Si ça coince (tous systèmes)

Afficher les cas qui coincent

Chaque cas : le message exact → la cause → le geste.

  • terraform plan répond Error: Inconsistent dependency lock filerequired by this configuration but no version is selected → Tu n'as pas lancé terraform init dans ce dossier (ou tu as ajouté un provider depuis). Le message finit par la solution : terraform init, puis relance.

  • terraform validate répond Error: Missing required providerYou may be able to install it automatically by running: terraform init → Même cause, même geste : terraform init.

  • Error: Unsupported argumentAn argument named "contenu" is not expected here. Did you mean "content"? → Faute de frappe dans un nom d'argument. Terraform propose souvent le bon mot. Corrige, réenregistre, relance validate.

  • Error: Unclosed configuration blockThere is no closing brace for this block before the end of the file. → Une accolade } manque. Compte-les : chaque { a son }. Dans main.tf, il y en a trois paires imbriquées pour le bloc terraform, une pour provider, une pour resource.

  • Error: Invalid multi-line string puis Error: Unterminated template stringNo closing marker was found for the string. → Un guillemet " manque à la fin d'une valeur. La ligne fautive est citée (3: content = "Bonjour Terraform).

  • Error: Invalid resource typeThe provider hashicorp/local does not support resource type "local_fichier". → Le type de ressource n'existe pas dans ce provider. Les types du provider local s'appellent local_file et local_sensitive_file ; la documentation du provider sur le Registry en donne la liste.

  • Error: Invalid Attribute CombinationNo attribute specified when one (and only one) of [content,sensitive_content,content_base64] is required (répété quatre fois) → Tu as retiré la ligne content : un local_file doit dire ce qu'il contient. Remets-la.

  • terraform validate dit Success! mais terraform plan répond Error: No configuration filescreate a Terraform configuration file (.tf file) and try again. → Il n'y a aucun fichier .tf dans le dossier : soit tu n'es pas dans travail/projet-01-local (vérifie avec pwd), soit le fichier s'appelle main.tf.txt. Renomme-le en main.tf. Un dossier vide est une configuration valide (vide), d'où le Success! trompeur.

  • terraform apply répond Apply cancelled. et rien n'est créé → Tu as tapé autre chose que yes (par exemple y, Y, oui, ou Entrée seul). Relance et tape yes en entier.

  • terraform state list répond No state file was found! → Aucun apply n'a encore été fait dans ce dossier, ou tu n'es pas dans le bon dossier. Ce n'est pas une panne : il n'y a encore rien à lister.

  • terraform state show répond No instance found for the given address! → L'adresse est mal tapée, ou la ressource a été détruite. terraform state list donne les adresses exactes.

  • terraform fmt -check sort en erreur (code 3) en affichant main.tf → Le fichier n'est pas au format officiel (indentation, alignement des =). Ce n'est pas une erreur de syntaxe : terraform fmt sans -check le corrige.

  • Après destroy, terraform.tfstate est toujours là et tu crois que le destroy a échoué → Non : ouvre-le, "resources": []. Le fichier reste, vide, avec un terraform.tfstate.backup à côté. Destroy complete! Resources: 1 destroyed. fait foi.

  • .\labo.ps1 etat ou ./labo.sh etat affiche Ressources encore gérées : 1 → Un destroy a été oublié dans un des dossiers listés au-dessus (travail projet-01-local : 1 ressource dans le state). Va dans ce dossier, terraform destroy, yes, relance etat.

  • Windows seulement — terraform : Le terme «terraform» n'est pas reconnu comme nom d'applet de commande, fonction, fichier de script ou programme exécutable. → Terraform n'est pas dans le PATH, ou PowerShell n'a pas été redémarré après l'installation. Ferme toutes les fenêtres PowerShell, rouvre, retape terraform version. Si ça persiste, annexe A.1 (ajout de C:\terraform au PATH).

  • Windows seulement — aws : Le terme «aws» n'est pas reconnu… → AWS CLI n'est pas installé ou PowerShell n'a pas été redémarré. Réinstalle AWS CLI version 2 (A.2), ferme et rouvre PowerShell, retape aws --version.

  • Windows seulement — .\labo.ps1 est refusé : « l'exécution de scripts est désactivée sur ce système »Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, réponds O, relance. Une seule fois par machine.

  • Windows seulement — le fichier s'appelle main.tf.txt → Le Bloc-notes ajoute .txt. Dans PowerShell : Rename-Item main.tf.txt main.tf. Utilise VS Code pour créer les fichiers.

  • Linux natif et macOS — bash: terraform: command not found → Le binaire n'est pas dans un dossier du PATH. sudo mv terraform /usr/local/bin/, ou vérifie l'installation par paquet (B.1). Rouvre le terminal.

  • macOS et bash — ./labo.sh répond Permission deniedchmod +x labo.sh, une seule fois. Si bash: ./labo.sh: /bin/bash^M: bad interpreter, le fichier a des fins de ligne Windows : git config core.autocrlf input puis re-clone, ou sed -i 's/\r$//' labo.sh (sed -i '' … sur macOS).

  • aws sts get-caller-identity répond AccessDenied ou InvalidClientTokenId → La clé configurée n'est pas la bonne, ou le token de session Academy a expiré. aws configure avec la clé de terraform-admin (vérifie que l'utilisateur est dans terraform-admins avec AdministratorAccess), ou refais aws configure set … avec de nouveaux identifiants Academy. Sans effet sur le projet 01, qui n'appelle pas AWS.

Ce que tu remets

Un dossier compressé ou un dépôt Git contenant :

  • main.tf (le fichier final, avec la deuxième version du content) ;
  • sorties.txt : la sortie complète de terraform plan (première version), de terraform state list après apply, du terraform plan de modification (celui avec must be replaced), et de terraform state list après destroy (vide) ;
  • README.md sur le modèle ci-dessous.

Ne remets ni le dossier .terraform/, ni terraform.tfstate, ni aucune Access Key.

markdown
# Projet 01 — Terraform local

- Terraform : (sortie de `terraform version`)
- Système : Windows / Linux / macOS

## Ce que j'ai fait
init, fmt, validate, plan, apply, state list, state show, modification de content, plan, apply, destroy, state list.

## Le fichier terraform.tfstate, en trois lignes à moi
(À quoi il sert. Pourquoi Terraform en a besoin pour destroy. Pourquoi on ne l'édite pas et on ne le partage pas.)

## Ce qui m'a bloqué, et comment j'ai débloqué
(Une ligne, ou « rien ».)

Sources officielles

Phrase à retenir : Terraform ne clique pas dans la console à ta place. Il lit ton code, le compare à son state et au réel, t'annonce ce qui va changer, puis crée, remplace ou détruit pour atteindre l'état demandé.