How to read this page. Each section is collapsed under its title: click "Show …" to open it, close it when you are done. Reading order: Objective, then In short (all the commands, to copy in order), then The code (the
main.tffile explained line by line), then Terraform, in the terminal (T1 to T13, one command at a time with its real output). The detailed walkthrough, with the tool installation, the AWS account preparation and the expected output of each step, is in the appendices: Appendix A for Windows (PowerShell), Appendix B for Linux, macOS, WSL 2 and Git Bash. Open a single appendix, the one for your system. Appendix C, shared, gathers the cases where things go wrong.
You join a team that manages its whole infrastructure with Terraform. Before letting you touch the AWS account, your manager asks you one thing: "Show me that you master the full cycle on your machine. One file is enough. I want to see the plan before the apply, the state after, the modification detected, and a clean folder at the end." That is exactly this project. The local provider writes a text file on your disk instead of creating a server, but the commands, the sentences displayed and the files produced are the same as for an S3 bucket or a GitHub repository. Everything you read here, you will read again in the eight following projects.
The ten steps of this diagram are detailed, with the expected output of each command, in Appendix A (Windows) or Appendix B (Linux, macOS, WSL 2, Git Bash) at the bottom of the page. The Terraform commands themselves, identical on every system, are explained one by one in Terraform, in the terminal.
Course kit: https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr
You clone the kit into a lab-terraform folder, you check your tools, you create an empty working folder, you write main.tf in it yourself, then you run the cycle: init, fmt, validate, plan, apply, verification, state, modification, destroy. At the end, terraform state list displays nothing anymore and etat says Ressources encore gérées : 0 (0 attendu à la fin d'une séance). (resources still managed: 0). The kit contains the solution in projets/01-terraform-local/, to open only if you get stuck: the goal is to write the file by hand.
Windows (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).If PowerShell refuses .\labo.ps1 ("running scripts is disabled on this system"): Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, answer Y (O on a French system), run again.
Linux, macOS, WSL 2, Git 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).If ./labo.sh answers Permission denied: chmod +x labo.sh, once only.
The project fits in a single file, main.tf, to create in travail/projet-01-local/. Type it or paste it exactly like this (it is word for word the kit's solution, projets/01-terraform-local/main.tf):
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."
}Three blocks, from the most general to the most concrete.
| Line | What it tells Terraform |
|---|---|
terraform { … } | The settings of Terraform itself for this folder. |
required_providers { local = { … } } | "This project needs a plugin called local." |
source = "hashicorp/local" | Where to download it: the local provider published by HashiCorp on the Terraform Registry. |
version = "~> 2.5" | Which version to accept: 2.5 or newer, but not 3.0. On the course machine, init picked v2.9.1. |
provider "local" {} | "Enable this provider." The braces are empty because the local provider has nothing to configure (no region, no account). |
resource "local_file" "message" { … } | "Manage an object of type local_file, which I call message in my code." The type comes from the provider (local_ + file); the name is yours. Together they form the address local_file.message. |
filename = "${path.module}/message.txt" | Where to write the file. path.module is the folder containing this main.tf; Terraform will display it as ./message.txt. |
content = "Bonjour, ce fichier a été créé avec Terraform." | The text to write inside (French for "Hello, this file was created with Terraform."). This is the line you will change at step T10. |
Terraform runs its commands from the current folder. If you write filename = "message.txt", the file is created where you type terraform apply, which is usually the right folder, but not always (when the code is called as a module from another folder, in Module 5). path.module always designates the folder of the .tf file, wherever Terraform is launched from. The ${…} syntax inserts the value of an expression into a string: "${path.module}/message.txt" becomes "./message.txt".
It still works: Terraform infers from the local_file type that it needs the hashicorp/local provider, and init displays Finding latest version of hashicorp/local... instead of Finding hashicorp/local versions matching "~> 2.5".... That is what the two fundamental workshops of this module do, to keep things as short as possible. But without a version constraint, two colleagues who run init six months apart can get two different versions of the provider. In a real project, you always write the block.
The commands in this section are typed in the travail/projet-01-local folder, once main.tf is written. They are identical on Windows, Linux and macOS: Terraform is the same program everywhere. The outputs are those of the course machine (Terraform 1.12.2, Windows 11), captured as is; only the identifiers (id=ff6b93dc…) and the fingerprints change from one machine to another. The commands that do depend on the system (reading the file, listing the folder) are in your appendix.
An image to keep for the whole section: Lesson 01 introduced the architect. Each command below is one of their gestures. init opens the construction site, plan writes the estimate, apply has it carried out, state rereads the record, destroy takes everything down.
The rule: a single new thing per command. We start with the command that takes no parameter and touches nothing.
terraform versionTerraform v1.12.2
on windows_amd64What the command asks: "Terraform, which version are you, and on which system?" No parameter, no file read, nothing modified. If this command fails (terraform : Le terme «terraform» n'est pas reconnu… or command not found), nothing else will work: it is the installation or the PATH, see Appendix A.1 or B.1. On the course machine, Terraform sometimes adds two lines Your version of Terraform is out of date!: that is information, not an error.
The familiar equivalent: aws --version, git --version, python --version. Same gesture, same purpose.
terraform initInitializing 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.
…What the command asks: "Read my main.tf, download the providers it requires, and prepare this folder for work."
A single new thing: the folder becomes a Terraform project. Read the output line by line:
| Line | What happened |
|---|---|
Initializing the backend... | The backend is the place where the state will be stored. Nothing is specified in main.tf, so it will be a local file, terraform.tfstate. |
Finding hashicorp/local versions matching "~> 2.5"... | Terraform read your version constraint and is searching the Registry. |
Installing hashicorp/local v2.9.1... | It picked the most recent one that satisfies ~> 2.5, and downloads it into .terraform/. |
(signed by HashiCorp) | The plugin is signed: you do have the real provider. |
Terraform has created a lock file .terraform.lock.hcl | The chosen version is recorded in a file to keep in Git: the next init, on a colleague's machine, will take the same one. |
Terraform has been successfully initialized! | The sentence to expect. |
Two things appeared in your folder: the hidden .terraform/ directory (the downloaded provider, about 18 MB on the course machine, never to be put in Git) and the .terraform.lock.hcl file (22 lines, to put in Git). You will see them in the appendix, step A.6 or B.6.
The familiar equivalent: npm install or pip install -r requirements.txt. You read a list of dependencies, download, lock the versions (package-lock.json).
Terraform refuses to continue and tells you what to do. terraform plan without init answers:
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 initand terraform validate without init:
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 initIn both cases, the last line is the solution. init can be rerun safely as many times as you want; it never touches your resources.
terraform fmtWhat the command asks: "Realign the spaces and indentation of my .tf files according to the official style."
A single new thing: the empty output. fmt displays the name of each file it modified; if you pasted the code as is, it has nothing to fix and displays nothing. That is not an error, it is the best possible answer. If you had misindented a line, it would have displayed main.tf and rewritten the file.
The familiar equivalent: prettier --write, black, gofmt. The kit checks this point with terraform fmt -check -recursive, which modifies nothing and exits with an error (code 3) if a file is not formatted.
terraform validateSuccess! The configuration is valid.What the command asks: "Reread my blocks. Do the argument names exist, are the braces closed, do the references point to something?"
A single new thing: Terraform consults the provider to know the valid arguments of a local_file. That is why validate requires init beforehand. It does not read the state, contacts no API, creates nothing. The sentence to expect: Success! The configuration is valid.
What it catches: contenu instead of content (An argument named "contenu" is not expected here. Did you mean "content"?), a forgotten brace (Error: Unclosed configuration block), an unclosed quote (Error: Unterminated template string), a nonexistent type (The provider hashicorp/local does not support resource type "local_fichier".). Appendix C goes through them one by one.
What it does not catch: a path that does not exist, a bucket name already taken, a bad architecture. validate says "this is correct HCL", not "this is a good idea".
terraform planTerraform 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.What the command asks: "Compare what my code describes with what you already know, and tell me what you would do, without doing it."
A single new thing: the estimate. Nothing has been created; message.txt still does not exist. Read it from top to bottom:
| Line | How to read it |
|---|---|
+ create | The legend: in this plan, there are only creations. |
# local_file.message will be created | The address of the resource (type . name) and its fate. |
+ resource "local_file" "message" { | The whole block is preceded by a +: everything is new. |
+ content = "Bonjour, …" | An attribute you wrote: Terraform knows its value. |
+ content_md5 = (known after apply) | An attribute the provider will compute after creation: the fingerprint of the content. Terraform cannot guess it beforehand. |
+ directory_permission = "0777" | An attribute you did not write: the provider has a default value. |
+ filename = "./message.txt" | Your ${path.module}/message.txt, resolved. |
Plan: 1 to add, 0 to change, 0 to destroy. | The line to read first, always. One creation, no modification, no destruction. |
Note: You didn't use the -out option… | Information: this estimate is not saved; apply will recompute one. Irrelevant here. |
The familiar equivalent: git diff before git commit, or the "simulation" mode (--dry-run, -WhatIf) of a script. The difference: a --dry-run script shows you what it is going to do; terraform plan shows you what is missing between the code and reality, after rereading reality.
For a local_file, the local provider exposes everything it knows how to compute: six fingerprints of the content (md5, sha1, sha256, sha512, and two base64-encoded versions), the permissions, the identifier. You wrote only two of them (filename, content); the others are attributes the provider fills in. It is the same for an S3 bucket: you write three lines, the plan displays thirty (ARN, region, owner identifier…). Learn to spot yours and skim the others, except the last Plan: line, which is never skimmed.
terraform applyTerraform 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.What the command asks: "Recompute the estimate, show it to me, and if I answer yes, do it."
A single new thing: the question. Terraform stops at Enter a value: and waits. Type yes in full, then Enter. y, Y, oui or an empty line give Apply cancelled. and nothing is touched: it is a protection, not a bug. Then three lines: Creating..., Creation complete after 0s [id=…] (the identifier the plan announced as (known after apply) is now known), and the sentence to expect: Apply complete! Resources: 1 added, 0 changed, 0 destroyed. The three numbers are those of the plan.
Now message.txt exists in your folder, and a new file has appeared next to it: terraform.tfstate. Go check it with your appendix (A.6 or B.6) before continuing: the file does contain Bonjour, ce fichier a été créé avec Terraform.
The familiar equivalent: a script that writes the file, Set-Content or echo > message.txt. The difference: the script writes and forgets; Terraform writes and records in its ledger that it wrote, under which identifier, with which content.
terraform state listlocal_file.messageWhat the command asks: "List everything you manage in this folder."
A single new thing: the state can be read. One line, one resource: the address local_file.message, the same as in the plan. This line proves that Terraform tied the block of your code to the real file: that is what will let it modify or destroy it later without you having to tell it which one again.
Before apply, the same command answered No state file was found!: the record did not exist yet. After destroy (T12), it will answer nothing at all.
The familiar equivalent: docker ps, kubectl get pods, aws s3 ls. "What is running, what exists?" Except that state list does not go look at reality: it reads its notebook.
terraform state show local_file.message# 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"
}What the command asks: "Show me everything you know about the resource local_file.message."
A single new thing: the command takes a parameter, the address read in T7. Compare with the plan of T5: all the (known after apply) values are now filled in. The id is the SHA-1 fingerprint of the content, the same value as content_sha1 and as the [id=…] displayed by apply. It is the architect's record, room by room.
If you get the address wrong (local_file.mesage), Terraform answers No instance found for the given address! and sends you back to terraform state list.
The terraform.tfstate file is JSON: a serial number ("serial"), the Terraform version, and a "resources" list where each resource has exactly the attributes that state show displays. You can open it to read it. You never modify it by hand: one misplaced comma, and Terraform no longer recognizes what it built, or believes it manages something that no longer exists. To act on the state, there are commands (terraform state rm, terraform state mv, terraform import) that you will see in Module 4.
Two more rules, starting today. The state can contain secrets (here, the text of the file; elsewhere, a database password): it is not shared by message nor committed to Git. And it is the memory of Terraform: if you delete it, Terraform believes nothing exists and offers to recreate everything, while the resources are still there and, in the cloud, still billed.
terraform outputWarning: 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.What the command asks: "Display the values the code chose to expose with output blocks."
A single new thing: a warning (Warning), not an error. Your main.tf has no output block, so Terraform has nothing to display and tells you so. You will write your first outputs in Project 02; here, the command is used to see the difference between "it failed" (Error:) and "there is nothing to show" (Warning:).
Open main.tf, replace the content line with this one, save:
content = "Deuxième version du fichier créée avec Terraform."Then:
terraform planlocal_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.What the command asks: the same thing as in T5. But this time Terraform has a record to compare against.
A single new thing: the -/+ symbol, "destroy then recreate". Three clues tell you, and you must know how to spot them:
| Clue | Where | What it says |
|---|---|---|
must be replaced | the title # local_file.message must be replaced | This is not will be updated in-place. |
# forces replacement | at the end of the line ~ content = "…" -> "…" | This attribute is the one forcing the replacement. The ~ in front says the value changes; the comment says this change cannot be done in place. |
Plan: 1 to add, 0 to change, 1 to destroy. | the last line | One destruction and one creation, zero modification. |
Look also at the first line, which is new: Refreshing state... [id=…]. Before comparing, Terraform went to reread the real file to check that it still matches its record. That is where it would detect drift (Lesson 01), if you had modified message.txt by hand.
The essential difference between
~and-/+. For a text file, being replaced or modified in place amounts to the same thing: at the end, the content is the new one. For a database, a disk or a user,-/+means loss of the object and of its identifier: the disk's data, the user's password, the server's IP address. The provider decides, attribute by attribute, what forces a replacement; you read it in the plan before sayingyes. Thelocalprovider replaces the file as soon ascontentchanges: never a lone~for this attribute.
terraform apply…
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.What the command asks: the same thing as in T6.
Nothing new in the command; everything is in the output. Four action lines instead of two: Destroying..., Destruction complete, then Creating..., Creation complete. The order is that of the -/+ symbol: the minus first, the plus afterwards. And the identifier changed (ff6b93dc… before, 1df9196a… after): it is a different object, not the same one modified. Check the new content with your appendix (A.8 or B.8): Deuxième version du fichier créée avec Terraform.
Summary of T5 to T11: you have seen the two estimates you will reread throughout your career: + (create) and -/+ (replace). The third, ~ (modify in place), does not exist for the content of a local_file; you will meet it in the cloud projects, for example when a tag changes on an AWS resource.
terraform destroylocal_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.What the command asks: "Destroy everything you manage in this folder, and show me the estimate first."
A single new thing: the - symbol on its own, and a more serious question: Do you really want to destroy all resources? … There is no undo. Every attribute goes to -> null: there will be nothing left. The sentence to expect: Destroy complete! Resources: 1 destroyed. The message.txt file has disappeared from your folder.
This is the command that closes every session of this course. Here it erases a text file; in Project 03 it will delete a bucket which, otherwise, would remain billed.
terraform state listWhat the command asks: the same as in T7.
Nothing new, and an empty output: Terraform no longer manages anything in this folder. The terraform.tfstate file still exists, but its "resources" list is [], and a terraform.tfstate.backup has appeared next to it (the version from before the destroy, which Terraform keeps out of caution). Your appendix (A.9 and A.10, or B.9 and B.10) has you list the folder and run etat for the final proof: Ressources encore gérées : 0 (0 attendu à la fin d'une séance).
Summary of T1 to T13: init once, then fmt, validate, plan, apply, state list, state show, a modification plan and apply, destroy, state list. Thirteen commands, five sentences to recognize: successfully initialized!, The configuration is valid., Plan: N to add, N to change, N to destroy., Apply complete!, Destroy complete!.
The message to take away. A PowerShell or bash script would have written
message.txtin one line, and deleted it in another. What the script does not do and what you saw Terraform do: announce before acting (plan), ask for explicit agreement (yes), record what it created (state list), reread reality before every decision (Refreshing state...), distinguish modifying from replacing (-/+), and know how to undo everything without being told what (destroy).
Project 01 does not touch AWS. But Project 03 creates a real S3 bucket, and you will need an account that is ready. This section is done in the browser, it is identical on Windows, Linux and macOS, and you can do it before or after the Terraform cycle of this project. The aws configure and aws sts get-caller-identity commands that go with it are in your appendix (A.2 or B.2).
If you work in AWS Academy, AWS Educate, a sandbox provided by your school or a temporary lab account: use the credentials provided by the platform and go directly to step A.2 or B.2 (the "Academy" case). Do not create an IAM user if your teacher already gives you temporary access.
ca-central-1 unless the lab imposes another one.The root account is only used to create the account, manage billing and carry out sensitive operations. Enable MFA (two-factor authentication) on the root account, then create a separate user for the lab work, below.
IAM and open the IAM service.terraform-admins.AdministratorAccess, tick the AdministratorAccess policy.For a lab only.
AdministratorAccessgives full access to every service and resource in the account. It is convenient for learning; it is not a security model for production, where you give Terraform only the permissions it needs (Module 4).
terraform-admin.terraform-admins → Next → Create user.terraform-admin.aws-cli-terraform-lab → Create access key..csv file or copy Access key and Secret access key into a password manager. The Secret access key will never be displayed again.Never do this. Never paste an Access Key, a Secret Access Key, a session token, an
.envfile or aterraform.tfstateinto GitHub, Word, Teams, Discord, Slack, a ticket or an email. These elements give access to the account. If a key leaks: IAM → the user → Security credentials → Deactivate then Delete, and you create another one.
Before rerunning terraform destroy a second time on a folder that has already been destroyed, write on paper what the last line of the plan will say. Then run it. Next, recreate the file with terraform apply, open message.txt in VS Code, add a word by hand, save, and type terraform plan: what does the Refreshing state... line say, which symbol appears, and what is this gap called in Lesson 01? Finish with destroy: the folder must return to Ressources encore gérées : 0.
All the commands in this appendix are typed in PowerShell (Windows Terminal or PowerShell 7), with .\labo.ps1 …. The reproduced outputs are those of the course machine, on Windows 11 and Terraform 1.12.2.
git --version answers), VS Code installed with the code command available in PowerShell (otherwise: VS Code → Ctrl+Shift+P → "Shell Command: Install 'code' command in PATH", or reinstall ticking "Add to PATH")..\labo.ps1 ("running scripts is disabled on this system"): Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, answer Y (O on a French system), run again. Once per machine..\labo.ps1 etat must display Ressources encore gérées : 0 (0 attendu à la fin d'une séance).The simplest way, with the Windows package manager:
winget install Hashicorp.TerraformClose PowerShell, reopen it, then:
terraform versionCheckpoint:
Terraform v1.12.2
on windows_amd64Your version may be more recent; anything 1.6 or later is fine. If Terraform adds Your version of Terraform is out of date!, that is information, not an error.
Without winget, with the official binary: go to https://developer.hashicorp.com/terraform/install, choose Windows, download the AMD64 version, unzip the .zip, create the folder C:\terraform, move terraform.exe into it, then add C:\terraform to the PATH: Start menu → "environment variables" → Edit the system environment variables → Environment Variables → under System variables, select Path → Edit → New → C:\terraform → OK. Close all PowerShell windows, reopen, retype terraform version.
If you see something else: terraform : Le terme «terraform» n'est pas reconnu comme nom d'applet de commande… (the term is not recognized as a cmdlet name) → Terraform is not in the PATH, or PowerShell was not restarted after the installation. Appendix C.
Project 01 does not use AWS; this step prepares Project 03. You can do it now or postpone it.
.msi: Next, accept the license, keep the default folder, Install.aws --versionCheckpoint: a version starting with aws-cli/2 (on the course machine: aws-cli/2.22.18). If you see aws-cli/1, that is the old version: uninstall it.
Then configure the keys created in the section The AWS console, in the browser:
aws configureAWS 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]: jsonCheck that the CLI does talk to AWS:
aws sts get-caller-identityExample answer (the values are those of your account):
{
"UserId": "AIDA...",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/terraform-admin"
}AWS Academy or temporary sandbox case: open the lab, click AWS Details or Credentials, copy Access Key, Secret Key and Session Token (mandatory with temporary access), then:
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-identityGo to the folder where you keep your projects (for example C:\Users\<you>\Documents), then:
git clone https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr.git lab-terraform
cd lab-terraform
dirCheckpoint: dir lists projets, labo.ps1, labo.sh, README.md (and .gitattributes, .gitignore with dir -Force). Then:
.\labo.ps1 prerequisOK 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.The four required tools are terraform, aws, git and code. gh, az and gcloud are optional and can be ABSENT without consequence before Module 3. If aws is ABSENT because you postponed A.2, the last line says 3 outils requis présents sur 4 (3 required tools present out of 4): you can still continue this project.
.\labo.ps1 nouveau projet-01-localDossier travail\projet-01-local créé (ignoré par Git). Tapez :
cd travail\projet-01-localDo what the script tells you (the folder was created, ignored by Git; type cd travail\projet-01-local), then open the folder in VS Code:
cd travail\projet-01-local
code .The travail\ folder is ignored by Git: you can write in it freely without dirtying the kit. The solution stays in projets\01-terraform-local\.
If code . does nothing: open VS Code by hand, File → Open Folder, and choose lab-terraform\travail\projet-01-local.
main.tfIn VS Code: New File icon in the left explorer, name the file main.tf (careful: not main.tf.txt, VS Code does not add an extension but Windows Notepad would), paste the code from the section The code: file by file, save with Ctrl+S.
Check from PowerShell:
Get-Content .\main.tfCheckpoint: the fifteen lines of the file, from terraform { to }. Then:
Get-ChildItemA single entry: main.tf. If you see main.tf.txt, rename it: Rename-Item main.tf.txt main.tf.
Run T2 to T6 from the section Terraform, in the terminal:
terraform init
terraform fmt
terraform validate
terraform plan
terraform applyAnswer yes to the apply question. Checkpoints, in order: Terraform has been successfully initialized!, nothing for 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.
Check what appeared in the folder:
Get-ChildItem -Force.terraform
.terraform.lock.hcl
main.tf
message.txt
terraform.tfstate(-Force also displays hidden entries, those whose name starts with a dot; without it, you would only see main.tf, message.txt and terraform.tfstate.) Five entries:
| Entry | Who created it | What it is |
|---|---|---|
.terraform | init | Hidden folder, the downloaded provider. Never in Git. |
.terraform.lock.hcl | init | The chosen provider versions. In Git. |
main.tf | you | The code. |
message.txt | apply | The resource. |
terraform.tfstate | apply | The state, the record. Never in Git, never shared. |
Read the created file:
Get-Content .\message.txtBonjour, ce fichier a été créé avec Terraform.You can also check it in Windows Explorer: open the folder lab-terraform\travail\projet-01-local, double-click message.txt.
Run T7, T8 and T9:
terraform state list
terraform state show local_file.message
terraform outputCheckpoints: local_file.message; the full block with filename = "./message.txt" and a forty-character id; Warning: No outputs found.
If you are curious, look at the state itself, without modifying it:
Get-Content .\terraform.tfstateJSON with "serial", "terraform_version": "1.12.2" and a "resources" list containing local_file.message. Close it. It is never edited by hand.
In VS Code, replace the content line of main.tf with:
content = "Deuxième version du fichier créée avec Terraform."Save, then run T10 and T11:
terraform plan
terraform applyAnswer yes. Checkpoints: # local_file.message must be replaced, # forces replacement at the end of the content line, Plan: 1 to add, 0 to change, 1 to destroy., then Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
Get-Content .\message.txtDeuxième version du fichier créée avec Terraform.Run T12 and T13:
terraform destroy
terraform state listAnswer yes. Checkpoints: Destroy complete! Resources: 1 destroyed., then an empty output for state list.
Get-ChildItem -Force.terraform
.terraform.lock.hcl
main.tf
terraform.tfstate
terraform.tfstate.backupmessage.txt has disappeared. terraform.tfstate is still there but no longer contains any resource; terraform.tfstate.backup is the copy from before the destroy. .terraform and .terraform.lock.hcl remain: that is normal, they will be useful if you rerun apply.
Go back up to the root of the kit and run etat:
cd ..\..
.\labo.ps1 etattravail projet-01-local : aucune ressource
Ressources encore gérées : 0 (0 attendu à la fin d'une séance).(No resource in projet-01-local; resources still managed: 0, 0 expected at the end of a session.) If you had forgotten the destroy, the output would say travail projet-01-local : 1 ressource dans le state then Ressources encore gérées : 1 (0 attendu à la fin d'une séance).: this is the guardrail you will run at the end of every session, once resources are billed.
To start over from scratch, rather than cleaning the folder: .\labo.ps1 nouveau projet-01-reprise, and you start again from an empty folder.
All the commands in this appendix are typed in a bash terminal (or zsh on macOS), with ./labo.sh …. The Terraform outputs are those of the course machine (Terraform 1.12.2); the ls -la outputs were captured under Git Bash, the permission, owner and date columns will differ on your machine.
git --version answers), VS Code installed with the code command available in the terminal (on macOS: VS Code → Cmd+Shift+P → "Shell Command: Install 'code' command in PATH")../labo.sh answers Permission denied: chmod +x labo.sh, once only../labo.sh etat must display Ressources encore gérées : 0 (0 attendu à la fin d'une séance).Follow the official page https://developer.hashicorp.com/terraform/install for your system. In summary:
apt with the GPG key, commands copied from the official page), then sudo apt-get install terraform.brew tap hashicorp/tap then brew install hashicorp/tap/terraform..zip, unzip, then sudo mv terraform /usr/local/bin/.winget), Git Bash sees it in the PATH.Close the terminal, reopen it, then:
terraform versionCheckpoint: Terraform v1.12.2 (or newer) followed by on linux_amd64, on darwin_arm64 or on windows_amd64 depending on your machine. Anything 1.6 or later is fine.
If you see something else: bash: terraform: command not found → the binary is not in a PATH folder (echo $PATH), or the terminal was not reopened. Appendix C.
Project 01 does not use AWS; this step prepares Project 03. You can do it now or postpone it.
Follow https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html for your system (.zip package with ./aws/install on Linux, .pkg on macOS). Then:
aws --versionCheckpoint: a version starting with aws-cli/2. If you see aws-cli/1, that is the old version: uninstall it.
Configure the keys created in the section The AWS console, in the browser:
aws configureAWS 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]: jsonCheck that the CLI does talk to AWS:
aws sts get-caller-identityExample answer (the values are those of your account):
{
"UserId": "AIDA...",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/terraform-admin"
}AWS Academy or temporary sandbox case: open the lab, click AWS Details or Credentials, copy Access Key, Secret Key and Session Token (mandatory with temporary access), then:
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-identityGo to the folder where you keep your projects (for example ~/projets), then:
git clone https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr.git lab-terraform
cd lab-terraform
lsCheckpoint: ls lists labo.ps1 labo.sh projets README.md (and .gitattributes, .gitignore with ls -a). Then:
./labo.sh prerequisOK 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.(Output captured under Git Bash, hence the git 2.49.0.windows.1; on Linux you will read your own Git version.) The four required tools are terraform, aws, git and code. gh, az and gcloud are optional and can be ABSENT without consequence before Module 3. If aws is ABSENT because you postponed B.2, the last line says 3 outils requis présents sur 4 (3 required tools present out of 4): you can still continue this project.
./labo.sh nouveau projet-01-localDossier travail/projet-01-local créé (ignoré par Git). Tapez :
cd travail/projet-01-localDo what the script tells you (the folder was created, ignored by Git; type cd travail/projet-01-local), then open the folder in VS Code:
cd travail/projet-01-local
code .The travail/ folder is ignored by Git: you can write in it freely without dirtying the kit. The solution stays in projets/01-terraform-local/.
If code . does nothing: open VS Code by hand, File → Open Folder, and choose lab-terraform/travail/projet-01-local.
main.tfIn VS Code: New File icon in the left explorer, name the file main.tf, paste the code from the section The code: file by file, save with Ctrl+S (Cmd+S on macOS).
Third path, without an editor, if you prefer the terminal:
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(The quotes around 'EOF' prevent bash from interpreting ${path.module}: without them, the filename line would be emptied.) Check:
cat main.tf
ls -latotal 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.tfCheckpoint: the fifteen lines of the file, and a single entry in the folder, main.tf, of about 286 bytes.
Run T2 to T6 from the section Terraform, in the terminal:
terraform init
terraform fmt
terraform validate
terraform plan
terraform applyAnswer yes to the apply question. Checkpoints, in order: Terraform has been successfully initialized!, nothing for 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.
Check what appeared in the folder:
ls -latotal 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 also displays hidden entries, those whose name starts with a dot; -l gives the detail.) Five entries in addition to . and ..:
| Entry | Who created it | What it is |
|---|---|---|
.terraform | init | Hidden folder, the downloaded provider. Never in Git. |
.terraform.lock.hcl | init | The chosen provider versions. In Git. |
main.tf | you | The code. |
message.txt | apply | The resource. 50 bytes: the text, without a trailing newline. |
terraform.tfstate | apply | The state, the record. Never in Git, never shared. |
Read the created file:
cat message.txtBonjour, ce fichier a été créé avec Terraform.(The file has no trailing newline: your prompt may appear glued to the end of the sentence. That is normal.)
Run T7, T8 and T9:
terraform state list
terraform state show local_file.message
terraform outputCheckpoints: local_file.message; the full block with filename = "./message.txt" and a forty-character id; Warning: No outputs found.
If you are curious, look at the state itself, without modifying it:
cat terraform.tfstateJSON with "serial", "terraform_version": "1.12.2" and a "resources" list containing local_file.message. Close it. It is never edited by hand.
In VS Code, replace the content line of main.tf with:
content = "Deuxième version du fichier créée avec Terraform."Save, then run T10 and T11:
terraform plan
terraform applyAnswer yes. Checkpoints: # local_file.message must be replaced, # forces replacement at the end of the content line, Plan: 1 to add, 0 to change, 1 to destroy., then Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
cat message.txtDeuxième version du fichier créée avec Terraform.Run T12 and T13:
terraform destroy
terraform state listAnswer yes. Checkpoints: Destroy complete! Resources: 1 destroyed., then an empty output for state list.
ls -latotal 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.backupmessage.txt has disappeared. terraform.tfstate went from 1667 to 181 bytes: it no longer contains any resource; terraform.tfstate.backup (1667 bytes) is the copy from before the destroy. .terraform and .terraform.lock.hcl remain: that is normal, they will be useful if you rerun apply.
cat terraform.tfstate{
"version": 4,
"terraform_version": "1.12.2",
"serial": 3,
"lineage": "9ddb3372-803f-0964-7665-8413b8f65ee1",
"outputs": {},
"resources": [],
"check_results": null
}"resources": []: the record is empty. Your lineage (the unique identifier of this state) will be different.
Go back up to the root of the kit and run etat:
cd ../..
./labo.sh etattravail projet-01-local : aucune ressource
Ressources encore gérées : 0 (0 attendu à la fin d'une séance).(No resource in projet-01-local; resources still managed: 0, 0 expected at the end of a session.) If you had forgotten the destroy, the output would say travail projet-01-local : 1 ressource dans le state then Ressources encore gérées : 1 (0 attendu à la fin d'une séance).: this is the guardrail you will run at the end of every session, once resources are billed.
To start over from scratch, rather than cleaning the folder: ./labo.sh nouveau projet-01-reprise, and you start again from an empty folder.
Each case: the exact message → the cause → the fix.
terraform plan answers Error: Inconsistent dependency lock file … required by this configuration but no version is selected → You did not run terraform init in this folder (or you added a provider since). The message ends with the solution: terraform init, then run again.
terraform validate answers Error: Missing required provider … You may be able to install it automatically by running: terraform init → Same cause, same fix: terraform init.
Error: Unsupported argument … An argument named "contenu" is not expected here. Did you mean "content"? → Typo in an argument name. Terraform often suggests the right word. Fix, save again, rerun validate.
Error: Unclosed configuration block … There is no closing brace for this block before the end of the file. → A } brace is missing. Count them: each { has its }. In main.tf, there are three nested pairs for the terraform block, one for provider, one for resource.
Error: Invalid multi-line string then Error: Unterminated template string … No closing marker was found for the string. → A " quote is missing at the end of a value. The faulty line is quoted (3: content = "Bonjour Terraform).
Error: Invalid resource type … The provider hashicorp/local does not support resource type "local_fichier". → The resource type does not exist in this provider. The local provider's types are called local_file and local_sensitive_file; the provider documentation on the Registry lists them.
Error: Invalid Attribute Combination … No attribute specified when one (and only one) of [content,sensitive_content,content_base64] is required (repeated four times) → You removed the content line: a local_file must say what it contains. Put it back.
terraform validate says Success! but terraform plan answers Error: No configuration files … create a Terraform configuration file (.tf file) and try again. → There is no .tf file in the folder: either you are not in travail/projet-01-local (check with pwd), or the file is called main.tf.txt. Rename it to main.tf. An empty folder is a valid (empty) configuration, hence the misleading Success!.
terraform apply answers Apply cancelled. and nothing is created → You typed something other than yes (for example y, Y, oui, or Enter alone). Run again and type yes in full.
terraform state list answers No state file was found! → No apply has been done yet in this folder, or you are not in the right folder. It is not a failure: there is nothing to list yet.
terraform state show answers No instance found for the given address! → The address is mistyped, or the resource was destroyed. terraform state list gives the exact addresses.
terraform fmt -check exits with an error (code 3) displaying main.tf → The file is not in the official format (indentation, alignment of =). It is not a syntax error: terraform fmt without -check fixes it.
After destroy, terraform.tfstate is still there and you think the destroy failed → No: open it, "resources": []. The file remains, empty, with a terraform.tfstate.backup next to it. Destroy complete! Resources: 1 destroyed. is authoritative.
.\labo.ps1 etat or ./labo.sh etat displays Ressources encore gérées : 1 → A destroy was forgotten in one of the folders listed above (travail projet-01-local : 1 ressource dans le state). Go into that folder, terraform destroy, yes, rerun etat.
Windows only — terraform : Le terme «terraform» n'est pas reconnu comme nom d'applet de commande, fonction, fichier de script ou programme exécutable. (the term is not recognized as a cmdlet, function, script file or executable program) → Terraform is not in the PATH, or PowerShell was not restarted after the installation. Close all PowerShell windows, reopen, retype terraform version. If it persists, Appendix A.1 (adding C:\terraform to the PATH).
Windows only — aws : Le terme «aws» n'est pas reconnu… → AWS CLI is not installed or PowerShell was not restarted. Reinstall AWS CLI version 2 (A.2), close and reopen PowerShell, retype aws --version.
Windows only — .\labo.ps1 is refused: "running scripts is disabled on this system" → Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, answer Y (O on a French system), run again. Once per machine.
Windows only — the file is called main.tf.txt → Notepad adds .txt. In PowerShell: Rename-Item main.tf.txt main.tf. Use VS Code to create the files.
Native Linux and macOS — bash: terraform: command not found → The binary is not in a PATH folder. sudo mv terraform /usr/local/bin/, or check the package installation (B.1). Reopen the terminal.
macOS and bash — ./labo.sh answers Permission denied → chmod +x labo.sh, once only. If bash: ./labo.sh: /bin/bash^M: bad interpreter, the file has Windows line endings: git config core.autocrlf input then re-clone, or sed -i 's/\r$//' labo.sh (sed -i '' … on macOS).
aws sts get-caller-identity answers AccessDenied or InvalidClientTokenId → The configured key is not the right one, or the Academy session token has expired. aws configure with the terraform-admin key (check that the user is in terraform-admins with AdministratorAccess), or redo aws configure set … with new Academy credentials. No effect on Project 01, which does not call AWS.
A compressed folder or a Git repository containing:
main.tf (the final file, with the second version of the content);sorties.txt: the full output of terraform plan (first version), of terraform state list after apply, of the modification terraform plan (the one with must be replaced), and of terraform state list after destroy (empty);README.md following the template below.Hand in neither the .terraform/ folder, nor terraform.tfstate, nor any Access Key.
# Project 01 — Terraform local
- Terraform: (output of `terraform version`)
- System: Windows / Linux / macOS
## What I did
init, fmt, validate, plan, apply, state list, state show, content modification, plan, apply, destroy, state list.
## The terraform.tfstate file, in three lines of my own
(What it is for. Why Terraform needs it for destroy. Why you do not edit it and do not share it.)
## What blocked me, and how I got unblocked
(One line, or "nothing".)init, fmt, validate, plan, apply, state, destroy): https://developer.hashicorp.com/terraform/cli/commandshashicorp/local, resource local_file: https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/fileAdministratorAccess: https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AdministratorAccess.htmlSentence to remember: Terraform does not click in the console for you. It reads your code, compares it to its state and to reality, announces what is going to change, then creates, replaces or destroys to reach the requested state.