Où j'en suis
Dans l'article précédent, je racontais pourquoi je me lance dans le DevOps. J'ai créé mon compte AWS, compris les bases du cloud, et j'ai dit que la prochaine étape serait Terraform.
On y est. Et comme prévu, ça n'a pas été un long fleuve tranquille.
Installer Terraform sous Windows (avec Git Bash)
Je travaille sous Windows au quotidien, mais avec Git Bash comme terminal — ça me donne un environnement proche de Linux. J'adore éditer les fichier avec vi. Voilà ce que j'ai fait :
- Téléchargé Terraform depuis le site officiel HashiCorp
- Extrait le
terraform.exedansC:\tools\terraform\ - Ajouté ce dossier au PATH Windows
Même chose pour AWS CLI — version MSI installer, installation classique.
Vérification dans Git Bash :
$ terraform --version
Terraform v1.14.8
on windows_amd64
$ aws --version
aws-cli/2.34.32 Python/3.14.4 Windows/11 exe/AMD64
Ça roule.
Connecter Terraform à mon compte AWS
Ici j'ai appris un truc important : on n'utilise jamais son compte root pour Terraform. La bonne pratique c'est de créer un utilisateur IAM dédié avec des droits limités.
Dans la console AWS → IAM → Users → Create user → j'ai créé terraform-admin avec la policy AdministratorAccess. Ensuite je lui ai généré des clés d'accès (Access Key ID + Secret Access Key).
C'est comme sudo en Linux : tu ne bosses pas en root, tu as un user normal avec les droits nécessaires.
Puis :
aws configure
# Access Key ID : ...
# Secret Access Key : ...
# Default region : eu-west-3
# Default output format : json
Test :
$ aws sts get-caller-identity
{
"UserId": "AIDA...",
"Account": "***",
"Arn": "arn:aws:iam::***:user/terraform-admin"
}
Parfait, Terraform peut maintenant parler à AWS en mon nom.
Mon premier fichier main.tf
Voici ce que j'ai écrit (avec plein de questions en tête sur chaque ligne) :
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "eu-west-3"
}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
}
resource "aws_instance" "mon_premier_serveur" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Name = "lecon01-premier-serveur"
}
}
Les questions que je me suis posées
"C'est quoi cet AMI ID ?" Une AMI (Amazon Machine Image) c'est l'équivalent d'un ISO pour monter une VM. Sauf qu'AWS en a des milliers pré-faits et prêts à démarrer. Chaque AMI a un identifiant unique par région.
"Pourquoi owners = ["099720109477"] ?" Ce numéro, c'est l'identifiant du compte AWS de Canonical, l'éditeur d'Ubuntu. N'importe qui peut publier une AMI avec un nom qui ressemble à Ubuntu — donc filtrer par owner, c'est la garantie que l'image vient bien de la source officielle. Sécurité de base.
"Pourquoi passer par un bloc data au lieu de mettre l'AMI ID en dur ?"
Là j'ai dû m'arrêter et comprendre. En fait il y a deux concepts différents en Terraform :
-
resource→ "je veux que Terraform CRÉE ça pour moi" (une EC2, un VPC, un bucket S3...) -
data→ "je veux LIRE une info qui existe déjà quelque part" (l'AMI publiée par Canonical, un VPC créé par un collègue, etc.) Si je mets un AMI ID en dur dans mon code, je suis coincé avec cette version précise. Dans 3 mois, Canonical publie une mise à jour de sécurité d'Ubuntu : nouvelle AMI avec un nouvel ID. L'ancienne existe encore (AWS la garde pour ne pas casser les déploiements), mais elle ne reçoit plus de patches.
Pour bien visualiser le problème, voilà un exemple concret :
Version 1 — avec l'ID en dur :
resource "aws_instance" "serveur" {
ami = "ami-025ddada2a5392251" # Ubuntu 24.04 du 9 avril
instance_type = "t3.micro"
}
Trois mois plus tard, Canonical publie ami-abc123fresh456 qui contient les derniers correctifs de sécurité. Mon code continue de déployer l'ancienne AMI. Pour mettre à jour :
- Quelqu'un doit aller chercher manuellement le nouvel ID
- Modifier le code
- Faire une PR, la faire valider, la merger
- Et si personne n'y pense, ça reste vulnérable pendant des mois
Version 2 — avec un bloc
data:
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
}
resource "aws_instance" "serveur" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
}
À chaque terraform apply, Terraform va demander à AWS : "donne-moi la plus récente AMI Ubuntu 24.04 signée Canonical" et l'utilise. Aucun travail humain nécessaire.
Nuance que j'ai découverte : en production, ce comportement peut poser problème — si une nouvelle AMI sort entre deux déploiements, Terraform peut vouloir recréer l'instance (et donc avoir un downtime). Il y a un arbitrage à faire entre "toujours à jour" et "reproductibilité". Pour mon apprentissage,
most_recent = truefait très bien l'affaire.
"Et le ~> 5.0 ?"
Terraform utilise le versionnement sémantique — chaque version a 3 chiffres : MAJEUR.MINEUR.PATCH. Par exemple 5.100.0.
- Un changement MAJEUR (5 → 6) peut casser le code existant
- Un changement MINEUR (5.99 → 5.100) ajoute des fonctionnalités sans casser
- Un PATCH (5.100.0 → 5.100.1) corrige juste des bugs
Le
~>est un opérateur spécial qui dit : "accepte les mises à jour compatibles, mais bloque les majeures".
| Syntaxe | Ce que ça accepte |
|---|---|
= 5.100.0 |
Exactement cette version |
~> 5.0 |
5.x uniquement (5.0 jusqu'à 5.999, mais pas 6.0) |
~> 5.100 |
5.100.x uniquement (bloque aussi 5.101) |
>= 5.0 |
Tout à partir de 5.0 (y compris 6.x, 7.x...) |
Un exemple réel qui m'a fait comprendre l'importance :
Entre la v4 et la v5 du provider AWS, HashiCorp a supprimé l'argument s3_force_path_style des buckets S3 (remplacé par s3_use_path_style dans le bloc provider). Imagine que tu avais ce code qui tournait nickel en v4 :
provider "aws" {
region = "eu-west-3"
s3_force_path_style = true # Valide en v4
}
Sans contrainte de version, tu fais un terraform init un matin, il télécharge la v5 toute fraîche, et ton apply plante avec :
Error: Unsupported argument
on main.tf line 4, in provider "aws":
4: s3_force_path_style = true
An argument named "s3_force_path_style" is not expected here.
Ton infra de prod s'arrête. Tu passes 2h à comprendre, à migrer ton code, et tu te fais engueuler.
Avec ~> 4.0 dans ton required_providers, Terraform refuse de télécharger la v5 et reste sur la v4.x. Tu as le temps de planifier la migration tranquillement.
La double protection réelle c'est : ~> 5.0 dans le main.tf (la plage acceptable) + le .terraform.lock.hcl qui verrouille la version exacte pour que tous les devs de l'équipe utilisent la même.
La séquence des 3 commandes
terraform init
$ terraform init
Initializing provider plugins...
- Installing hashicorp/aws v5.100.0...
Terraform has been successfully initialized!
Terraform a téléchargé le provider AWS (une sorte de plugin qui sait parler à AWS). Il a aussi créé un fichier .terraform.lock.hcl qui verrouille la version exacte du plugin — pour que mon code marche pareil même si AWS publie une nouvelle version demain. Exactement le même principe qu'un package-lock.json ou un gradle.lock.
Et oui c'est important pour que tous les développeurs puisse avoir la même version du plugin change, les développeurs travailleront tous sur la même version inscrite dans le fichier lock
terraform plan
Ça, c'est la commande qui m'a rassuré. Elle me montre ce qui va se passer avant de faire quoi que ce soit :
Plan: 1 to add, 0 to change, 0 to destroy.
Un truc à créer, rien à modifier, rien à supprimer. Le + create devant chaque ligne = ça va créer. Et plein de (known after apply) — parce que des valeurs comme l'IP publique du serveur n'existent pas encore. Toutes ces choses ne seront connus que lorsque l'instance sera créé.
terraform apply
Le moment de vérité. Je tape yes, j'appuie sur Entrée.
Et là... ERREUR.
Mon premier mur : le Free Tier qui a changé
Error: creating EC2 Instance: InvalidParameterCombination:
The specified instance type is not eligible for Free Tier.
J'avais mis t2.micro — parce que c'est ce qu'on voit partout dans les tutos. Sauf qu'AWS a fait évoluer son Free Tier, et dans certaines régions (dont Paris), c'est t3.micro qui est gratuit maintenant.
Comment j'ai trouvé le bon ? AWS CLI a la réponse :
$ aws ec2 describe-instance-types \
--filters "Name=free-tier-eligible,Values=true" \
--query "InstanceTypes[].InstanceType" --output text
t4g.small c7i-flex.large t3.micro t4g.micro t3.small ...
En fait le t3.micro c'est mieux — processeurs plus récents, 2 vCPU au lieu de 1, et même prix. AWS pousse la migration depuis 2018.
Donc t3.micro. Je modifie le main.tf, je relance terraform apply, et :
aws_instance.mon_premier_serveur: Creating...
aws_instance.mon_premier_serveur: Still creating... [00m10s elapsed]
aws_instance.mon_premier_serveur: Creation complete after 13s
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
13 secondes. Un serveur Ubuntu 24.04 dans un datacenter à Paris, provisionné en 13 secondes à partir d'un fichier texte. Comparé aux délais habituels pour avoir un serveur au boulot, c'est assez fou.
Ma ceinture de sécurité : le budget alert
Premier réflexe d'un DevOps : ne jamais faire confiance à sa propre mémoire pour éteindre des ressources. J'ai configuré une alerte budget :
- Console AWS → Billing → Budgets → Create budget
- Template "Zero spend budget" (alerte dès 0.01$)
- Email sur mon adresse perso À partir de maintenant, si ma facture commence à grimper pour n'importe quelle raison, je reçois un mail. Ça me laisse le temps d'intervenir avant que ça devienne sérieux.
À savoir : AWS peut mettre jusqu'à 24h à détecter une dépense. L'alerte budget est une sécurité, pas une excuse pour laisser traîner des ressources.
Le réflexe de fin : terraform destroy
Avant de fermer mon terminal, une dernière commande cruciale :
terraform destroy
Pourquoi ? Parce que même si le serveur est dans le Free Tier, il vaut mieux prendre l'habitude de ne pas laisser traîner des ressources. Un terraform destroy et tout ce qui a été créé est supprimé.
C'est d'ailleurs l'un des gros avantages du Infrastructure as Code : le cycle "je crée / je teste / je détruis" devient trivial. Chez moi avant, créer un environnement de test prenait une journée. Ici c'est 13 secondes pour créer, 30 pour détruire.
Ce que j'ai appris aujourd'hui
- Installer Terraform et AWS CLI sur Windows via Git Bash
- Configurer un utilisateur IAM dédié (jamais utiliser le compte root)
- Écrire un premier fichier
main.tfavec provider, data source et resource - Comprendre
terraform init,plan,apply,destroy - Utiliser
data "aws_ami"pour ne pas hardcoder des IDs qui vont vieillir - Gérer ma première erreur Terraform (le Free Tier qui évolue)
- Utiliser
aws ec2 describe-instance-typespour trouver l'info directement via l'API
Prochaine étape
Dans le prochain article : comprendre les réseaux AWS (VPC, subnets, security groups). Parce que créer un serveur, c'est bien. Pouvoir s'y connecter et lui donner un rôle dans une architecture, c'est mieux.
Si tu es dans la même aventure que moi — développeur qui bascule vers le DevOps — n'hésite pas à suivre la série. Et si tu as des retours ou des conseils, je suis preneur en commentaires.
Top comments (0)