Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

đŸ—ïž Terraform – Infrastructure as Code (IaC) & Cloud

Guide complet IDEO‑Lab sur l'outil IaC (Core, HCL, State, Providers, Modules).

1.1 Facile

Concept : IaC

Infrastructure as Code. Déclaratif (Terraform) vs Impératif (Ansible).

IaC Déclaratif
1.2 Facile

Architecture

Core (CLI), Providers (AWS, GCP, ...), State File.

Core Providers
1.3 Facile

Installation

tfenv (gestionnaire de versions, reco) ou binaire manuel.

tfenv Install
1.4 Facile

Le Workflow Essentiel

init (Initier), plan (Prévoir), apply (Appliquer), destroy.

init plan apply
2.1 Moyen

HCL : Syntaxe (.tf)

provider, resource, data, variable, output.

HCL resource
2.2 Moyen

Fichier d'État Local (.tfstate)

Le "cerveau" de Terraform. .tfstate, .gitignore.

.tfstate State
2.3 Avancé

État distant (Backend)

backend "s3", S3 (HA) + DynamoDB (Locking). Indispensable en équipe.

Backend S3 Locking
3.1 Moyen

Providers (AWS, GCP, ...)

Configuration des "plugins" (AWS, GCP, Azure, Docker...).

provider AWS GCP
3.2 Moyen

Variables (Input)

variable {}, terraform.tfvars, -var, TF_VAR_.

variable .tfvars
3.3 Facile

Outputs (Sorties)

output {} (ex: aws_instance.web.public_ip).

output value
4.1 Moyen

Exemple: Instance AWS (EC2)

aws_instance, aws_security_group (Firewall HTTP/SSH).

EC2 AWS
4.2 Moyen

Exemple: Instance GCP (GCE)

google_compute_instance, google_compute_firewall.

GCE GCP
4.3 Avancé

Modules (Réutilisabilité)

module {}, source (Registry, Git), variables.tf.

Module DRY
4.4 Avancé

Data Sources (Lecture)

data "aws_vpc" ..., lire l'infra existante.

data Import
4.5 Avancé

Provisioners (À Ă©viter)

remote-exec (post-install), l'anti-pattern (utiliser Ansible).

provisioner remote-exec
5.1 Facile

Cheat-sheet (CLI & State)

init, plan, apply, fmt, validate, state list.

cheat CLI
1.1 Concept : Infrastructure as Code (IaC)
Infrastructure as Code (IaC)

L'IaC est la pratique de gestion et de provisionnement de l'infrastructure (serveurs, BDD, réseaux, DNS...) via des **fichiers de configuration lisibles par l'homme** (ex: HCL, YAML), plutÎt que par une configuration manuelle (clics dans la console AWS/GCP) ou des scripts impératifs.

Avantages :

  • ReproductibilitĂ© : Vous pouvez dĂ©truire et recrĂ©er votre infra 100 fois Ă  l'identique.
  • Versionning : Votre infra est dans Git. Vous pouvez voir qui a changĂ© quoi (git blame).
  • CI/CD : L'infra est dĂ©ployĂ©e automatiquement (ex: "plan" sur une Pull Request).
Déclaratif (Terraform) vs. Impératif (Ansible)

C'est la distinction la plus importante.

ModĂšleAnalogie (Le "Quoi")Analogie (Le "Comment")Outil
DĂ©claratif (État)"Je veux 3 serveurs Nginx (Instance A, B, C)."(Terraform compare l'Ă©tat actuel et l'Ă©tat dĂ©sirĂ©. Il voit que B manque, il le crĂ©e.)Terraform, Kubernetes, Pulumi
Impératif (Action)"Crée le serveur A. Crée le serveur B. Crée le serveur C."(Ansible exécute les 3 commandes. S'il est relancé, il essaie de les re-créer (sauf si state=present est géré).)Ansible, Chef, Puppet, Scripts Bash

Conclusion : Terraform est pour **provisionner** l'infrastructure (le "matériel"). Ansible est pour **configurer** l'infrastructure (le "logiciel"). (Ils sont complémentaires !)

1.2 Architecture Terraform
Les 3 Composants Clés
  1. 1. Terraform Core (CLI) : Le binaire (terraform) écrit en Go. Il lit les fichiers .tf, construit le graphe de dépendances, et exécute le "plan".
  2. 2. Providers (Plugins) : Les "traducteurs". Ce sont des binaires (plugins) qui font le lien entre HCL (le langage) et l'API du service cible (AWS, GCP, Cloudflare, Docker...).
  3. 3. State File (Fichier d'État) : Le "cerveau". Un fichier JSON (terraform.tfstate) qui stocke l'Ă©tat *actuel* de l'infrastructure gĂ©rĂ©e. (Voir 2.2 & 2.3).
Schéma de flux (terraform apply)
+-----------------------+
| Fichiers .tf (HCL)    |
| (État dĂ©sirĂ©)         |
+-----------------------+
        |
        ▌
+-----------------------+
| Terraform Core (CLI)  |
| (GénÚre le "Plan")    |
+-----------------------+
        |           |
 (Compare)          | (Appelle)
        |           ▌
        |     +--------------+
        |     | Provider AWS | (Plugin)
        |     +--------------+
        |           |
        |           ▌ (Appels API)
        |     [Infrastructure Cloud (AWS)]
        |
        ▌
+-----------------------+
| State File (.tfstate) |
| (État actuel)         |
+-----------------------+
1.3 Installation (tfenv ou Manuelle)
tfenv (Gestionnaire de versions)

ProblÚme : Les projets Terraform sont sensibles à la version (ex: 1.5 vs 1.6). tfenv (comme nvm pour Node) permet de gérer plusieurs versions.

# 1. Installer tfenv (ex: macOS)
brew install tfenv

# (ex: Linux)
git clone https://github.com/tfutils/tfenv.git ~/.tfenv
# (Ajouter ~/.tfenv/bin au PATH)

# 2. Installer une version de Terraform
tfenv install 1.8.0
tfenv install latest

# 3. Utiliser une version (globale)
tfenv use 1.8.0

# (Utiliser par projet)
# (Crée un .terraform-version à la racine du projet)
tfenv use 1.8.0
Installation Manuelle (Binaire)

Terraform est un binaire Go unique.

# (Adapté pour Linux)
# 1. Télécharger (HashiCorp)
VERSION="1.8.0"
wget https://releases.hashicorp.com/terraform/${VERSION}/terraform_${VERSION}_linux_amd64.zip

# 2. Dézipper
unzip terraform_${VERSION}_linux_amd64.zip

# 3. Déplacer dans le PATH
sudo mv terraform /usr/local/bin/

# 4. Vérifier
terraform --version
1.4 Le Workflow Essentiel (init, plan, apply)
terraform init

À lancer une fois par projet (ou si les providers changent).
Action : Lit les provider {} et module {} dans vos fichiers .tf et télécharge les plugins nécessaires (ex: provider-aws) dans un dossier .terraform/.

$ terraform init
Initializing the backend...
Initializing provider plugins...
terraform plan

(Recommandé) Un "dry-run" (simulation).
Action : Compare votre code (.tf) à l'état (.tfstate) et aux ressources réelles (API Cloud). Il vous montre ce qu'il *va* faire (Créer, Modifier, Détruire).

$ terraform plan
...
Plan: 1 to add, 0 to change, 0 to destroy.

+ create (A green '+' = Création)
terraform apply

Action : Exécute le plan. (Il vous montre le "plan" une derniÚre fois et demande confirmation "yes").

$ terraform apply
...
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions? [yes/no] yes

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

(Dangereux) L'inverse d'apply.
Action : Lit l'état (.tfstate) et détruit toutes les ressources qu'il gÚre.

$ terraform destroy
Plan: 0 to add, 0 to change, 1 to destroy.
...
Destroy complete! Resources: 1 destroyed.
2.1 HCL : Syntaxe de base (.tf)

HCL (HashiCorp Configuration Language) est le langage de Terraform. Il est déclaratif.

# main.tf

# 1. Définir le provider (plugin) requis
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# 2. Configurer le provider
provider "aws" {
  region = "eu-west-3"
}

# 3. Définir une Variable (Input)
variable "instance_type" {
  description = "Taille de l'instance EC2"
  type        = string
  default     = "t2.micro"
}

# 4. Définir une Ressource (L'infra !)
# resource "[TYPE]" "[NOM_LOCAL]"
resource "aws_instance" "web_server" {
  ami           = "ami-0abcdef123"
  instance_type = var.instance_type # Référence à la variable
  
  tags = {
    Name = "Serveur Web (Terraform)"
  }
}

# 5. Définir une Sortie (Output)
output "instance_ip" {
  description = "IP Publique de l'instance"
  value       = aws_instance.web_server.public_ip
}
2.2 Fichier d'État (.tfstate) (Crucial)
Le "Cerveau" de Terraform

Le fichier terraform.tfstate est un JSON généré par terraform apply. C'est la partie la plus importante (et la plus dangereuse) de Terraform.

RÎle : Il agit comme une "base de données" qui mappe votre code (resource "aws_instance" "web") à l'ID de la ressource réelle ("i-123abc456").

Quand vous lancez plan, Terraform compare .tf (désiré) à .tfstate (actuel) pour savoir quoi faire (créer, modifier, détruire).

.gitignore (CRITIQUE)

⚠ Le .tfstate local contient des secrets (mots de passe BDD, clĂ©s API...) en CLAIR.

Il ne doit **JAMAIS** ĂȘtre commitĂ© (push) sur Git.

# .gitignore
*.tfstate
*.tfstate.backup
.terraform/
terraform.tfvars

ProblÚme : Si vous le perdez, Terraform "oublie" tout ce qu'il a créé. Si vous travaillez en équipe, comment partager l'état ? (Voir 2.3).

2.3 État distant (Backend) & Locking (Prod)
Backend (État distant)

(Indispensable pour les équipes) Le "backend" configure Terraform pour qu'il stocke le .tfstate non pas localement, mais dans un stockage distant (ex: S3, GCP Bucket, Azure Blob).

Avantages :

  • Partage : L'Ă©quipe partage le mĂȘme Ă©tat.
  • SĂ©curitĂ© : Les secrets sont chiffrĂ©s (ex: S3 Encryption).

# main.tf
terraform {
  backend "s3" {
    # 1. Bucket S3 oĂč stocker le .tfstate
    bucket = "mon-bucket-terraform-state-ideo"
    # 2. Chemin du fichier
    key    = "production/terraform.tfstate"
    # 3. Région du bucket
    region = "eu-west-3"
  }
}
# (AprĂšs ajout, relancer 'terraform init')
State Locking (Verrouillage)

ProblĂšme : Que se passe-t-il si 2 dĂ©veloppeurs lancent terraform apply en mĂȘme temps ? (Conflit, corruption de l'Ă©tat).

Solution : Le "Locking". Avant de faire apply, Terraform "verrouille" l'état. Si un 2Úme apply est lancé, il échoue.

Pour AWS (S3), le locking se fait via une table DynamoDB.

# main.tf
terraform {
  backend "s3" {
    bucket = "mon-bucket-terraform-state-ideo"
    key    = "production/terraform.tfstate"
    region = "eu-west-3"
    
    # 4. (Verrouillage) Nom de la table DynamoDB
    dynamodb_table = "terraform-lock-table"
  }
}
# (La table DynamoDB doit ĂȘtre créée au prĂ©alable)
3.1 Providers (AWS, GCP, Azure)
AWS (Amazon Web Services)

Terraform utilise les credentials standards (~/.aws/credentials, ou variables d'env AWS_ACCESS_KEY_ID).

# main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "eu-west-3" // Paris
  
  default_tags {
    tags = {
      Owner   = "IDEO-Lab"
      Project = "Terraform Guide"
    }
  }
}
GCP (Google Cloud Platform)

Terraform utilise un "Service Account Key File" (JSON).

# main.tf
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

provider "google" {
  project = "ideo-lab-project-id"
  region  = "europe-west1"
  zone    = "europe-west1-b"
  
  credentials = file("/path/to/service-account.json")
}
3.2 Variables (Input)
variables.tf (Déclaration)

On déclare les "inputs" du projet (ex: ce qui change entre 'dev' et 'prod').

variable "env" {
  description = "Environnement (dev, staging, prod)"
  type        = string
  default     = "dev"
}

variable "instance_type" {
  description = "Taille de l'instance EC2"
  type        = string
  default     = "t2.micro"
}

variable "ami_id" {
  description = "AMI Ă  utiliser (ex: Ubuntu)"
  type        = string
  # (Pas de 'default' = Requis)
}

variable "tags" {
  description = "Tags Ă  appliquer"
  type        = map(string)
  default     = {}
}
Assignation (Priorité)

Comment Terraform assigne les valeurs (du plus bas au plus haut) :

  1. (Bas) default: (dans variable {})
  2. *.auto.tfvars (Fichiers chargés auto)
  3. terraform.tfvars (Fichier de dev local)
  4. -var="ami_id=ami-123" (Flag CLI)
  5. (Haut) TF_VAR_ami_id=ami-123 (Variable d'env)
terraform.tfvars (Dev local)

(Fichier Ă  ignorer dans .gitignore)

# terraform.tfvars
# (Assignations pour 'ami_id' (requis))

ami_id        = "ami-0abcdef123"
instance_type = "t3.small"
3.3 Outputs (Sorties)

Les "Outputs" (Sorties) affichent des informations utiles (ex: l'IP du serveur) à la fin d'un apply. Elles sont aussi utilisées pour lire les données d'un "Module" (voir 4.3).

outputs.tf
output "instance_ip" {
  description = "IP Publique de l'instance web"
  # (Référence à la ressource "aws_instance" nommée "web_server")
  value       = aws_instance.web_server.public_ip
}

output "instance_id" {
  description = "ID de l'instance"
  value       = aws_instance.web_server.id
}
Résultat (Fin de terraform apply)
Apply complete!

Outputs:

instance_id = "i-1234567890abcdef0"
instance_ip = "54.123.45.67"
4.1 Exemple : Serveur Django (AWS EC2)

Crée un "Security Group" (Firewall) et une Instance (VM) Ubuntu pour un serveur Django/Nginx.

# --- 1. Security Group (Firewall) ---
resource "aws_security_group" "django_sg" {
  name        = "django-sg"
  description = "Autorise HTTP(S) et SSH"

  # (Entrée) Port 22 (SSH) (depuis votre IP)
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["80.1.2.3/32"] # (Votre IP)
  }
  
  # (Entrée) Port 80 (HTTP)
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  # (Sortie) Tout autorisé
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# --- 2. Instance EC2 (VM) ---
resource "aws_instance" "django_server" {
  ami           = "ami-0fc5d9351a0b0b8e1" # (Ubuntu 22.04 - eu-west-3)
  instance_type = "t3.micro"
  
  # (Lier le Firewall)
  vpc_security_group_ids = [aws_security_group.django_sg.id]
  
  # (Lier la clé SSH (doit exister dans AWS))
  key_name = "ma-cle-ssh-aws"

  tags = {
    Name = "Django Server (Prod)"
  }
}
4.2 Exemple : Serveur Django (GCP GCE)

Crée une rÚgle de Firewall et une Instance (VM) Debian.

# --- 1. Firewall (HTTP/SSH) ---
resource "google_compute_firewall" "django_firewall" {
  name    = "allow-http-ssh"
  network = "default"
  
  allow {
    protocol = "tcp"
    ports    = ["22", "80"]
  }
  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["web-server"]
}

# --- 2. Instance GCE (VM) ---
resource "google_compute_instance" "django_server" {
  name         = "django-server-prod"
  machine_type = "e2-micro"
  zone         = "europe-west1-b"

  # (Appliquer le tag du firewall)
  tags = ["web-server"]
  
  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-11"
    }
  }

  network_interface {
    network = "default"
    access_config {
      # (Donne une IP publique)
    }
  }
  
  metadata = {
    # (Ajoute votre clé SSH à l'instance)
    ssh-keys = "ideo_user:${file("~/.ssh/id_rsa.pub")}"
  }
}
4.3 Modules (Réutilisabilité)
Concept (DRY - Don't Repeat Yourself)

ProblĂšme : Vous avez 10 projets Django. Vous devez copier/coller le code (EC2 + SG) 10 fois. Si vous devez mettre Ă  jour le firewall, vous modifiez 10 fichiers.

Solution : Un **Module** est un "composant" Terraform réutilisable (l'équivalent d'un "Role" Ansible ou d'une "Fonction").

Utilisation (main.tf du projet)

On "appelle" le module (ex: un module VPC) et on lui passe des variables.

# --- 1. Appel d'un module (Registry) ---
module "vpc_prod" {
  # Source: Terraform Registry
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.0.0"
  
  # (Variables du module)
  name = "vpc-prod"
  cidr = "10.0.0.0/16"
}

# --- 2. Appel d'un module (Git/Local) ---
module "serveur_django" {
  source = "./modules/ec2-django"
  # (ou 'git::https://.../tf-modules.git//ec2-django')
  
  env       = "prod"
  ami_id    = "ami-123"
  subnet_id = module.vpc_prod.public_subnets[0]
}
Structure (./modules/ec2-django/)

Un module est un dossier Terraform standard.

modules/ec2-django/
├── main.tf       (Les 'resource "aws_instance"...')
├── variables.tf  (Les 'inputs', ex: var.env)
└── outputs.tf    (Les 'outputs', ex: output "ip" ...)
4.4 Data Sources (Lecture)
data (Lecture de l'existant)

ProblÚme : Votre VPC a été créé manuellement (ou par un autre projet Terraform). Comment obtenir son ID ?

Solution : Utiliser un bloc data. C'est une requĂȘte en "lecture seule" (Read-Only) Ă  l'API du Provider.

Exemple (data)
# (On suppose que le VPC "ideo-vpc" existe déjà)
data "aws_vpc" "existing_vpc" {
  filter {
    name   = "tag:Name"
    values = ["ideo-vpc"]
  }
}

# (On suppose que l'AMI Ubuntu existe)
data "aws_ami" "ubuntu" {
  most_recent = true
  filter {
    name   = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-*"
    values = ["099720109477"] # (ID du Propriétaire Canonical)
  }
}

resource "aws_instance" "web" {
  # Utiliser les données lues
  ami           = data.aws_ami.ubuntu.id
  vpc_id        = data.aws_vpc.existing_vpc.id
  ...
}
4.5 Provisioners (remote-exec) (Anti-Pattern)
Concept (À Ă©viter)

Les "Provisioners" (remote-exec, local-exec) sont une "porte de sortie" pour exécuter des scripts impératifs (ex: Bash) aprÚs la création d'une ressource.

⚠ Anti-Pattern : C'est considĂ©rĂ© comme une mauvaise pratique. Terraform est dĂ©claratif (État). Si le script remote-exec Ă©choue, l'Ă©tat de Terraform est "contaminĂ©" (il ne sait pas ce qui a Ă©tĂ© fait).

La "Bonne" Façon (Terraform + Ansible)

1. Terraform (main.tf) : Provisionne l'infra (EC2, SG) et génÚre un inventaire.

resource "aws_instance" "web" { ... }

output "inventory" {
  value = templatefile("inventory.ini.tpl", {
    ip = aws_instance.web.public_ip
  })
}
# (terraform apply -> inventory.ini)

2. Ansible (playbook.yml) : Configure le logiciel (Nginx, Gunicorn, Django) sur l'IP générée.

ansible-playbook -i inventory.ini deploy.yml
5.1 Cheat-sheet (CLI & State)
Workflow (Essentiel)
# 1. Initialiser (Télécharge les providers)
terraform init

# 2. Formater (Nettoie le code HCL)
terraform fmt

# 3. Valider (Vérifie la syntaxe)
terraform validate

# 4. Planifier (Simulation / Dry-run)
terraform plan
# (Sauver le plan)
terraform plan -out=mon_plan.tfplan

# 5. Appliquer (Créer / Mettre à jour)
terraform apply
terraform apply "mon_plan.tfplan"
# (Appliquer sans demande de confirmation)
terraform apply -auto-approve

# 6. Détruire (Tout supprimer)
terraform destroy
Gestion de l'État (.tfstate)
# Lister les ressources (selon .tfstate)
terraform state list

# Voir une ressource
terraform state show aws_instance.web_server

# (Avancé) Retirer une ressource de l'état
# (Ne détruit pas la ressource, Terraform l'oublie)
terraform state rm aws_instance.web_server

# (Avancé) Importer une ressource (créée manuellement)
# terraform import [ADRESSE_HCL] [ID_CLOUD]
terraform import aws_instance.web_server i-123abc456

# (Avancé) Déplacer une ressource
terraform state mv aws_instance.web aws_instance.old_web