# Fichier: python_cheats/cheatsheets/terraform_ultra_detaille.txt
# Terraform pour Python - Guide Ultra-Détaillé pour Grands Débutants
# Ce guide explique le POURQUOI, le COMMENT et le QUAND de chaque concept


═══════════════════════════════════════════════════════════════════════════════
[DOCS] TABLE DES MATIÈRES
═══════════════════════════════════════════════════════════════════════════════

1. Introduction à Terraform - Concepts Fondamentaux
2. Installation et Configuration Initiale
3. Anatomie d'un Projet Terraform
4. Commandes de Base Expliquées en Détail
5. Variables - La Base de la Flexibilité
6. Ressources - Créer votre Infrastructure
7. Data Sources - Récupérer des Informations
8. Outputs - Exposer des Informations
9. Modules - Réutiliser du Code
10. State Management - Le Cerveau de Terraform
11. Déploiement d'Applications Python
12. Exemples Pratiques Complets
13. Troubleshooting et Bonnes Pratiques


═══════════════════════════════════════════════════════════════════════════════
[GUIDE] PARTIE 1: INTRODUCTION À TERRAFORM - CONCEPTS FONDAMENTAUX
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ QU'EST-CE QUE TERRAFORM?                                                  ║
╚═══════════════════════════════════════════════════════════════════════════╝

Terraform est un outil d'Infrastructure as Code (IaC) créé par HashiCorp.

┌───────────────────────────────────────────────────────────────────────────┐
│ ANALOGIE POUR COMPRENDRE:                                                 │
│                                                                           │
│ Imaginez que vous construisez une maison:                                 │
│ • Sans Terraform: Vous devez aller sur chaque chantier, parler à chaque   │
│   ouvrier, vérifier manuellement que tout est fait correctement.          │
│ • Avec Terraform: Vous écrivez un plan détaillé (code), et Terraform      │
│   s'occupe de coordonner tous les ouvriers et de construire la maison     │
│   exactement comme décrit dans le plan.                                   │
└───────────────────────────────────────────────────────────────────────────┘

POURQUOI utiliser Terraform?
─────────────────────────────

1. REPRODUCTIBILITÉ
   • Vous écrivez le code UNE FOIS
   • Vous pouvez créer la MÊME infrastructure 100 fois
   • Exemple: Créer un environnement de dev identique à la production

2. VERSIONNAGE
   • Votre infrastructure est dans Git
   • Vous pouvez revenir en arrière si quelque chose ne va pas
   • Vous voyez qui a changé quoi et quand

3. AUTOMATISATION
   • Plus besoin de cliquer dans des interfaces web
   • Tout est scripté et automatisé
   • Réduction des erreurs humaines

4. DOCUMENTATION AUTOMATIQUE
   • Votre code Terraform EST votre documentation
   • Toujours à jour (sinon l'infra ne fonctionne pas)

5. COLLABORATION EN ÉQUIPE
   • Plusieurs personnes peuvent travailler sur la même infrastructure
   • Revue de code avant modification (Pull Requests)
   • Planification des changements avant application

6. MULTI-CLOUD
   • Même syntaxe pour AWS, GCP, Azure, etc.
   • Facile de changer de provider ou d'utiliser plusieurs clouds


╔═══════════════════════════════════════════════════════════════════════════╗
║ CONCEPTS CLÉS À COMPRENDRE                                                ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ 1. INFRASTRUCTURE AS CODE (IaC)                                           │
└───────────────────────────────────────────────────────────────────────────┘

DÉFINITION:
L'infrastructure est définie par du code plutôt que par des processus manuels.

AVANT (Approche Manuelle):
┌─────────────────────────────────────────────────────────────────────┐
│ 1. Se connecter à AWS Console                                       │
│ 2. Cliquer sur "EC2" -> "Launch Instance"                           │
│ 3. Choisir Ubuntu 22.04                                            │
│ 4. Sélectionner t2.micro                                           │
│ 5. Configurer le réseau                                            │
│ 6. Ajouter un disque de 20GB                                       │
│ 7. Créer un security group                                         │
│ 8. Lancer l'instance                                               │
│ 9. Répéter tout ça pour chaque environnement (dev, staging, prod) │
│ 10. Documenter tout ça dans un Wiki (souvent oublié ou obsolète)  │
└─────────────────────────────────────────────────────────────────────┘

APRÈS (Avec Terraform):
┌─────────────────────────────────────────────────────────────┐
│ resource "aws_instance" "web" {                             │
│   ami           = "ami-ubuntu-22.04"                        │
│   instance_type = "t2.micro"                                │
│                                                              │
│   root_block_device {                                       │
│     volume_size = 20                                        │
│   }                                                          │
│                                                              │
│   vpc_security_group_ids = [aws_security_group.web.id]    │
│                                                              │
│   tags = {                                                  │
│     Name = "web-server"                                     │
│   }                                                          │
│ }                                                            │
└─────────────────────────────────────────────────────────────┘

AVANTAGES:
[OK] 10 lignes de code vs 10 étapes manuelles
[OK] Réutilisable pour dev, staging, prod
[OK] Versionné dans Git
[OK] Peut être revu par l'équipe
[OK] Auto-documenté


┌───────────────────────────────────────────────────────────────────────────┐
│ 2. DECLARATIVE vs IMPERATIVE                                              │
└───────────────────────────────────────────────────────────────────────────┘

Terraform utilise une approche DÉCLARATIVE.

IMPÉRATIVE (comme un script bash):
┌─────────────────────────────────────────────────────┐
│ "Fais ça, puis ça, puis ça"                         │
│                                                     │
│ # Script bash                                       │
│ aws ec2 create-security-group --name web-sg         │
│ aws ec2 authorize-ingress --port 80                 │
│ aws ec2 run-instances --ami ami-123 --type t2.micro │
│                                                     │
│ PROBLÈME: Si vous relancez ce script, ça va créer   │
│ des DOUBLONS! Il faut gérer vous-même ce qui        │
│ existe déjà ou pas.                                 │
└─────────────────────────────────────────────────────┘

DÉCLARATIVE (Terraform):
┌─────────────────────────────────────────────────────────────┐
│ "Voici ce que je VEUX avoir à la fin"                       │
│                                                             │
│ resource "aws_security_group" "web" {                       │
│   name = "web-sg"                                           │
│   # configuration...                                        │
│ }                                                           │
│                                                             │
│ resource "aws_instance" "web" {                             │
│   ami           = "ami-123"                                 │
│   instance_type = "t2.micro"                                │
│   # configuration...                                        │
│ }                                                           │
│                                                             │
│ AVANTAGE: Terraform compare l'état actuel avec l'état       │
│ désiré et fait UNIQUEMENT ce qui est nécessaire.            │
│ • Si la ressource existe déjà -> rien à faire                │
│ • Si elle est différente -> la modifier                      │
│ • Si elle n'existe pas -> la créer                           │
└─────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────────────────┐
│ 3. STATE (État) - LE CONCEPT LE PLUS IMPORTANT                            │
└───────────────────────────────────────────────────────────────────────────┘

Le STATE est la mémoire de Terraform.

QU'EST-CE QUE C'EST?
────────────────────
Un fichier (terraform.tfstate) qui contient:
• Quelles ressources Terraform a créées
• Leurs propriétés actuelles
• Les dépendances entre ressources

POURQUOI C'EST IMPORTANT?
─────────────────────────

Exemple concret:
┌──────────────────────────────────────────────────────────────┐
│ JOUR 1: Vous créez une instance EC2                          │
│ • Terraform la crée                                          │
│ • ID de l'instance: i-1234567890abcdef                      │
│ • Terraform enregistre cet ID dans le state                 │
│                                                               │
│ JOUR 2: Vous modifiez instance_type dans votre code         │
│ • Terraform lit le state                                     │
│ • Il voit "j'ai créé i-1234567890abcdef"                   │
│ • Il compare avec le nouveau code                           │
│ • Il sait qu'il doit MODIFIER (pas recréer) cette instance │
└──────────────────────────────────────────────────────────────┘

SANS STATE:
• Terraform ne saurait pas ce qu'il a déjà créé
• Il créerait des doublons à chaque `terraform apply`
• Impossible de détruire les ressources proprement

ANALOGIE:
Le state est comme un inventaire de magasin:
• Vous savez ce que vous avez en stock
• Vous savez quand commander plus
• Vous savez quand quelque chose manque


┌───────────────────────────────────────────────────────────────────────────┐
│ 4. PROVIDERS - Les Connecteurs vers les Clouds                            │
└───────────────────────────────────────────────────────────────────────────┘

Un PROVIDER est un plugin qui permet à Terraform de parler à un service cloud.

COMMENT ÇA MARCHE?
──────────────────

┌────────────────┐         ┌──────────────┐         ┌──────────────┐
│  Votre Code    │  ────>  │   Provider   │  ────>  │   AWS API    │
│   Terraform    │         │     AWS      │         │              │
└────────────────┘         └──────────────┘         └──────────────┘

Le provider traduit votre code Terraform en appels API vers le cloud.

EXEMPLE:
┌──────────────────────────────────────────────────────────────┐
│ provider "aws" {                                             │
│   region = "us-east-1"  # Région AWS à utiliser             │
│ }                                                            │
│                                                              │
│ # Maintenant vous pouvez utiliser des ressources AWS:      │
│ resource "aws_instance" "example" {                         │
│   ami           = "ami-123"                                 │
│   instance_type = "t2.micro"                                │
│ }                                                            │
└──────────────────────────────────────────────────────────────┘

PROVIDERS POPULAIRES:
• aws: Amazon Web Services
• google: Google Cloud Platform
• azurerm: Microsoft Azure
• kubernetes: Kubernetes
• docker: Docker
• github: GitHub

Il existe 3000+ providers! Presque tous les services ont un provider.


╔═══════════════════════════════════════════════════════════════════════════╗
║ LE WORKFLOW TERRAFORM EN 4 ÉTAPES                                         ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌─────────────────────────────────────────────────────────────────────────┐
│                                                                          │
│  1. WRITE      2. PLAN       3. APPLY      4. DESTROY (optionnel)      │
│  ┌────────┐   ┌────────┐    ┌────────┐    ┌────────┐                  │
│  │ Écrire │──>│ Prévoir│───>│ Créer  │───>│ Détruire│                 │
│  │  code  │   │ change-│    │ infra  │    │  infra  │                 │
│  │  .tf   │   │  ments │    │        │    │         │                  │
│  └────────┘   └────────┘    └────────┘    └────────┘                  │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

DÉTAIL DE CHAQUE ÉTAPE:

1. WRITE (Écrire)
   ──────────────
   Vous écrivez votre infrastructure en code dans des fichiers .tf
   
   Exemple: main.tf
   ┌──────────────────────────────────────────────────────┐
   │ resource "aws_instance" "web" {                      │
   │   ami           = "ami-123"                          │
   │   instance_type = "t2.micro"                         │
   │ }                                                     │
   └──────────────────────────────────────────────────────┘

2. PLAN (Planifier)
   ────────────────
   Commande: terraform plan
   
   Terraform vous montre ce qu'il va faire AVANT de le faire
   
   Sortie typique:
   ┌──────────────────────────────────────────────────────────────┐
   │ Plan: 1 to add, 0 to change, 0 to destroy.                  │
   │                                                              │
   │ + aws_instance.web                                          │
   │     ami:           "ami-123"                                │
   │     instance_type: "t2.micro"                               │
   │                                                              │
   │ Terraform va créer 1 nouvelle ressource.                   │
   └──────────────────────────────────────────────────────────────┘
   
   ANALOGIE: C'est comme un devis de travaux - vous voyez ce qui
   va être fait et combien ça coûte AVANT de signer.

3. APPLY (Appliquer)
   ─────────────────
   Commande: terraform apply
   
   Terraform crée RÉELLEMENT l'infrastructure
   
   Processus:
   ┌──────────────────────────────────────────────────────────────┐
   │ 1. Terraform vous montre le plan (comme dans step 2)        │
   │ 2. Il demande confirmation: "Voulez-vous continuer?"        │
   │ 3. Si vous tapez "yes", il crée les ressources             │
   │ 4. Il met à jour le state avec les infos des ressources    │
   └──────────────────────────────────────────────────────────────┘

4. DESTROY (Détruire) - Optionnel
   ───────────────────────────────
   Commande: terraform destroy
   
   Supprime TOUTE l'infrastructure créée par Terraform
   
   ATTENTION: Cette commande est DANGEREUSE!
   • Elle supprime tout
   • C'est irréversible
   • Utilisez-la avec précaution


═══════════════════════════════════════════════════════════════════════════════
[OUTIL] PARTIE 2: INSTALLATION ET CONFIGURATION INITIALE
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ INSTALLER TERRAFORM                                                        ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ MACOS (avec Homebrew)                                                     │
└───────────────────────────────────────────────────────────────────────────┘

# 1. Installer Homebrew (si pas déjà fait)
# Rendez-vous sur https://brew.sh et suivez les instructions

# 2. Installer Terraform
brew install terraform

# 3. Vérifier l'installation
terraform version
# Devrait afficher quelque chose comme: Terraform v1.6.0

POURQUOI Homebrew?
• Gère les mises à jour automatiquement
• Installation en une commande
• Pas besoin de configurer le PATH manuellement


┌───────────────────────────────────────────────────────────────────────────┐
│ LINUX (Ubuntu/Debian)                                                     │
└───────────────────────────────────────────────────────────────────────────┘

# 1. Ajouter la clé GPG de HashiCorp
wget -O- https://apt.releases.hashicorp.com/gpg | \
sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg

# 2. Ajouter le repository HashiCorp
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list

# 3. Installer Terraform
sudo apt update && sudo apt install terraform

# 4. Vérifier l'installation
terraform version

EXPLICATION DES COMMANDES:
• wget: télécharge la clé de sécurité
• gpg: vérifie que les packages viennent bien de HashiCorp
• apt: le gestionnaire de packages Ubuntu
• lsb_release -cs: détecte votre version d'Ubuntu automatiquement


┌───────────────────────────────────────────────────────────────────────────┐
│ WINDOWS (avec Chocolatey)                                                 │
└───────────────────────────────────────────────────────────────────────────┘

# 1. Installer Chocolatey (dans PowerShell en tant qu'admin)
# Suivez les instructions sur https://chocolatey.org/install

# 2. Installer Terraform
choco install terraform

# 3. Vérifier l'installation
terraform version


╔═══════════════════════════════════════════════════════════════════════════╗
║ PREMIER PROJET TERRAFORM - PAS À PAS                                      ║
╚═══════════════════════════════════════════════════════════════════════════╝

Créons un premier projet simple pour comprendre le workflow.

┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 1: Créer la Structure du Projet                                    │
└───────────────────────────────────────────────────────────────────────────┘

# Créer un dossier pour votre projet
mkdir mon-premier-terraform
cd mon-premier-terraform

# Créer un fichier main.tf
touch main.tf

POURQUOI cette structure?
• Terraform cherche automatiquement les fichiers .tf dans le dossier
• main.tf est la convention pour le fichier principal
• Vous pouvez avoir plusieurs fichiers .tf, Terraform les lira tous


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 2: Écrire Votre Premier Code Terraform                             │
└───────────────────────────────────────────────────────────────────────────┘

Ouvrez main.tf et ajoutez:

# main.tf
# ───────────────────────────────────────────────────────────────────────
# Ce fichier crée un simple fichier local pour démontrer Terraform
# ───────────────────────────────────────────────────────────────────────

# BLOC 1: Configuration Terraform
# ────────────────────────────────
# Définit les versions et providers requis
terraform {
  required_version = ">= 1.0"  # Version minimale de Terraform
  
  # Providers nécessaires pour ce projet
  required_providers {
    local = {
      source  = "hashicorp/local"  # Provider pour créer des fichiers locaux
      version = "~> 2.4"           # Version 2.4.x
    }
  }
}

# BLOC 2: Ressource
# ─────────────────
# Crée un fichier texte sur votre ordinateur
resource "local_file" "hello" {
  # Le contenu du fichier
  content  = "Hello, Terraform! Ceci est mon premier fichier créé par IaC."
  
  # Où créer le fichier
  filename = "${path.module}/hello.txt"
  
  # Permissions du fichier (format Unix: 0644 = rw-r--r--)
  file_permission = "0644"
}

# BLOC 3: Output
# ──────────────
# Affiche des informations après la création
output "file_path" {
  description = "Chemin complet du fichier créé"
  value       = local_file.hello.filename
}

output "file_content" {
  description = "Contenu du fichier"
  value       = local_file.hello.content
}


EXPLICATION DÉTAILLÉE DE CHAQUE PARTIE:
───────────────────────────────────────

1. BLOC terraform {}
   ─────────────────
   C'est la configuration de Terraform lui-même.
   
   • required_version: Spécifie quelle version de Terraform utiliser
     ">= 1.0" signifie "version 1.0 ou supérieure"
     Pourquoi? Pour éviter les problèmes de compatibilité
   
   • required_providers: Liste les plugins nécessaires
     Terraform va les télécharger automatiquement

2. BLOC resource
   ─────────────
   Définit une ressource à créer.
   
   Syntaxe: resource "TYPE" "NOM" { configuration }
   • TYPE: Le type de ressource (ici "local_file")
   • NOM: Un nom que vous choisissez (ici "hello")
   
   IMPORTANT: Vous référencez cette ressource avec TYPE.NOM
   Exemple: local_file.hello
   
   • content: Le contenu à mettre dans le fichier
   • filename: Où créer le fichier
     ${path.module} = le dossier où est votre fichier .tf
   • file_permission: Permissions Unix du fichier

3. BLOC output
   ───────────
   Affiche des informations à la fin de l'exécution.
   
   C'est comme un "return" en programmation.
   
   • description: Texte explicatif (optionnel mais recommandé)
   • value: Quelle valeur afficher
     local_file.hello.filename accède au chemin du fichier créé


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 3: Initialiser le Projet                                           │
└───────────────────────────────────────────────────────────────────────────┘

terraform init

QUE FAIT CETTE COMMANDE?
────────────────────────

1. Analyse vos fichiers .tf
2. Lit la section required_providers
3. Télécharge les providers nécessaires (ici: provider "local")
4. Crée un dossier .terraform/ pour stocker les plugins
5. Crée un fichier .terraform.lock.hcl pour figer les versions

SORTIE TYPIQUE:
┌──────────────────────────────────────────────────────────────────────┐
│ Initializing the backend...                                          │
│                                                                       │
│ Initializing provider plugins...                                     │
│ - Finding hashicorp/local versions matching "~> 2.4"...             │
│ - Installing hashicorp/local v2.4.0...                              │
│ - Installed hashicorp/local v2.4.0 (signed by HashiCorp)            │
│                                                                       │
│ Terraform has been successfully initialized!                         │
└──────────────────────────────────────────────────────────────────────┘

FICHIERS CRÉÉS:
mon-premier-terraform/
├── main.tf                    # Votre code
├── .terraform/                # Dossier avec les plugins (ne pas commiter)
│   └── providers/
│       └── hashicorp/
│           └── local/
└── .terraform.lock.hcl        # Versions figées (à commiter dans Git)

QUAND RELANCER init?
• Quand vous ajoutez un nouveau provider
• Quand vous changez de version de provider
• Quand vous clonez le projet depuis Git
• Après avoir changé de backend (où stocker le state)


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 4: Planifier les Changements                                       │
└───────────────────────────────────────────────────────────────────────────┘

terraform plan

QUE FAIT CETTE COMMANDE?
────────────────────────

1. Lit tous vos fichiers .tf
2. Lit le state actuel (pour l'instant vide car première exécution)
3. Compare "ce qui existe" vs "ce que vous voulez"
4. Génère un plan d'exécution
5. Affiche ce qu'il va faire

SORTIE TYPIQUE:
┌──────────────────────────────────────────────────────────────────────┐
│ 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.hello will be created                                │
│   + resource "local_file" "hello" {                                 │
│       + content              = "Hello, Terraform!"                  │
│       + filename             = "./hello.txt"                        │
│       + file_permission      = "0644"                               │
│       + id                   = (known after apply)                  │
│     }                                                                │
│                                                                       │
│ Plan: 1 to add, 0 to change, 0 to destroy.                         │
└──────────────────────────────────────────────────────────────────────┘

INTERPRÉTATION:
• "+" = va créer une nouvelle ressource
• "~" = va modifier une ressource existante
• "-" = va détruire une ressource
• "-/+" = va détruire puis recréer (remplacement)

"(known after apply)" = la valeur sera connue seulement après création
Exemple: l'ID d'une instance EC2 n'existe pas avant de la créer


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 5: Appliquer les Changements                                       │
└───────────────────────────────────────────────────────────────────────────┘

terraform apply

QUE FAIT CETTE COMMANDE?
────────────────────────

1. Affiche le plan (comme terraform plan)
2. Demande confirmation
3. Si vous tapez "yes", crée les ressources
4. Met à jour le state

INTERACTION:
┌──────────────────────────────────────────────────────────────────────┐
│ [... affichage du plan ...]                                          │
│                                                                       │
│ Do you want to perform these actions?                                │
│   Terraform will perform the actions described above.                │
│   Only 'yes' will be accepted to approve.                           │
│                                                                       │
│   Enter a value:                                                     │
└──────────────────────────────────────────────────────────────────────┘

Tapez "yes" et appuyez sur Entrée.

SORTIE APRÈS APPLICATION:
┌──────────────────────────────────────────────────────────────────────┐
│ local_file.hello: Creating...                                        │
│ local_file.hello: Creation complete after 0s [id=abc123...]         │
│                                                                       │
│ Apply complete! Resources: 1 added, 0 changed, 0 destroyed.         │
│                                                                       │
│ Outputs:                                                             │
│                                                                       │
│ file_content = "Hello, Terraform!"                                   │
│ file_path = "./hello.txt"                                           │
└──────────────────────────────────────────────────────────────────────┘

VÉRIFICATION:
Listez les fichiers dans votre dossier:

ls -la

Vous devriez voir:
┌──────────────────────────────────────────────────────────────────────┐
│ main.tf                    # Votre code                              │
│ hello.txt                  # Le fichier créé par Terraform!          │
│ terraform.tfstate          # Le state (mémoire de Terraform)         │
│ .terraform/                # Les plugins                             │
│ .terraform.lock.hcl        # Les versions figées                     │
└──────────────────────────────────────────────────────────────────────┘

Vérifiez le contenu:
cat hello.txt
# Output: Hello, Terraform! Ceci est mon premier fichier créé par IaC.

FÉLICITATIONS! [BRAVO]
Vous venez de créer votre première ressource avec Terraform!


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 6: Modifier et Appliquer à Nouveau                                 │
└───────────────────────────────────────────────────────────────────────────┘

Modifions le contenu du fichier pour voir comment Terraform gère les changements.

Ouvrez main.tf et changez la ligne content:

resource "local_file" "hello" {
  # Nouveau contenu
  content  = "Hello, Terraform! J'ai modifié ce fichier."
  
  filename = "${path.module}/hello.txt"
  file_permission = "0644"
}

Sauvegardez et exécutez:
terraform plan

SORTIE:
┌──────────────────────────────────────────────────────────────────────┐
│ Terraform will perform the following actions:                       │
│                                                                       │
│   # local_file.hello must be replaced                               │
│ -/+ resource "local_file" "hello" {                                 │
│       ~ content         = "Hello, Terraform!" -> "Hello, Terraform! │
│                           J'ai modifié ce fichier."                  │
│       ~ id              = "abc123..." -> (known after apply)        │
│       # (2 unchanged attributes hidden)                             │
│     }                                                                │
│                                                                       │
│ Plan: 1 to add, 0 to change, 1 to destroy.                         │
└──────────────────────────────────────────────────────────────────────┘

INTERPRÉTATION:
• "-/+" = destruction puis recréation
• "~" = attribut qui change
• Le symbole "->" montre l'ancienne valeur -> nouvelle valeur

Appliquez:
terraform apply
# Tapez "yes"

Vérifiez:
cat hello.txt
# Output: Hello, Terraform! J'ai modifié ce fichier.

LEÇON IMPORTANTE:
Terraform a DÉTECTÉ le changement et a mis à jour la ressource!
C'est la magie de l'approche déclarative.


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 7: Détruire les Ressources                                         │
└───────────────────────────────────────────────────────────────────────────┘

Pour nettoyer:

terraform destroy

QUE FAIT CETTE COMMANDE?
────────────────────────

1. Lit le state
2. Identifie toutes les ressources gérées par Terraform
3. Génère un plan de destruction
4. Demande confirmation
5. Supprime toutes les ressources

SORTIE:
┌──────────────────────────────────────────────────────────────────────┐
│ Terraform will perform the following actions:                       │
│                                                                       │
│   # local_file.hello will be destroyed                              │
│   - resource "local_file" "hello" {                                 │
│       - content         = "Hello, Terraform!" -> null               │
│       - filename        = "./hello.txt" -> null                     │
│       - id              = "abc123..." -> null                       │
│     }                                                                │
│                                                                       │
│ Plan: 0 to add, 0 to change, 1 to destroy.                         │
│                                                                       │
│ Do you really want to destroy all resources?                        │
│   Enter a value:                                                     │
└──────────────────────────────────────────────────────────────────────┘

Tapez "yes".

APRÈS:
ls -la
# hello.txt a disparu!

Le fichier terraform.tfstate existe toujours mais est vide.


╔═══════════════════════════════════════════════════════════════════════════╗
║ RÉCAPITULATIF DU WORKFLOW                                                 ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌────────────────────────────────────────────────────────────────────────┐
│ WORKFLOW COMPLET:                                                      │
│                                                                         │
│ 1. terraform init       # Une fois au début, ou après changement      │
│ 2. terraform fmt        # Formater le code (optionnel)                │
│ 3. terraform validate   # Vérifier la syntaxe (optionnel)             │
│ 4. terraform plan       # Voir ce qui va changer                      │
│ 5. terraform apply      # Appliquer les changements                   │
│                                                                         │
│ Pour nettoyer:                                                         │
│ 6. terraform destroy    # Supprimer toutes les ressources             │
└────────────────────────────────────────────────────────────────────────┘

COMMANDES ADDITIONNELLES UTILES:
• terraform show: Voir le state actuel en format lisible
• terraform output: Voir uniquement les outputs
• terraform state list: Lister toutes les ressources dans le state


═══════════════════════════════════════════════════════════════════════════════
[CONSTRUCTION] PARTIE 3: ANATOMIE D'UN PROJET TERRAFORM
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ STRUCTURE D'UN PROJET BIEN ORGANISÉ                                       ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ STRUCTURE DE BASE (Petit Projet)                                         │
└───────────────────────────────────────────────────────────────────────────┘

mon-projet-terraform/
├── main.tf              # Configuration principale
├── variables.tf         # Déclaration des variables
├── outputs.tf           # Déclaration des outputs
├── terraform.tfvars     # Valeurs des variables (ne PAS commiter si secrets)
├── versions.tf          # Versions de Terraform et providers
├── .gitignore           # Fichiers à ignorer dans Git
└── README.md            # Documentation du projet

POURQUOI SÉPARER EN PLUSIEURS FICHIERS?
────────────────────────────────────────

1. LISIBILITÉ
   • Plus facile de trouver les variables (toutes dans variables.tf)
   • Plus facile de voir les outputs (tous dans outputs.tf)

2. MAINTENANCE
   • Modifier les variables ne touche pas la config principale
   • Moins de conflits Git dans les équipes

3. CONVENTION
   • Tout le monde utilise la même structure
   • Facile de reprendre le projet de quelqu'un d'autre

NOTE IMPORTANTE:
Terraform lit TOUS les fichiers .tf dans le dossier.
L'ordre des fichiers n'a PAS d'importance.
La séparation est juste pour l'organisation humaine.


┌───────────────────────────────────────────────────────────────────────────┐
│ CONTENU DE CHAQUE FICHIER - EXPLIQUÉ                                     │
└───────────────────────────────────────────────────────────────────────────┘

═══ versions.tf ═══
──────────────────

# versions.tf
# ────────────────────────────────────────────────────────────────
# Ce fichier définit les versions de Terraform et des providers
# Il est lu en PREMIER (conceptuellement) par Terraform
# ────────────────────────────────────────────────────────────────

terraform {
  # Version minimale de Terraform requise
  # ">= 1.0" signifie "1.0 ou plus récent"
  required_version = ">= 1.0"
  
  # Liste des providers nécessaires
  required_providers {
    # Provider AWS
    aws = {
      source  = "hashicorp/aws"  # Où télécharger le provider
      version = "~> 5.0"         # Version 5.x (mais pas 6.0)
    }
    
    # Provider Random (pour générer des valeurs aléatoires)
    random = {
      source  = "hashicorp/random"
      version = "~> 3.5"
    }
  }
  
  # Configuration du backend (où stocker le state)
  # Pour l'instant, state local (fichier dans le dossier)
  # backend "local" {}  # Optionnel, c'est le défaut
}

# Configuration du provider AWS
provider "aws" {
  region = var.aws_region  # Utilise la variable aws_region
  
  # Tags par défaut appliqués à toutes les ressources
  default_tags {
    tags = {
      Projet     = var.project_name
      ManagedBy  = "Terraform"
      CreatedAt  = timestamp()
    }
  }
}

EXPLICATION DES VERSIONS:
• "~> 5.0" signifie: >= 5.0.0 et < 6.0.0
  (accepte 5.0.1, 5.1.0, 5.99.0 mais PAS 6.0.0)
  Pourquoi? Pour avoir les bug fixes mais pas les breaking changes

• ">= 1.0" signifie: 1.0 ou supérieur
  (accepte 1.0, 1.5, 2.0, etc.)


═══ variables.tf ═══
────────────────────

# variables.tf
# ────────────────────────────────────────────────────────────────
# Ce fichier DÉCLARE les variables (leur nom, type, valeur par défaut)
# Les VALEURS réelles sont dans terraform.tfvars
# ────────────────────────────────────────────────────────────────

# ┌─────────────────────────────────────────────────────────────┐
# │ VARIABLE SIMPLE: String (texte)                            │
# └─────────────────────────────────────────────────────────────┘

variable "aws_region" {
  description = "Région AWS où créer les ressources"
  type        = string
  default     = "us-east-1"
}

# Explication:
# • description: Texte explicatif (optionnel mais FORTEMENT recommandé)
# • type: Type de données (string, number, bool, list, map, object)
# • default: Valeur par défaut (optionnel)
#   Si pas de default, la variable est OBLIGATOIRE

# Comment utiliser cette variable dans le code:
# var.aws_region


# ┌─────────────────────────────────────────────────────────────┐
# │ VARIABLE AVEC VALIDATION                                    │
# └─────────────────────────────────────────────────────────────┘

variable "instance_type" {
  description = "Type d'instance EC2 (t2.micro, t2.small, etc.)"
  type        = string
  default     = "t2.micro"
  
  # Validation: la valeur DOIT être dans cette liste
  validation {
    condition     = contains(["t2.micro", "t2.small", "t2.medium"], var.instance_type)
    error_message = "instance_type doit être t2.micro, t2.small ou t2.medium."
  }
}

# Explication de la validation:
# • contains(): fonction qui vérifie si une valeur est dans une liste
# • condition: expression booléenne qui doit être vraie
# • error_message: message affiché si la condition est fausse

# Si vous essayez terraform apply avec instance_type = "t2.large":
# Error: Invalid value for variable
# instance_type doit être t2.micro, t2.small ou t2.medium.


# ┌─────────────────────────────────────────────────────────────┐
# │ VARIABLE NUMBER (nombre)                                    │
# └─────────────────────────────────────────────────────────────┘

variable "instance_count" {
  description = "Nombre d'instances EC2 à créer"
  type        = number
  default     = 1
  
  # Validation: doit être entre 1 et 10
  validation {
    condition     = var.instance_count >= 1 && var.instance_count <= 10
    error_message = "instance_count doit être entre 1 et 10."
  }
}


# ┌─────────────────────────────────────────────────────────────┐
# │ VARIABLE BOOL (vrai/faux)                                   │
# └─────────────────────────────────────────────────────────────┘

variable "enable_monitoring" {
  description = "Activer le monitoring détaillé?"
  type        = bool
  default     = true
}

# Utilisation dans le code:
# resource "aws_instance" "web" {
#   monitoring = var.enable_monitoring
# }


# ┌─────────────────────────────────────────────────────────────┐
# │ VARIABLE LIST (liste)                                       │
# └─────────────────────────────────────────────────────────────┘

variable "availability_zones" {
  description = "Liste des zones de disponibilité à utiliser"
  type        = list(string)  # Liste de strings
  default     = ["us-east-1a", "us-east-1b", "us-east-1c"]
}

# Accéder aux éléments:
# var.availability_zones[0]  # Premier élément: "us-east-1a"
# var.availability_zones[1]  # Deuxième élément: "us-east-1b"


# ┌─────────────────────────────────────────────────────────────┐
# │ VARIABLE MAP (dictionnaire clé-valeur)                     │
# └─────────────────────────────────────────────────────────────┘

variable "tags" {
  description = "Tags à appliquer aux ressources"
  type        = map(string)  # Map de strings
  default = {
    Environment = "dev"
    Project     = "mon-app"
    Team        = "backend"
  }
}

# Accéder aux valeurs:
# var.tags["Environment"]  # "dev"
# var.tags["Project"]      # "mon-app"

# Fusionner avec d'autres tags:
# merge(var.tags, { Name = "mon-serveur" })
# Résultat: {
#   Environment = "dev"
#   Project     = "mon-app"
#   Team        = "backend"
#   Name        = "mon-serveur"
# }


# ┌─────────────────────────────────────────────────────────────┐
# │ VARIABLE OBJECT (structure complexe)                        │
# └─────────────────────────────────────────────────────────────┘

variable "database" {
  description = "Configuration de la base de données"
  type = object({
    engine         = string  # "postgres", "mysql", etc.
    instance_class = string  # "db.t3.micro", etc.
    storage        = number  # Taille en GB
    multi_az       = bool    # Haute disponibilité?
  })
  
  default = {
    engine         = "postgres"
    instance_class = "db.t3.micro"
    storage        = 20
    multi_az       = false
  }
}

# Accéder aux champs:
# var.database.engine          # "postgres"
# var.database.instance_class  # "db.t3.micro"
# var.database.storage         # 20


# ┌─────────────────────────────────────────────────────────────┐
# │ VARIABLE SENSIBLE (mots de passe, clés API)                │
# └─────────────────────────────────────────────────────────────┘

variable "db_password" {
  description = "Mot de passe de la base de données"
  type        = string
  sensitive   = true  # NE PAS afficher dans les logs
}

# ATTENTION:
# • sensitive = true empêche l'affichage dans les logs Terraform
# • Mais la valeur est QUAND MÊME stockée en clair dans le state!
# • Pour plus de sécurité, utilisez AWS Secrets Manager ou Vault


# ┌─────────────────────────────────────────────────────────────┐
# │ VARIABLE SANS VALEUR PAR DÉFAUT (obligatoire)              │
# └─────────────────────────────────────────────────────────────┘

variable "project_name" {
  description = "Nom du projet (utilisé pour nommer les ressources)"
  type        = string
  # Pas de default = OBLIGATOIRE
}

# Si vous lancez terraform apply sans définir cette variable,
# Terraform vous la demandera interactivement:
# var.project_name
#   Nom du projet (utilisé pour nommer les ressources)
#   Enter a value:


═══ terraform.tfvars ═══
────────────────────────

# terraform.tfvars
# ────────────────────────────────────────────────────────────────
# Ce fichier contient les VALEURS des variables
# Terraform le lit automatiquement (pas besoin de -var-file)
# ────────────────────────────────────────────────────────────────

# ATTENTION: Ce fichier peut contenir des secrets!
# Ne le commitez PAS dans Git si c'est le cas.
# Ajoutez-le dans .gitignore

# Valeurs des variables
project_name     = "mon-app-python"
aws_region       = "eu-west-1"
instance_type    = "t2.small"
instance_count   = 2
enable_monitoring = true

availability_zones = [
  "eu-west-1a",
  "eu-west-1b"
]

tags = {
  Environment = "production"
  Project     = "mon-app-python"
  Team        = "backend"
  CostCenter  = "engineering"
}

database = {
  engine         = "postgres"
  instance_class = "db.t3.small"
  storage        = 100
  multi_az       = true
}

# Variables sensibles
# MIEUX: Passer via variable d'environnement ou Secrets Manager
db_password = "MON_MOT_DE_PASSE_SUPER_SECRET"  # À NE PAS FAIRE!

# ALTERNATIVE SÉCURISÉE:
# 1. Via variable d'environnement:
#    export TF_VAR_db_password="mon_password"
#
# 2. Via ligne de commande:
#    terraform apply -var="db_password=mon_password"
#
# 3. Via AWS Secrets Manager (recommandé en production)


═══ main.tf ═══
───────────────

# main.tf
# ────────────────────────────────────────────────────────────────
# Configuration principale de l'infrastructure
# ────────────────────────────────────────────────────────────────

# ┌─────────────────────────────────────────────────────────────┐
# │ DATA SOURCES: Récupérer des informations existantes        │
# └─────────────────────────────────────────────────────────────┘

# Récupérer l'AMI Ubuntu la plus récente
data "aws_ami" "ubuntu" {
  most_recent = true  # Prendre la plus récente
  owners      = ["099720109477"]  # Canonical (éditeur d'Ubuntu)
  
  # Filtres pour trouver la bonne image
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }
  
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

# POURQUOI utiliser un data source plutôt que hardcoder l'AMI ID?
# • L'AMI ID change selon la région
# • L'AMI ID change quand Ubuntu sort une nouvelle version
# • Avec data source, vous avez TOUJOURS la dernière version


# ┌─────────────────────────────────────────────────────────────┐
# │ LOCALS: Variables calculées                                │
# └─────────────────────────────────────────────────────────────┘

locals {
  # Tags communs à toutes les ressources
  common_tags = {
    Project     = var.project_name
    Environment = terraform.workspace  # dev, staging, prod
    ManagedBy   = "Terraform"
  }
  
  # Nom de ressource basé sur le projet et l'environnement
  name_prefix = "${var.project_name}-${terraform.workspace}"
  
  # Condition: production ou pas?
  is_production = terraform.workspace == "prod"
  
  # Type d'instance selon l'environnement
  instance_type = local.is_production ? "t3.large" : "t3.micro"
}

# DIFFÉRENCE entre locals et variables:
# • variables: Définies par l'utilisateur, peuvent être passées en paramètre
# • locals: Calculées dans le code, ne peuvent pas être changées de l'extérieur


# ┌─────────────────────────────────────────────────────────────┐
# │ RESSOURCES: Créer l'infrastructure                         │
# └─────────────────────────────────────────────────────────────┘

# Security Group (pare-feu)
resource "aws_security_group" "web" {
  name        = "${local.name_prefix}-web-sg"
  description = "Security group pour les serveurs web"
  
  # Règle entrante: autoriser HTTP (port 80)
  ingress {
    description = "HTTP depuis Internet"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]  # Tout le monde
  }
  
  # Règle entrante: autoriser HTTPS (port 443)
  ingress {
    description = "HTTPS depuis Internet"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  # Règle entrante: autoriser SSH (port 22) - restreint
  ingress {
    description = "SSH depuis bureau uniquement"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["1.2.3.4/32"]  # Remplacez par votre IP
  }
  
  # Règle sortante: autoriser tout le trafic sortant
  egress {
    description = "Tout le trafic sortant"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"  # Tous les protocoles
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  tags = merge(
    local.common_tags,
    {
      Name = "${local.name_prefix}-web-sg"
    }
  )
}

# Instance EC2
resource "aws_instance" "web" {
  # Créer plusieurs instances (selon var.instance_count)
  count = var.instance_count
  
  # AMI: Utilise l'AMI Ubuntu trouvée par le data source
  ami           = data.aws_ami.ubuntu.id
  
  # Type d'instance: Utilise la variable
  instance_type = var.instance_type
  
  # Security group: Référence le SG créé ci-dessus
  vpc_security_group_ids = [aws_security_group.web.id]
  
  # Monitoring détaillé (coûte plus cher)
  monitoring = var.enable_monitoring
  
  # Configuration du disque racine
  root_block_device {
    volume_size = 20      # 20 GB
    volume_type = "gp3"   # Type SSD moderne
    encrypted   = true    # Chiffrement activé
  }
  
  # Script d'initialisation (lancé au premier démarrage)
  user_data = templatefile("${path.module}/user_data.sh", {
    project_name = var.project_name
  })
  
  # Tags: Fusionner les tags communs + tags spécifiques
  tags = merge(
    local.common_tags,
    {
      Name  = "${local.name_prefix}-web-${count.index}"
      Index = count.index
    }
  )
  
  # Cycle de vie: Options avancées
  lifecycle {
    # Créer la nouvelle instance AVANT de détruire l'ancienne
    create_before_destroy = true
    
    # Ne jamais détruire cette ressource avec terraform destroy
    # prevent_destroy = true  # Décommentez pour activer
    
    # Ignorer les changements sur ces attributs
    # (utile si des outils externes modifient l'instance)
    # ignore_changes = [
    #   user_data,
    #   tags["LastModified"]
    # ]
  }
}

# Elastic IP (IP publique fixe)
resource "aws_eip" "web" {
  # Créer une EIP pour chaque instance
  count = var.instance_count
  
  # Associer à l'instance correspondante
  instance = aws_instance.web[count.index].id
  domain   = "vpc"
  
  tags = merge(
    local.common_tags,
    {
      Name = "${local.name_prefix}-eip-${count.index}"
    }
  )
  
  # Dépendance explicite: attendre que l'Internet Gateway existe
  # depends_on = [aws_internet_gateway.main]
}

# EXPLICATION DES RÉFÉRENCES:
# • aws_security_group.web.id -> ID du security group créé
# • aws_instance.web[count.index] -> Instance numéro count.index
# • data.aws_ami.ubuntu.id -> ID de l'AMI trouvée
# • var.instance_count -> Valeur de la variable


═══ outputs.tf ═══
──────────────────

# outputs.tf
# ────────────────────────────────────────────────────────────────
# Définition des outputs (informations à afficher après apply)
# ────────────────────────────────────────────────────────────────

# ┌─────────────────────────────────────────────────────────────┐
# │ OUTPUT SIMPLE: Une valeur                                  │
# └─────────────────────────────────────────────────────────────┘

output "instance_ids" {
  description = "IDs des instances EC2 créées"
  value       = aws_instance.web[*].id
}

# [*] signifie "tous les éléments"
# Équivalent à: [aws_instance.web[0].id, aws_instance.web[1].id, ...]


# ┌─────────────────────────────────────────────────────────────┐
# │ OUTPUT: Liste d'IPs publiques                              │
# └─────────────────────────────────────────────────────────────┘

output "public_ips" {
  description = "Adresses IP publiques des serveurs"
  value       = aws_eip.web[*].public_ip
}

# Exemple de sortie:
# public_ips = [
#   "54.123.45.67",
#   "54.123.45.68"
# ]


# ┌─────────────────────────────────────────────────────────────┐
# │ OUTPUT: Map structuré                                      │
# └─────────────────────────────────────────────────────────────┘

output "instances_info" {
  description = "Informations complètes sur les instances"
  value = {
    for idx, instance in aws_instance.web : idx => {
      id         = instance.id
      public_ip  = aws_eip.web[idx].public_ip
      private_ip = instance.private_ip
      az         = instance.availability_zone
    }
  }
}

# Exemple de sortie:
# instances_info = {
#   "0" = {
#     id         = "i-1234567890abcdef0"
#     public_ip  = "54.123.45.67"
#     private_ip = "10.0.1.10"
#     az         = "us-east-1a"
#   }
#   "1" = {
#     id         = "i-0987654321fedcba0"
#     public_ip  = "54.123.45.68"
#     private_ip = "10.0.1.11"
#     az         = "us-east-1b"
#   }
# }


# ┌─────────────────────────────────────────────────────────────┐
# │ OUTPUT: Commande SSH prête à utiliser                      │
# └─────────────────────────────────────────────────────────────┘

output "ssh_commands" {
  description = "Commandes SSH pour se connecter aux serveurs"
  value = [
    for idx, ip in aws_eip.web[*].public_ip :
    "ssh -i ~/.ssh/mykey.pem ubuntu@${ip}"
  ]
}

# Exemple de sortie:
# ssh_commands = [
#   "ssh -i ~/.ssh/mykey.pem ubuntu@54.123.45.67",
#   "ssh -i ~/.ssh/mykey.pem ubuntu@54.123.45.68"
# ]


# ┌─────────────────────────────────────────────────────────────┐
# │ OUTPUT SENSIBLE: Ne pas afficher dans les logs            │
# └─────────────────────────────────────────────────────────────┘

output "database_password" {
  description = "Mot de passe de la base de données"
  value       = var.db_password
  sensitive   = true  # Ne sera pas affiché dans les logs
}

# Affichage:
# database_password = <sensitive>

# Pour voir la valeur quand même:
# terraform output database_password


# ┌─────────────────────────────────────────────────────────────┐
# │ OUTPUT: URL de l'application                               │
# └─────────────────────────────────────────────────────────────┘

output "application_urls" {
  description = "URLs pour accéder à l'application"
  value = [
    for ip in aws_eip.web[*].public_ip :
    "http://${ip}"
  ]
}


═══ .gitignore ═══
──────────────────

# .gitignore
# ────────────────────────────────────────────────────────────────
# Fichiers à NE PAS commiter dans Git
# ────────────────────────────────────────────────────────────────

# ┌─────────────────────────────────────────────────────────────┐
# │ Dossiers et fichiers Terraform                             │
# └─────────────────────────────────────────────────────────────┘

# Dossier des plugins (téléchargés par terraform init)
.terraform/

# State files (contiennent des infos sensibles!)
*.tfstate
*.tfstate.*
*.tfstate.backup

# Fichiers de plan (peuvent contenir des secrets)
*.tfplan

# Crash logs
crash.log
crash.*.log

# Fichiers de configuration avec secrets
*.tfvars  # Si contient des passwords
terraform.tfvars  # Ou juste le fichier principal
secrets.tfvars

# Fichiers override (utilisés pour tester localement)
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Fichiers de configuration CLI
.terraformrc
terraform.rc


# ┌─────────────────────────────────────────────────────────────┐
# │ Fichiers Python (si vous utilisez python-terraform)        │
# └─────────────────────────────────────────────────────────────┘

__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
.venv/
env/
ENV/


# ┌─────────────────────────────────────────────────────────────┐
# │ Autres                                                      │
# └─────────────────────────────────────────────────────────────┘

.DS_Store  # macOS
Thumbs.db  # Windows
.idea/     # IntelliJ IDEA
.vscode/   # Visual Studio Code

# Sauvegardes
*.backup
*.bak


# ┌─────────────────────────────────────────────────────────────┐
# │ EXCEPTION: Fichiers à TOUJOURS commiter                    │
# └─────────────────────────────────────────────────────────────┘

# Lock file (versions des providers) - À COMMITER!
!.terraform.lock.hcl


POURQUOI ne pas commiter le state?
───────────────────────────────────

1. CONTIENT DES SECRETS
   • Mots de passe en clair
   • Clés API
   • Autres infos sensibles

2. GROS FICHIER
   • Peut devenir très gros (plusieurs MB)
   • Ralentit Git

3. CONFLITS
   • Si deux personnes modifient en même temps
   • Risque de corruption du state

SOLUTION: Backend distant (S3, Terraform Cloud, etc.)
Voir plus loin dans ce guide.


═══════════════════════════════════════════════════════════════════════════════
[OBJECTIF] PARTIE 4: COMMANDES TERRAFORM EN DÉTAIL
═══════════════════════════════════════════════════════════════════════════════

Nous allons explorer chaque commande Terraform avec:
• Ce qu'elle fait EXACTEMENT
• Quand l'utiliser
• Options utiles
• Cas d'usage pratiques


╔═══════════════════════════════════════════════════════════════════════════╗
║ terraform init                                                             ║
╚═══════════════════════════════════════════════════════════════════════════╝

SYNTAXE:
terraform init [options]

QUE FAIT-ELLE?
──────────────

1. Lit la configuration Terraform (tous les fichiers .tf)
2. Identifie les providers nécessaires (aws, google, etc.)
3. Télécharge les plugins des providers
4. Initialise le backend (où stocker le state)
5. Crée le dossier .terraform/ et .terraform.lock.hcl

QUAND L'UTILISER?
─────────────────

• [OK] PREMIÈRE FOIS que vous travaillez sur un projet
• [OK] Après avoir cloné un projet depuis Git
• [OK] Après avoir ajouté un nouveau provider
• [OK] Après avoir changé la version d'un provider
• [OK] Après avoir changé la configuration du backend

OPTIONS UTILES:
───────────────

# Mise à jour des providers vers les dernières versions compatibles
terraform init -upgrade

# Reconfigurer le backend (changer où stocker le state)
terraform init -reconfigure

# Ne pas demander de confirmation
terraform init -input=false

# Mode backend uniquement (ne pas télécharger les providers)
terraform init -backend=false

EXEMPLE PRATIQUE:
─────────────────

# Situation: Vous clonez un projet depuis GitHub
git clone https://github.com/team/mon-projet-terraform.git
cd mon-projet-terraform

# Les fichiers .tf sont là, mais pas les plugins
ls
# Output: main.tf  variables.tf  outputs.tf  versions.tf

# Initialiser le projet
terraform init

# Output:
# Initializing the backend...
# Initializing provider plugins...
# - Finding hashicorp/aws versions matching "~> 5.0"...
# - Installing hashicorp/aws v5.25.0...
# Terraform has been successfully initialized!

# Maintenant vous pouvez utiliser terraform plan, apply, etc.


╔═══════════════════════════════════════════════════════════════════════════╗
║ terraform fmt                                                              ║
╚═══════════════════════════════════════════════════════════════════════════╝

SYNTAXE:
terraform fmt [options] [DIR]

QUE FAIT-ELLE?
──────────────

Formate vos fichiers .tf selon les conventions Terraform:
• Indentation correcte
• Espacement cohérent
• Alignement des valeurs
• Ordre alphabétique des attributs (optionnel)

POURQUOI C'EST IMPORTANT?
─────────────────────────

1. LISIBILITÉ
   • Code uniforme, facile à lire
   
2. COLLABORATION
   • Tout le monde utilise le même format
   • Moins de diff dans Git
   
3. PROFESSIONALISME
   • Code propre = code sérieux

AVANT fmt:
──────────

resource "aws_instance" "web" {
ami="ami-123"
instance_type = "t2.micro"
  tags={
Name="web-server"
  }
}

APRÈS fmt:
──────────

resource "aws_instance" "web" {
  ami           = "ami-123"
  instance_type = "t2.micro"
  
  tags = {
    Name = "web-server"
  }
}

OPTIONS UTILES:
───────────────

# Formater tous les fichiers dans le dossier courant
terraform fmt

# Formater récursivement (tous les sous-dossiers)
terraform fmt -recursive

# Mode vérification (ne modifie pas, juste vérifie)
terraform fmt -check

# Afficher les différences
terraform fmt -diff

UTILISATION EN CI/CD:
─────────────────────

# Dans votre pipeline CI (GitHub Actions, GitLab CI, etc.)
# Vérifier que le code est bien formaté
terraform fmt -check -recursive

# Si mal formaté, le build échoue
# Les développeurs doivent formatter avant de pousser


╔═══════════════════════════════════════════════════════════════════════════╗
║ terraform validate                                                         ║
╚═══════════════════════════════════════════════════════════════════════════╝

SYNTAXE:
terraform validate

QUE FAIT-ELLE?
──────────────

Vérifie que votre configuration Terraform est valide:
• Syntaxe correcte
• Références entre ressources valides
• Types de données corrects
• Attributs obligatoires présents

CE QU'ELLE NE FAIT PAS:
───────────────────────

• [X] Ne vérifie PAS si les credentials AWS sont valides
• [X] Ne vérifie PAS si les ressources existent dans le cloud
• [X] Ne vérifie PAS la logique métier (c'est à vous)

QUAND L'UTILISER?
─────────────────

• [OK] Avant de faire un commit Git
• [OK] Dans votre pipeline CI/CD
• [OK] Après avoir écrit du nouveau code
• [OK] Pour debugger des erreurs de syntaxe

EXEMPLE D'ERREUR DÉTECTÉE:
──────────────────────────

# Code avec une erreur
resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.instance_type
  
  # ERREUR: mauvaise référence
  security_groups = [aws_security_group.typo.id]  # "typo" n'existe pas
}

# Résultat de validate:
terraform validate

# Output:
# Error: Reference to undeclared resource
#   on main.tf line 5:
#     security_groups = [aws_security_group.typo.id]
# 
# A managed resource "aws_security_group" "typo" has not been declared
# in the root module.

WORKFLOW RECOMMANDÉ:
────────────────────

# 1. Écrire du code
vim main.tf

# 2. Formater
terraform fmt

# 3. Valider
terraform validate

# 4. Si OK, continuer avec plan
terraform plan


╔═══════════════════════════════════════════════════════════════════════════╗
║ terraform plan                                                             ║
╚═══════════════════════════════════════════════════════════════════════════╝

SYNTAXE:
terraform plan [options]

QUE FAIT-ELLE?
──────────────

1. Lit votre configuration (.tf files)
2. Lit le state actuel (ce qui existe déjà)
3. Appelle les APIs des providers pour vérifier l'état réel
4. Compare "ce qui existe" vs "ce que vous voulez"
5. Génère un plan d'exécution
6. Affiche ce qui sera créé/modifié/détruit

POURQUOI C'EST CRUCIAL?
───────────────────────

C'est votre FILET DE SÉCURITÉ!
• Vous voyez AVANT de faire des changements
• Vous pouvez détecter des erreurs
• Vous pouvez prévoir les impacts

ANALOGIE: C'est comme regarder le devis AVANT de signer les travaux.

SYMBOLES DANS LE PLAN:
──────────────────────

+ create       # Créer une nouvelle ressource
~ update       # Modifier une ressource existante
- destroy      # Détruire une ressource
-/+ replace    # Détruire puis recréer (certains attributs non modifiables)
<= read        # Lire des données (data sources)

EXEMPLE DE PLAN:
────────────────

terraform plan

# Output:

Terraform will perform the following actions:

  # aws_instance.web[0] will be created
  + resource "aws_instance" "web" {
      + ami                    = "ami-12345678"
      + instance_type          = "t2.micro"
      + id                     = (known after apply)
      + public_ip              = (known after apply)
      + ...
    }

  # aws_instance.web[1] will be updated in-place
  ~ resource "aws_instance" "web" {
        id             = "i-1234567890abcdef0"
      ~ instance_type  = "t2.micro" -> "t2.small"
        # (8 unchanged attributes hidden)
    }

  # aws_security_group.old will be destroyed
  - resource "aws_security_group" "old" {
      - id   = "sg-12345678" -> null
      - name = "old-sg" -> null
      ...
    }

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

INTERPRÉTATION:
───────────────

• 1 to add: Une nouvelle instance web[0] sera créée
• 1 to change: L'instance web[1] sera modifiée (t2.micro -> t2.small)
• 1 to destroy: L'ancien security group sera supprimé

Total: 3 actions

"(known after apply)" = La valeur sera connue seulement après création
• Exemple: L'IP publique d'une instance n'existe pas avant de la créer

OPTIONS UTILES:
───────────────

# Sauvegarder le plan dans un fichier
terraform plan -out=tfplan

# Puis l'appliquer directement (sans redemander confirmation)
terraform apply tfplan

# Plan pour une ressource spécifique uniquement
terraform plan -target=aws_instance.web

# Plan avec variables en ligne de commande
terraform plan -var="instance_type=t2.small"

# Plan avec un fichier de variables spécifique
terraform plan -var-file="prod.tfvars"

# Mode détaillé (voir les valeurs sensibles)
terraform plan -json

# Plan avec code de sortie détaillé (utile en CI)
terraform plan -detailed-exitcode
# Code 0 = pas de changement
# Code 1 = erreur
# Code 2 = changements détectés

CAS D'USAGE: CI/CD
──────────────────

# Dans un pipeline GitLab CI / GitHub Actions
# Faire un plan et commenter la Pull Request avec le résultat

name: Terraform Plan
on: [pull_request]

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: hashicorp/setup-terraform@v2
      
      - name: Terraform Init
        run: terraform init
      
      - name: Terraform Plan
        run: terraform plan -no-color > plan.txt
      
      - name: Comment PR
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('fs');
            const plan = fs.readFileSync('plan.txt', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '## Terraform Plan\n```\n' + plan + '\n```'
            });


╔═══════════════════════════════════════════════════════════════════════════╗
║ terraform apply                                                            ║
╚═══════════════════════════════════════════════════════════════════════════╝

SYNTAXE:
terraform apply [options] [PLAN]

QUE FAIT-ELLE?
──────────────

1. Exécute un plan (comme terraform plan)
2. Demande confirmation (sauf si -auto-approve)
3. Applique les changements (crée/modifie/détruit)
4. Met à jour le state avec les nouvelles infos

[ATTENTION]  C'EST LA COMMANDE QUI MODIFIE RÉELLEMENT LE CLOUD!

PROCESSUS COMPLET:
──────────────────

┌────────────────────────────────────────────────────────────────┐
│ $ terraform apply                                              │
│                                                                 │
│ 1. Terraform génère un plan                                   │
│    [... affichage du plan ...]                                │
│                                                                 │
│ 2. Terraform demande confirmation                             │
│    Do you want to perform these actions?                      │
│      Enter a value: yes                                       │
│                                                                 │
│ 3. Terraform applique les changements                         │
│    aws_security_group.web: Creating...                        │
│    aws_security_group.web: Creation complete after 2s         │
│    aws_instance.web[0]: Creating...                           │
│    aws_instance.web[0]: Still creating... [10s elapsed]       │
│    aws_instance.web[0]: Creation complete after 34s           │
│                                                                 │
│ 4. Résumé                                                      │
│    Apply complete! Resources: 2 added, 0 changed, 0 destroyed│
│                                                                 │
│ 5. Outputs (si définis)                                       │
│    Outputs:                                                    │
│    instance_id = "i-1234567890abcdef0"                        │
│    public_ip = "54.123.45.67"                                 │
└────────────────────────────────────────────────────────────────┘

OPTIONS UTILES:
───────────────

# Application automatique (ATTENTION: DANGEREUX!)
terraform apply -auto-approve

# Appliquer un plan sauvegardé
terraform plan -out=tfplan
terraform apply tfplan  # Pas de demande de confirmation

# Appliquer pour une ressource spécifique
terraform apply -target=aws_instance.web

# Avec variables
terraform apply -var="instance_type=t2.large"
terraform apply -var-file="prod.tfvars"

# Mode parallèle (nombre d'opérations simultanées)
terraform apply -parallelism=20  # Défaut: 10

# Refresh désactivé (ne pas vérifier l'état réel avant)
terraform apply -refresh=false  # Plus rapide mais risqué

ERREURS COURANTES:
──────────────────

1. CREDENTIALS INVALIDES
   ┌──────────────────────────────────────────────────────────┐
   │ Error: error configuring Terraform AWS Provider: error  │
   │ validating provider credentials: error calling sts:     │
   │ GetCallerIdentity: InvalidClientTokenId                 │
   └──────────────────────────────────────────────────────────┘
   
   SOLUTION:
   • Vérifier AWS_ACCESS_KEY_ID et AWS_SECRET_ACCESS_KEY
   • Vérifier ~/.aws/credentials
   • Essayer: aws sts get-caller-identity

2. RESSOURCE DÉJÀ EXISTANTE
   ┌──────────────────────────────────────────────────────────┐
   │ Error: Error creating security group: InvalidGroup.     │
   │ Duplicate: The security group 'web-sg' already exists   │
   └──────────────────────────────────────────────────────────┘
   
   SOLUTION:
   • Importer la ressource existante: terraform import
   • Ou changer le nom dans votre configuration

3. DÉPENDANCE MANQUANTE
   ┌──────────────────────────────────────────────────────────┐
   │ Error: Error launching instance: VPCIdNotSpecified:     │
   │ No default VPC for this user                            │
   └──────────────────────────────────────────────────────────┘
   
   SOLUTION:
   • Créer un VPC d'abord
   • Ou utiliser depends_on pour forcer l'ordre

WORKFLOW SÉCURISÉ:
──────────────────

# 1. Toujours faire un plan d'abord
terraform plan -out=tfplan

# 2. Examiner attentivement le plan
# Vérifier que tout est OK

# 3. Appliquer le plan sauvegardé
terraform apply tfplan

# Avantage: Le plan affiché est EXACTEMENT ce qui sera appliqué


╔═══════════════════════════════════════════════════════════════════════════╗
║ terraform destroy                                                          ║
╚═══════════════════════════════════════════════════════════════════════════╝

SYNTAXE:
terraform destroy [options]

QUE FAIT-ELLE?
──────────────

[ATTENTION]  DÉTRUIT TOUTE L'INFRASTRUCTURE GÉRÉE PAR TERRAFORM!

1. Lit le state
2. Identifie toutes les ressources à détruire
3. Génère un plan de destruction
4. Demande confirmation (à moins que -auto-approve)
5. Détruit les ressources dans l'ordre inverse des dépendances

POURQUOI CETTE COMMANDE EXISTE?
───────────────────────────────

• Nettoyer un environnement de test
• Supprimer une infrastructure temporaire
• Économiser de l'argent (arrêter des ressources qui coûtent)

[ATTENTION]  ATTENTION: C'EST IRRÉVERSIBLE!
─────────────────────────────────

• Pas de backup automatique
• Pas de corbeille
• Les données sont PERDUES (sauf si vous avez des backups externes)

PROCESSUS:
──────────

$ terraform destroy

Terraform will perform the following actions:

  # aws_eip.web will be destroyed
  - resource "aws_eip" "web" {
      - id         = "eipalloc-12345678" -> null
      - public_ip  = "54.123.45.67" -> null
      ...
    }

  # aws_instance.web will be destroyed
  - resource "aws_instance" "web" {
      - ami           = "ami-12345678" -> null
      - id            = "i-1234567890abcdef0" -> null
      - instance_type = "t2.micro" -> null
      ...
    }

  # aws_security_group.web will be destroyed
  - resource "aws_security_group" "web" {
      - id   = "sg-12345678" -> null
      - name = "web-sg" -> null
      ...
    }

Plan: 0 to add, 0 to change, 3 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:

OPTIONS UTILES:
───────────────

# Destruction automatique (TRÈS DANGEREUX!)
terraform destroy -auto-approve

# Détruire une ressource spécifique
terraform destroy -target=aws_instance.web

# Avec variables
terraform destroy -var-file="dev.tfvars"

PROTECTION CONTRE LA DESTRUCTION:
──────────────────────────────────

# Dans votre ressource, ajouter:
resource "aws_db_instance" "prod" {
  # ...
  
  lifecycle {
    prevent_destroy = true  # Empêche terraform destroy
  }
}

# Si vous essayez de destroy:
# Error: Instance cannot be destroyed
# Resource aws_db_instance.prod has lifecycle.prevent_destroy set,
# but the plan calls for this resource to be destroyed.

CAS D'USAGE PRATIQUE:
─────────────────────

# Environnement de développement temporaire
# Créer le matin
terraform apply -var="environment=dev-john" -auto-approve

# Travailler toute la journée...

# Détruire le soir pour économiser
terraform destroy -var="environment=dev-john" -auto-approve


╔═══════════════════════════════════════════════════════════════════════════╗
║ terraform output                                                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

SYNTAXE:
terraform output [options] [NAME]

QUE FAIT-ELLE?
──────────────

Affiche les valeurs des outputs définis dans outputs.tf

UTILISATION:
────────────

# Afficher tous les outputs
terraform output

# Output:
# instance_id = "i-1234567890abcdef0"
# public_ip = "54.123.45.67"
# ssh_command = "ssh -i key.pem ubuntu@54.123.45.67"

# Afficher un output spécifique
terraform output public_ip
# Output: 54.123.45.67

# Format JSON (utile pour scripts)
terraform output -json

# Output:
# {
#   "instance_id": {
#     "sensitive": false,
#     "type": "string",
#     "value": "i-1234567890abcdef0"
#   },
#   "public_ip": {
#     "sensitive": false,
#     "type": "string",
#     "value": "54.123.45.67"
#   }
# }

# Format brut (sans guillemets, sans couleur)
terraform output -raw public_ip
# Output: 54.123.45.67

CAS D'USAGE: SCRIPTS D'AUTOMATISATION
──────────────────────────────────────

#!/bin/bash
# Script pour se connecter automatiquement au serveur

# Récupérer l'IP publique
SERVER_IP=$(terraform output -raw public_ip)

# Se connecter
ssh -i ~/.ssh/mykey.pem ubuntu@$SERVER_IP


# Script Python pour déployer une app
#!/usr/bin/env python3
import subprocess
import json

# Récupérer tous les outputs
result = subprocess.run(
    ['terraform', 'output', '-json'],
    capture_output=True,
    text=True
)

outputs = json.loads(result.stdout)
server_ip = outputs['public_ip']['value']

# Déployer l'application
print(f"Déploiement sur {server_ip}...")
subprocess.run([
    'scp', '-i', '~/.ssh/mykey.pem',
    'app.py', f'ubuntu@{server_ip}:/home/ubuntu/'
])


╔═══════════════════════════════════════════════════════════════════════════╗
║ terraform show                                                             ║
╚═══════════════════════════════════════════════════════════════════════════╝

SYNTAXE:
terraform show [options] [FILE]

QUE FAIT-ELLE?
──────────────

Affiche le state actuel ou un plan sauvegardé en format lisible

UTILISATION:
────────────

# Afficher le state actuel
terraform show

# Output: Affiche toutes les ressources avec leurs attributs

# Afficher un plan sauvegardé
terraform show tfplan

# Format JSON
terraform show -json

# Afficher un state spécifique
terraform show terraform.tfstate

POURQUOI C'EST UTILE?
─────────────────────

• Voir l'état actuel de toutes vos ressources
• Debugger des problèmes
• Vérifier les valeurs d'attributs
• Auditer votre infrastructure

EXEMPLE DE SORTIE:
──────────────────

# aws_instance.web[0]:
resource "aws_instance" "web" {
    ami                    = "ami-12345678"
    arn                    = "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0"
    availability_zone      = "us-east-1a"
    id                     = "i-1234567890abcdef0"
    instance_type          = "t2.micro"
    private_ip             = "10.0.1.10"
    public_ip              = "54.123.45.67"
    security_groups        = ["sg-12345678"]
    subnet_id              = "subnet-12345678"
    vpc_id                 = "vpc-12345678"
    
    root_block_device {
        volume_size = 20
        volume_type = "gp3"
    }
    
    tags = {
        "Name" = "web-server-0"
        "Project" = "mon-app"
    }
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ terraform state (commandes de gestion du state)                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

Le state est LA MÉMOIRE de Terraform. Ces commandes permettent de le manipuler.

[ATTENTION]  ATTENTION: Manipuler le state directement est DANGEREUX!
─────────────────────────────────────────────────────────────

• Faites un backup AVANT toute manipulation
• Comprenez ce que vous faites
• En production, utilisez un backend distant avec versioning

┌───────────────────────────────────────────────────────────────────────────┐
│ terraform state list - Lister les ressources                             │
└───────────────────────────────────────────────────────────────────────────┘

SYNTAXE:
terraform state list [options] [FILTER]

# Lister toutes les ressources
terraform state list

# Output:
# aws_eip.web[0]
# aws_eip.web[1]
# aws_instance.web[0]
# aws_instance.web[1]
# aws_security_group.web

# Filtrer par pattern
terraform state list aws_instance.*

# Output:
# aws_instance.web[0]
# aws_instance.web[1]

UTILITÉ:
• Voir rapidement ce qui est géré par Terraform
• Trouver l'adresse exacte d'une ressource
• Vérifier qu'une ressource est bien dans le state


┌───────────────────────────────────────────────────────────────────────────┐
│ terraform state show - Voir les détails d'une ressource                  │
└───────────────────────────────────────────────────────────────────────────┘

SYNTAXE:
terraform state show ADDRESS

# Voir les détails d'une instance
terraform state show aws_instance.web[0]

# Output: Affiche tous les attributs de cette ressource

UTILITÉ:
• Voir les attributs exacts d'une ressource
• Récupérer des IDs, ARNs, etc.
• Debugger des problèmes de référence


┌───────────────────────────────────────────────────────────────────────────┐
│ terraform state mv - Déplacer/Renommer une ressource                     │
└───────────────────────────────────────────────────────────────────────────┘

SYNTAXE:
terraform state mv SOURCE DESTINATION

QUAND L'UTILISER?
─────────────────

Vous avez renommé une ressource dans votre code et vous ne voulez pas
la détruire/recréer.

EXEMPLE:
────────

# AVANT (dans main.tf):
resource "aws_instance" "web" {
  ami = "ami-123"
  # ...
}

# Vous changez le nom:
resource "aws_instance" "application" {
  ami = "ami-123"
  # ...
}

# Sans terraform state mv, Terraform va:
# 1. Détruire aws_instance.web
# 2. Créer aws_instance.application
# [X] Perte de données! Downtime!

# Avec terraform state mv:
terraform state mv aws_instance.web aws_instance.application

# [OK] Terraform sait que c'est la même ressource renommée
# Pas de destruction, pas de recréation!

AUTRE CAS D'USAGE: Déplacer dans un module
───────────────────────────────────────────

# Déplacer une ressource vers un module
terraform state mv aws_instance.web module.ec2.aws_instance.web


┌───────────────────────────────────────────────────────────────────────────┐
│ terraform state rm - Retirer du state (sans détruire)                    │
└───────────────────────────────────────────────────────────────────────────┘

SYNTAXE:
terraform state rm ADDRESS

QUAND L'UTILISER?
─────────────────

• Vous voulez que Terraform ARRÊTE de gérer une ressource
• Mais vous ne voulez PAS la détruire
• La ressource continue d'exister dans le cloud

EXEMPLE:
────────

# Situation: Vous avez une base de données gérée par Terraform
# Vous voulez la garder mais la gérer manuellement maintenant

# 1. Retirer du state
terraform state rm aws_db_instance.main

# 2. Supprimer le code Terraform
# (supprimer le bloc resource "aws_db_instance" "main" {...})

# 3. terraform plan ne verra plus cette ressource
# 4. La base de données continue de fonctionner normalement

ATTENTION:
──────────
Si vous faites juste "rm" sans supprimer le code:
• terraform plan verra que la ressource n'existe pas dans le state
• Il proposera de la RECRÉER
• [ATTENTION]  Risque de conflit (nom déjà pris, etc.)


┌───────────────────────────────────────────────────────────────────────────┐
│ terraform state pull/push - Télécharger/Uploader le state                │
└───────────────────────────────────────────────────────────────────────────┘

SYNTAXE:
terraform state pull > backup.tfstate
terraform state push backup.tfstate

QUAND L'UTILISER?
─────────────────

• Faire un backup manuel du state
• Restaurer un state d'un backup
• Migrer entre backends

EXEMPLE: BACKUP
───────────────

# Backup quotidien
#!/bin/bash
DATE=$(date +%Y%m%d)
terraform state pull > "backups/terraform-${DATE}.tfstate"

EXEMPLE: RESTAURATION
─────────────────────

# DANGER: Ceci écrase le state actuel!
terraform state push backup-20231215.tfstate


┌───────────────────────────────────────────────────────────────────────────┐
│ terraform state replace-provider - Changer de provider                   │
└───────────────────────────────────────────────────────────────────────────┘

SYNTAXE:
terraform state replace-provider FROM TO

QUAND L'UTILISER?
─────────────────

Le provider change de namespace (rare mais ça arrive)

EXEMPLE:
────────

# HashiCorp a changé le namespace des providers officiels
terraform state replace-provider \
  registry.terraform.io/-/aws \
  registry.terraform.io/hashicorp/aws


═══════════════════════════════════════════════════════════════════════════════
[IDEE] PARTIE 5: VARIABLES AVANCÉES - MAÎTRISER LA FLEXIBILITÉ
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ HIÉRARCHIE DES SOURCES DE VARIABLES                                       ║
╚═══════════════════════════════════════════════════════════════════════════╝

Terraform peut recevoir des valeurs de variables de PLUSIEURS sources.
Voici l'ORDRE DE PRIORITÉ (du plus faible au plus fort):

┌────────────────────────────────────────────────────────────────────────┐
│ 1. Valeur par défaut (default dans variables.tf)          [Priorité 1] │
│ 2. Variables d'environnement (TF_VAR_*)                   [Priorité 2] │
│ 3. Fichier terraform.tfvars                               [Priorité 3] │
│ 4. Fichier *.auto.tfvars                                  [Priorité 4] │
│ 5. Fichier -var-file="custom.tfvars"                      [Priorité 5] │
│ 6. Option -var="key=value" en ligne de commande           [Priorité 6] │
└────────────────────────────────────────────────────────────────────────┘

EXEMPLE CONCRET:
────────────────

# variables.tf
variable "instance_type" {
  default = "t2.micro"  # Priorité 1
}

# Variable d'environnement
export TF_VAR_instance_type="t2.small"  # Priorité 2 (écrase le default)

# terraform.tfvars
instance_type = "t2.medium"  # Priorité 3 (écrase l'env var)

# Ligne de commande
terraform apply -var="instance_type=t2.large"  # Priorité 6 (GAGNE!)

# Résultat final: t2.large


╔═══════════════════════════════════════════════════════════════════════════╗
║ VARIABLES D'ENVIRONNEMENT                                                  ║
╚═══════════════════════════════════════════════════════════════════════════╝

SYNTAXE:
export TF_VAR_nom_variable="valeur"

POURQUOI UTILISER DES VARIABLES D'ENVIRONNEMENT?
─────────────────────────────────────────────────

1. SECRETS
   • Ne pas mettre de mots de passe dans des fichiers
   • Injecter depuis un vault ou un secret manager

2. CI/CD
   • Configurer depuis GitLab CI / GitHub Actions
   • Chaque environnement a ses propres variables

3. DÉVELOPPEMENT LOCAL
   • Chaque développeur a ses propres valeurs
   • Ne pas commiter des configs personnelles

EXEMPLE:
────────

# Dans variables.tf
variable "db_password" {
  type      = string
  sensitive = true
}

# NE PAS FAIRE:
# terraform.tfvars
# db_password = "mon_super_password"  # [X] Va dans Git!

# À LA PLACE:
export TF_VAR_db_password="mon_super_password"
terraform apply
# [OK] Le password n'est jamais dans un fichier


# EXEMPLE CI/CD (GitHub Actions):
# .github/workflows/deploy.yml
- name: Terraform Apply
  env:
    TF_VAR_db_password: ${{ secrets.DB_PASSWORD }}
    TF_VAR_aws_region: "eu-west-1"
  run: terraform apply -auto-approve


╔═══════════════════════════════════════════════════════════════════════════╗
║ FICHIERS DE VARIABLES MULTIPLES                                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

Vous pouvez avoir plusieurs fichiers .tfvars pour différents environnements.

STRUCTURE RECOMMANDÉE:
──────────────────────

mon-projet/
├── main.tf
├── variables.tf
├── terraform.tfvars        # Variables communes (auto-chargé)
├── dev.tfvars              # Variables dev (manuel)
├── staging.tfvars          # Variables staging (manuel)
├── prod.tfvars             # Variables prod (manuel)
└── secrets.auto.tfvars     # Secrets (auto-chargé, .gitignored)

POURQUOI CETTE STRUCTURE?
─────────────────────────

• terraform.tfvars: Chargé automatiquement, contient les valeurs par défaut
• *.auto.tfvars: Chargés automatiquement (ex: secrets locaux)
• dev/staging/prod.tfvars: Chargés manuellement selon l'environnement

UTILISATION:
────────────

# Déployer en dev
terraform apply -var-file="dev.tfvars"

# Déployer en staging
terraform apply -var-file="staging.tfvars"

# Déployer en prod
terraform apply -var-file="prod.tfvars"

EXEMPLE DE CONTENU:
───────────────────

# terraform.tfvars (valeurs communes)
project_name = "mon-app"
tags = {
  Project = "mon-app"
  Team    = "backend"
}

# dev.tfvars
environment    = "dev"
instance_type  = "t2.micro"
instance_count = 1
db_size        = 20

# prod.tfvars
environment    = "prod"
instance_type  = "t3.large"
instance_count = 3
db_size        = 100


╔═══════════════════════════════════════════════════════════════════════════╗
║ VARIABLES COMPLEXES - TYPES AVANCÉS                                       ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ TYPE: LIST                                                                │
└───────────────────────────────────────────────────────────────────────────┘

# variables.tf
variable "availability_zones" {
  description = "Liste des AZs à utiliser"
  type        = list(string)
  default     = ["us-east-1a", "us-east-1b"]
}

# Utilisation dans main.tf
resource "aws_subnet" "public" {
  count             = length(var.availability_zones)
  availability_zone = var.availability_zones[count.index]
  # ...
}

FONCTIONS UTILES SUR LES LISTES:
─────────────────────────────────

# Longueur d'une liste
length(var.availability_zones)  # 2

# Premier élément
var.availability_zones[0]  # "us-east-1a"

# Concaténer des listes
concat(var.public_subnets, var.private_subnets)

# Vérifier si un élément est dans la liste
contains(var.availability_zones, "us-east-1c")  # false

# Créer une liste de N éléments identiques
["instance-${count.index}" for i in range(3)]
# ["instance-0", "instance-1", "instance-2"]


┌───────────────────────────────────────────────────────────────────────────┐
│ TYPE: MAP                                                                 │
└───────────────────────────────────────────────────────────────────────────┘

# variables.tf
variable "instance_types" {
  description = "Types d'instance par environnement"
  type        = map(string)
  default = {
    dev     = "t2.micro"
    staging = "t2.small"
    prod    = "t3.large"
  }
}

# Utilisation
resource "aws_instance" "web" {
  instance_type = var.instance_types[terraform.workspace]
  # Si workspace = "prod", alors instance_type = "t3.large"
}

FONCTIONS UTILES SUR LES MAPS:
──────────────────────────────

# Récupérer une valeur
var.instance_types["prod"]  # "t3.large"

# Récupérer avec valeur par défaut si clé n'existe pas
lookup(var.instance_types, "test", "t2.micro")
# Si "test" n'existe pas, retourne "t2.micro"

# Fusionner plusieurs maps
merge(var.common_tags, { Environment = "prod" })

# Lister les clés
keys(var.instance_types)  # ["dev", "staging", "prod"]

# Lister les valeurs
values(var.instance_types)  # ["t2.micro", "t2.small", "t3.large"]


┌───────────────────────────────────────────────────────────────────────────┐
│ TYPE: OBJECT (Structure complexe)                                        │
└───────────────────────────────────────────────────────────────────────────┘

# variables.tf
variable "database" {
  description = "Configuration de la base de données"
  type = object({
    engine            = string
    version           = string
    instance_class    = string
    allocated_storage = number
    multi_az          = bool
    backup_retention  = number
  })
}

# terraform.tfvars
database = {
  engine            = "postgres"
  version           = "15.4"
  instance_class    = "db.t3.micro"
  allocated_storage = 20
  multi_az          = false
  backup_retention  = 7
}

# Utilisation dans main.tf
resource "aws_db_instance" "main" {
  engine               = var.database.engine
  engine_version       = var.database.version
  instance_class       = var.database.instance_class
  allocated_storage    = var.database.allocated_storage
  multi_az             = var.database.multi_az
  backup_retention_period = var.database.backup_retention
}

AVANTAGES:
──────────
• Grouper des configs liées
• Validation de structure automatique
• Plus maintenable qu'une liste de variables séparées


┌───────────────────────────────────────────────────────────────────────────┐
│ TYPE: LIST OF OBJECTS (Liste de structures)                              │
└───────────────────────────────────────────────────────────────────────────┘

Très utile pour créer plusieurs ressources similaires avec des configs différentes.

# variables.tf
variable "security_group_rules" {
  description = "Règles de security group"
  type = list(object({
    description = string
    from_port   = number
    to_port     = number
    protocol    = string
    cidr_blocks = list(string)
  }))
  
  default = [
    {
      description = "HTTP depuis Internet"
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      description = "HTTPS depuis Internet"
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      description = "SSH depuis bureau"
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
      cidr_blocks = ["1.2.3.4/32"]
    }
  ]
}

# Utilisation avec dynamic blocks
resource "aws_security_group" "web" {
  name = "web-sg"
  
  dynamic "ingress" {
    for_each = var.security_group_rules
    content {
      description = ingress.value.description
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
}

POURQUOI C'EST PUISSANT?
────────────────────────

• Une seule variable pour toutes les règles
• Facile d'ajouter/supprimer des règles
• Pas de duplication de code
• Validation automatique de la structure


╔═══════════════════════════════════════════════════════════════════════════╗
║ VALIDATION DE VARIABLES                                                   ║
╚═══════════════════════════════════════════════════════════════════════════╝

La validation vous permet de vérifier que les valeurs sont valides AVANT
de faire des appels API coûteux ou dangereux.

VALIDATION SIMPLE:
──────────────────

variable "environment" {
  description = "Environnement de déploiement"
  type        = string
  
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "environment doit être dev, staging ou prod."
  }
}

# Si vous essayez:
# terraform apply -var="environment=test"
# Error: Invalid value for variable
# environment doit être dev, staging ou prod.


VALIDATION AVEC REGEX:
──────────────────────

variable "project_name" {
  description = "Nom du projet (lettres minuscules et tirets uniquement)"
  type        = string
  
  validation {
    condition     = can(regex("^[a-z][a-z0-9-]*$", var.project_name))
    error_message = "project_name doit commencer par une lettre et contenir uniquement des lettres minuscules, chiffres et tirets."
  }
}

# Valide: "mon-projet-2023"
# Invalide: "Mon-Projet" (majuscule)
# Invalide: "123-projet" (commence par un chiffre)


VALIDATION DE PLAGE:
────────────────────

variable "instance_count" {
  description = "Nombre d'instances"
  type        = number
  
  validation {
    condition     = var.instance_count >= 1 && var.instance_count <= 10
    error_message = "instance_count doit être entre 1 et 10."
  }
}


VALIDATION COMPLEXE:
────────────────────

variable "db_storage" {
  description = "Taille du stockage DB en GB"
  type        = number
  
  validation {
    # Le stockage doit être un multiple de 10
    condition     = var.db_storage % 10 == 0
    error_message = "db_storage doit être un multiple de 10 (20, 30, 40, etc.)."
  }
  
  validation {
    # Entre 20 et 1000 GB
    condition     = var.db_storage >= 20 && var.db_storage <= 1000
    error_message = "db_storage doit être entre 20 et 1000 GB."
  }
}

# Vous pouvez avoir PLUSIEURS validations!


VALIDATION AVEC PLUSIEURS CONDITIONS:
──────────────────────────────────────

variable "tags" {
  description = "Tags pour les ressources"
  type        = map(string)
  
  validation {
    # Vérifier que les tags obligatoires sont présents
    condition = (
      contains(keys(var.tags), "Environment") &&
      contains(keys(var.tags), "Project") &&
      contains(keys(var.tags), "Owner")
    )
    error_message = "Les tags Environment, Project et Owner sont obligatoires."
  }
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ VARIABLES SENSIBLES - SÉCURITÉ                                            ║
╚═══════════════════════════════════════════════════════════════════════════╝

POURQUOI MARQUER UNE VARIABLE COMME SENSIBLE?
──────────────────────────────────────────────

variable "db_password" {
  type      = string
  sensitive = true  # <- Important!
}

EFFETS:
───────

1. PAS AFFICHÉ DANS LES LOGS
   ┌────────────────────────────────────────────────────┐
   │ # Sans sensitive = true:                           │
   │ db_password = "mon_super_password"                 │
   │                                                     │
   │ # Avec sensitive = true:                           │
   │ db_password = <sensitive>                          │
   └────────────────────────────────────────────────────┘

2. PAS AFFICHÉ DANS LE PLAN
   terraform plan n'affichera pas la valeur

3. PAS AFFICHÉ DANS LES OUTPUTS (sauf si vous demandez explicitement)

[ATTENTION]  LIMITATION IMPORTANTE:
──────────────────────────

sensitive = true CACHE la valeur dans les logs, MAIS:
• La valeur est TOUJOURS en clair dans le state!
• La valeur peut être visible dans les logs du provider (AWS, etc.)
• Ce n'est PAS un chiffrement!

SOLUTION POUR VRAIS SECRETS:
────────────────────────────

Utiliser un gestionnaire de secrets externe:

# AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "prod/db/password"
}

locals {
  db_password = jsondecode(data.aws_secretsmanager_secret_version.db_password.secret_string)["password"]
}

resource "aws_db_instance" "main" {
  password = local.db_password  # Récupéré depuis Secrets Manager
}

AVANTAGES:
• Rotation automatique des mots de passe
• Audit trail (qui a accédé au secret?)
• Chiffrement au repos
• Ne jamais stocker le secret dans Git ou le state


═══════════════════════════════════════════════════════════════════════════════
[CONSTRUCTION] PARTIE 6: RESSOURCES - CRÉER VOTRE INFRASTRUCTURE
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ ANATOMIE D'UN BLOC RESOURCE                                               ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌────────────────────────────────────────────────────────────────────────┐
│                                                                         │
│  resource "TYPE" "NOM" {                                               │
│    argument1 = valeur1                                                 │
│    argument2 = valeur2                                                 │
│                                                                         │
│    nested_block {                                                      │
│      nested_argument = valeur                                          │
│    }                                                                    │
│                                                                         │
│    meta-arguments                                                      │
│  }                                                                      │
│                                                                         │
│  ^         ^      ^                                                    │
│  keyword   type   local name                                           │
│                                                                         │
└────────────────────────────────────────────────────────────────────────┘

COMPOSANTS:
───────────

1. TYPE
   • Le type de ressource (ex: aws_instance, google_compute_instance)
   • Défini par le provider
   • Format: PROVIDER_RESOURCE

2. NOM LOCAL
   • Nom que VOUS choisissez
   • Utilisé pour référencer la ressource
   • Doit être unique dans votre configuration

3. ARGUMENTS
   • Configuration de la ressource
   • Spécifiques à chaque type de ressource
   • Certains sont obligatoires, d'autres optionnels

4. META-ARGUMENTS
   • Arguments spéciaux disponibles pour TOUTES les ressources
   • count, for_each, depends_on, lifecycle, provider

EXEMPLE COMPLET:
────────────────

resource "aws_instance" "web_server" {
  # Arguments standards
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  key_name      = "mykey"
  
  # Nested block
  root_block_device {
    volume_size = 20
    volume_type = "gp3"
  }
  
  # Meta-arguments
  count = 2
  
  depends_on = [
    aws_security_group.web
  ]
  
  lifecycle {
    create_before_destroy = true
  }
  
  tags = {
    Name = "web-server-${count.index}"
  }
}

RÉFÉRENCER CETTE RESSOURCE:
───────────────────────────

# Dans d'autres ressources ou outputs
aws_instance.web_server[0].id          # ID de la première instance
aws_instance.web_server[1].public_ip   # IP de la deuxième
aws_instance.web_server[*].id          # Liste de tous les IDs


╔═══════════════════════════════════════════════════════════════════════════╗
║ META-ARGUMENTS - Les Options Universelles                                 ║
╚═══════════════════════════════════════════════════════════════════════════╝

Les meta-arguments sont disponibles pour TOUTES les ressources, peu importe
le provider.

┌───────────────────────────────────────────────────────────────────────────┐
│ count - Créer plusieurs copies d'une ressource                           │
└───────────────────────────────────────────────────────────────────────────┘

UTILISATION:
────────────

resource "aws_instance" "web" {
  count = 3  # Créer 3 instances
  
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  
  tags = {
    Name = "web-${count.index}"  # web-0, web-1, web-2
  }
}

COMMENT ÇA MARCHE?
──────────────────

• count.index: Index de l'itération (0, 1, 2, ...)
• Terraform crée count ressources
• Référence: aws_instance.web[0], aws_instance.web[1], etc.

AVEC UNE VARIABLE:
──────────────────

variable "instance_count" {
  default = 2
}

resource "aws_instance" "web" {
  count = var.instance_count
  # ...
}

COUNT CONDITIONNEL (créer ou pas):
───────────────────────────────────

variable "create_instance" {
  type    = bool
  default = true
}

resource "aws_instance" "web" {
  count = var.create_instance ? 1 : 0
  # Si true: crée 1 instance
  # Si false: crée 0 instance (= n'en crée pas)
  
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

UTILISATION PRATIQUE:
─────────────────────

# Créer des subnets dans plusieurs AZs
variable "availability_zones" {
  default = ["us-east-1a", "us-east-1b", "us-east-1c"]
}

resource "aws_subnet" "public" {
  count = length(var.availability_zones)
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index}.0/24"
  availability_zone = var.availability_zones[count.index]
  
  tags = {
    Name = "public-subnet-${count.index}"
  }
}


┌───────────────────────────────────────────────────────────────────────────┐
│ for_each - Créer des ressources à partir d'une map ou set                │
└───────────────────────────────────────────────────────────────────────────┘

for_each est plus flexible que count, surtout pour des ressources avec
des configurations différentes.

DIFFÉRENCE avec count:
──────────────────────

• count: Liste numérique (0, 1, 2...)
  Problème: Si vous supprimez l'élément 1, tous les index changent!
  
• for_each: Map ou set avec des clés stables
  Avantage: Supprimer "staging" ne touche pas "dev" et "prod"

EXEMPLE AVEC MAP:
─────────────────

variable "instances" {
  type = map(object({
    instance_type = string
    ami           = string
  }))
  
  default = {
    web = {
      instance_type = "t2.micro"
      ami           = "ami-web"
    }
    api = {
      instance_type = "t2.small"
      ami           = "ami-api"
    }
    worker = {
      instance_type = "t2.medium"
      ami           = "ami-worker"
    }
  }
}

resource "aws_instance" "servers" {
  for_each = var.instances
  
  ami           = each.value.ami
  instance_type = each.value.instance_type
  
  tags = {
    Name = "server-${each.key}"  # server-web, server-api, server-worker
    Role = each.key
  }
}

RÉFÉRENCES:
───────────

each.key    # Clé courante (ex: "web", "api", "worker")
each.value  # Valeur courante (l'objet complet)

# Référencer une ressource spécifique:
aws_instance.servers["web"].id       # Instance web
aws_instance.servers["api"].public_ip # IP de l'instance api

# Tous les IDs:
values(aws_instance.servers)[*].id


EXEMPLE AVEC SET:
─────────────────

variable "user_names" {
  type    = set(string)
  default = ["alice", "bob", "charlie"]
}

resource "aws_iam_user" "users" {
  for_each = var.user_names
  
  name = each.key  # Pour un set, each.key == each.value
  
  tags = {
    Department = "Engineering"
  }
}


QUAND UTILISER count vs for_each?
──────────────────────────────────

[OK] count:
• Liste simple et ordonnée
• Toutes les ressources sont identiques (sauf l'index)
• Exemple: 5 instances web identiques

[OK] for_each:
• Configuration différente pour chaque ressource
• Besoin de référencer par nom plutôt que par index
• Exemple: Créer des utilisateurs IAM avec des rôles différents


┌───────────────────────────────────────────────────────────────────────────┐
│ depends_on - Forcer l'ordre de création                                  │
└───────────────────────────────────────────────────────────────────────────┘

POURQUOI?
─────────

Terraform détecte AUTOMATIQUEMENT les dépendances entre ressources
quand vous utilisez des références.

# Dépendance IMPLICITE (automatique)
resource "aws_security_group" "web" {
  name = "web-sg"
}

resource "aws_instance" "web" {
  ami           = "ami-123"
  instance_type = "t2.micro"
  
  # Terraform voit cette référence
  vpc_security_group_ids = [aws_security_group.web.id]
  
  # Il sait qu'il doit créer le security group AVANT l'instance
}

MAIS PARFOIS, la dépendance n'est PAS visible dans le code.
C'est là qu'on utilise depends_on.

EXEMPLE:
────────

resource "aws_iam_role" "ec2_role" {
  name = "ec2-role"
  # ...
}

resource "aws_iam_role_policy_attachment" "ec2_policy" {
  role       = aws_iam_role.ec2_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}

resource "aws_instance" "web" {
  ami           = "ami-123"
  instance_type = "t2.micro"
  iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
  
  # Problème: L'instance peut démarrer AVANT que la policy soit attachée!
  # L'instance n'aura pas les permissions S3 au démarrage.
  
  # Solution:
  depends_on = [
    aws_iam_role_policy_attachment.ec2_policy
  ]
}

AUTRE CAS: APIS EXTERNES
────────────────────────

resource "null_resource" "register_domain" {
  provisioner "local-exec" {
    command = "curl -X POST https://api.example.com/register"
  }
}

resource "aws_route53_record" "www" {
  # Attendre que le domaine soit enregistré
  depends_on = [null_resource.register_domain]
  
  zone_id = aws_route53_zone.main.zone_id
  name    = "www"
  type    = "A"
  records = [aws_instance.web.public_ip]
}

[ATTENTION]  ATTENTION:
──────────────

• N'utilisez depends_on que quand NÉCESSAIRE
• Préférez les dépendances implicites (références)
• depends_on ralentit le parallélisme de Terraform


┌───────────────────────────────────────────────────────────────────────────┐
│ lifecycle - Contrôler le cycle de vie des ressources                     │
└───────────────────────────────────────────────────────────────────────────┘

lifecycle permet de contrôler COMMENT Terraform gère vos ressources.

┌─────────────────────────────────────────────────────────────────┐
│ lifecycle {                                                     │
│   create_before_destroy = true                                  │
│   prevent_destroy       = true                                  │
│   ignore_changes        = [attribute1, attribute2]              │
│   replace_triggered_by  = [resource.id]                         │
│ }                                                                │
└─────────────────────────────────────────────────────────────────┘

1. create_before_destroy
   ─────────────────────

Par défaut: Terraform DÉTRUIT puis CRÉE
Avec cette option: Terraform CRÉE puis DÉTRUIT

POURQUOI?
• Zero downtime
• La nouvelle ressource est prête avant de supprimer l'ancienne

EXEMPLE:
────────

resource "aws_instance" "web" {
  ami           = "ami-new-version"
  instance_type = "t2.micro"
  
  lifecycle {
    create_before_destroy = true
  }
}

# Quand vous changez l'AMI:
# 1. Terraform crée une nouvelle instance avec la nouvelle AMI
# 2. Attend qu'elle soit prête
# 3. Supprime l'ancienne instance
# [OK] Pas de downtime!

2. prevent_destroy
   ───────────────

Empêche la destruction accidentelle de ressources critiques.

EXEMPLE:
────────

resource "aws_db_instance" "production" {
  identifier = "prod-db"
  engine     = "postgres"
  # ... autres paramètres ...
  
  lifecycle {
    prevent_destroy = true  # Protection anti-destruction
  }
}

# Si vous essayez:
terraform destroy

# Erreur:
# Error: Instance cannot be destroyed
# Resource aws_db_instance.production has lifecycle.prevent_destroy set,
# but the plan calls for this resource to be destroyed.

QUAND UTILISER?
• Bases de données de production
• State buckets S3
• Ressources irremplaçables

3. ignore_changes
   ──────────────

Terraform ignore les changements sur certains attributs.

POURQUOI?
• Certaines ressources sont modifiées par des outils externes
• Vous voulez que Terraform ne les "remette pas" à leur valeur d'origine

EXEMPLE 1: Tags modifiés manuellement
──────────────────────────────────────

resource "aws_instance" "web" {
  ami           = "ami-123"
  instance_type = "t2.micro"
  
  tags = {
    Name = "web-server"
    Managed = "terraform"
  }
  
  lifecycle {
    ignore_changes = [
      tags,  # Ignore tous les changements de tags
    ]
  }
}

# Si quelqu'un ajoute manuellement des tags dans la console AWS,
# Terraform ne les supprimera pas au prochain apply.

EXEMPLE 2: Auto Scaling modifie la taille
──────────────────────────────────────────

resource "aws_autoscaling_group" "web" {
  min_size = 2
  max_size = 10
  desired_capacity = 2
  
  lifecycle {
    ignore_changes = [
      desired_capacity,  # Auto Scaling change cette valeur automatiquement
    ]
  }
}

EXEMPLE 3: Ignorer TOUS les changements
────────────────────────────────────────

lifecycle {
  ignore_changes = all
}

# Terraform ne touchera JAMAIS plus à cette ressource
# (sauf pour la créer initialement)

4. replace_triggered_by
   ────────────────────

(Terraform 1.2+) Forcer le remplacement quand une autre ressource change.

EXEMPLE:
────────

resource "aws_ami" "app" {
  # ... configuration de l'AMI personnalisée ...
}

resource "aws_instance" "web" {
  ami           = aws_ami.app.id
  instance_type = "t2.micro"
  
  lifecycle {
    replace_triggered_by = [
      aws_ami.app.id  # Recréer l'instance si l'AMI change
    ]
  }
}


┌───────────────────────────────────────────────────────────────────────────┐
│ provider - Utiliser un provider spécifique                               │
└───────────────────────────────────────────────────────────────────────────┘

Utile quand vous avez plusieurs configurations du même provider.

EXEMPLE: Déployer dans plusieurs régions AWS
─────────────────────────────────────────────

# Provider principal (us-east-1)
provider "aws" {
  region = "us-east-1"
}

# Provider secondaire (eu-west-1)
provider "aws" {
  alias  = "europe"
  region = "eu-west-1"
}

# Ressource aux US (provider par défaut)
resource "aws_instance" "us_server" {
  ami           = "ami-us"
  instance_type = "t2.micro"
}

# Ressource en Europe (provider spécifique)
resource "aws_instance" "eu_server" {
  provider = aws.europe  # <- Utilise le provider "europe"
  
  ami           = "ami-eu"
  instance_type = "t2.micro"
}

AUTRE EXEMPLE: Plusieurs comptes AWS
─────────────────────────────────────

provider "aws" {
  alias  = "dev"
  region = "us-east-1"
  
  assume_role {
    role_arn = "arn:aws:iam::111111111111:role/TerraformRole"
  }
}

provider "aws" {
  alias  = "prod"
  region = "us-east-1"
  
  assume_role {
    role_arn = "arn:aws:iam::222222222222:role/TerraformRole"
  }
}

resource "aws_instance" "dev_server" {
  provider = aws.dev
  # ... crée dans le compte dev ...
}

resource "aws_instance" "prod_server" {
  provider = aws.prod
  # ... crée dans le compte prod ...
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ DYNAMIC BLOCKS - Générer des blocs répétitifs                             ║
╚═══════════════════════════════════════════════════════════════════════════╝

PROBLÈME À RÉSOUDRE:
────────────────────

Imaginons que vous voulez créer un security group avec 10 règles.
Sans dynamic blocks:

resource "aws_security_group" "web" {
  name = "web-sg"
  
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["1.2.3.4/32"]
  }
  
  # ... 7 autres règles identiques ...
  
  # [X] Beaucoup de duplication!
}

SOLUTION: dynamic blocks
────────────────────────

variable "ingress_rules" {
  type = list(object({
    description = string
    from_port   = number
    to_port     = number
    protocol    = string
    cidr_blocks = list(string)
  }))
  
  default = [
    {
      description = "HTTP"
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      description = "HTTPS"
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      description = "SSH"
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
      cidr_blocks = ["1.2.3.4/32"]
    }
  ]
}

resource "aws_security_group" "web" {
  name = "web-sg"
  
  dynamic "ingress" {
    for_each = var.ingress_rules
    
    content {
      description = ingress.value.description
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
}

# [OK] Plus de duplication!
# [OK] Facile d'ajouter/supprimer des règles (modifier la variable)
# [OK] Même code pour 3 ou 100 règles

SYNTAXE DU DYNAMIC BLOCK:
─────────────────────────

dynamic "NOM_DU_BLOC" {
  for_each = LISTE_OU_MAP
  
  content {
    # Configuration utilisant NOM_DU_BLOC.value
  }
  
  # Optionnel:
  iterator = nom_custom  # Par défaut = NOM_DU_BLOC
}

EXEMPLE AVEC ITERATOR CUSTOM:
──────────────────────────────

resource "aws_security_group" "web" {
  name = "web-sg"
  
  dynamic "ingress" {
    for_each = var.ingress_rules
    iterator = rule  # Utiliser "rule" au lieu de "ingress"
    
    content {
      description = rule.value.description
      from_port   = rule.value.from_port
      to_port     = rule.value.to_port
      protocol    = rule.value.protocol
      cidr_blocks = rule.value.cidr_blocks
    }
  }
}


═══════════════════════════════════════════════════════════════════════════════
[GRAPHIQUE] PARTIE 7: DATA SOURCES - RÉCUPÉRER DES INFORMATIONS EXISTANTES
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ QU'EST-CE QU'UN DATA SOURCE?                                              ║
╚═══════════════════════════════════════════════════════════════════════════╝

Un DATA SOURCE permet de LIRE des informations depuis le cloud provider,
SANS créer de nouvelles ressources.

DIFFÉRENCE: resource vs data
────────────────────────────────

┌──────────────────────────────────────────────────────────────────────┐
│ resource                        │ data                              │
├─────────────────────────────────┼───────────────────────────────────┤
│ CRÉE une nouvelle ressource     │ LIT une ressource existante       │
│ Géré par Terraform               │ Géré en dehors de Terraform       │
│ Peut être modifié/détruit       │ Lecture seule                     │
│ Exemple: Créer une VM           │ Exemple: Lire l'ID d'une AMI      │
└──────────────────────────────────────────────────────────────────────┘

SYNTAXE:
────────

data "TYPE" "NOM" {
  # Filtres pour trouver la ressource
  filter {
    name   = "..."
    values = ["..."]
  }
}

# Référence: data.TYPE.NOM.ATTRIBUT

EXEMPLES COURANTS:
──────────────────

╔═══════════════════════════════════════════════════════════════════════════╗
║ DATA SOURCE: aws_ami - Trouver une AMI                                    ║
╚═══════════════════════════════════════════════════════════════════════════╝

PROBLÈME:
─────────
Les IDs d'AMI changent selon:
• La région AWS
• La version de l'OS
• Le type de virtualisation

[X] MAUVAISE PRATIQUE:
ami = "ami-0c55b159cbfafe1f0"  # Hardcodé, valide seulement en us-east-1

[OK] BONNE PRATIQUE:
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]  # Canonical (Ubuntu)
  
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }
  
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id  # <- Utilisation
  instance_type = "t2.micro"
}

COMMENT ÇA MARCHE?
──────────────────

1. Terraform appelle l'API AWS
2. Liste toutes les AMIs qui correspondent aux filtres
3. Prend la plus récente (most_recent = true)
4. Retourne son ID

AVANTAGES:
──────────
• Fonctionne dans N'IMPORTE QUELLE région
• Toujours la dernière version d'Ubuntu
• Pas besoin de maintenir une liste d'AMI IDs

AUTRES EXEMPLES D'AMI:
──────────────────────

# Amazon Linux 2
data "aws_ami" "amazon_linux_2" {
  most_recent = true
  owners      = ["amazon"]
  
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

# Amazon Linux 2023
data "aws_ami" "amazon_linux_2023" {
  most_recent = true
  owners      = ["amazon"]
  
  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

# CentOS
data "aws_ami" "centos" {
  most_recent = true
  owners      = ["125523088429"]  # CentOS official
  
  filter {
    name   = "name"
    values = ["CentOS Stream 9 x86_64*"]
  }
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ DATA SOURCE: aws_availability_zones - Lister les AZs                      ║
╚═══════════════════════════════════════════════════════════════════════════╝

UTILITÉ:
────────
Créer des ressources dans TOUTES les zones de disponibilité disponibles,
sans hardcoder les noms.

data "aws_availability_zones" "available" {
  state = "available"  # Seulement les AZs actives
}

# Créer un subnet dans chaque AZ
resource "aws_subnet" "public" {
  count = length(data.aws_availability_zones.available.names)
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]
  
  tags = {
    Name = "public-${data.aws_availability_zones.available.names[count.index]}"
  }
}

ATTRIBUTS DISPONIBLES:
──────────────────────

data.aws_availability_zones.available.names
# ["us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d", "us-east-1e", "us-east-1f"]

data.aws_availability_zones.available.zone_ids
# ["use1-az1", "use1-az2", "use1-az3", ...]

FILTRER LES AZs:
────────────────

# Exclure certaines AZs (utile si certaines n'ont pas tous les services)
data "aws_availability_zones" "available" {
  state = "available"
  
  filter {
    name   = "opt-in-status"
    values = ["opt-in-not-required"]
  }
  
  # Exclure us-east-1e (exemple)
  exclude_names = ["us-east-1e"]
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ DATA SOURCE: aws_vpc - Récupérer un VPC existant                          ║
╚═══════════════════════════════════════════════════════════════════════════╝

UTILITÉ:
────────
Vous voulez créer des ressources dans un VPC qui existe déjà
(créé manuellement ou par une autre stack Terraform).

# Récupérer le VPC par défaut
data "aws_vpc" "default" {
  default = true
}

# Récupérer un VPC par tag
data "aws_vpc" "main" {
  tags = {
    Name = "production-vpc"
  }
}

# Récupérer un VPC par ID
data "aws_vpc" "main" {
  id = "vpc-12345678"
}

# Utilisation
resource "aws_security_group" "web" {
  vpc_id = data.aws_vpc.main.id
  # ...
}

ATTRIBUTS DISPONIBLES:
──────────────────────

data.aws_vpc.main.id              # vpc-12345678
data.aws_vpc.main.cidr_block      # 10.0.0.0/16
data.aws_vpc.main.arn             # arn:aws:ec2:...
data.aws_vpc.main.enable_dns_hostnames  # true/false


╔═══════════════════════════════════════════════════════════════════════════╗
║ DATA SOURCE: aws_subnet_ids - Lister les subnets d'un VPC                 ║
╚═══════════════════════════════════════════════════════════════════════════╝

data "aws_vpc" "selected" {
  tags = {
    Name = "production-vpc"
  }
}

data "aws_subnets" "private" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.selected.id]
  }
  
  # Filtrer par tag
  tags = {
    Tier = "private"
  }
}

# Créer des ressources dans ces subnets
resource "aws_instance" "web" {
  count = length(data.aws_subnets.private.ids)
  
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t2.micro"
  subnet_id     = data.aws_subnets.private.ids[count.index]
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ DATA SOURCE: aws_secretsmanager_secret - Lire des secrets                 ║
╚═══════════════════════════════════════════════════════════════════════════╝

UTILITÉ:
────────
Récupérer des secrets stockés dans AWS Secrets Manager
(plutôt que de les mettre dans des variables Terraform).

# 1. Récupérer la référence au secret
data "aws_secretsmanager_secret" "db_password" {
  name = "production/database/password"
}

# 2. Récupérer la valeur du secret
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = data.aws_secretsmanager_secret.db_password.id
}

# 3. Parser le JSON et utiliser
locals {
  db_credentials = jsondecode(
    data.aws_secretsmanager_secret_version.db_password.secret_string
  )
}

resource "aws_db_instance" "main" {
  engine   = "postgres"
  username = local.db_credentials.username
  password = local.db_credentials.password
  # ...
}

AVANTAGES:
──────────
[OK] Pas de secrets dans le code
[OK] Rotation automatique des mots de passe
[OK] Audit trail (qui a accédé au secret?)
[OK] Pas de secrets dans Git


╔═══════════════════════════════════════════════════════════════════════════╗
║ DATA SOURCE: aws_caller_identity - Infos sur le compte AWS                ║
╚═══════════════════════════════════════════════════════════════════════════╝

UTILITÉ:
────────
Obtenir des informations sur le compte AWS actuel.

data "aws_caller_identity" "current" {}

output "account_id" {
  value = data.aws_caller_identity.current.account_id
}

output "caller_arn" {
  value = data.aws_caller_identity.current.arn
}

output "caller_user" {
  value = data.aws_caller_identity.current.user_id
}

UTILISATION PRATIQUE:
─────────────────────

# Construire des ARNs dynamiquement
locals {
  account_id = data.aws_caller_identity.current.account_id
  region     = data.aws_region.current.name
  
  s3_bucket_arn = "arn:aws:s3:::my-bucket-${local.account_id}"
  lambda_role_arn = "arn:aws:iam::${local.account_id}:role/lambda-role"
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ DATA SOURCE: aws_region - Région actuelle                                 ║
╚═══════════════════════════════════════════════════════════════════════════╝

data "aws_region" "current" {}

output "current_region" {
  value = data.aws_region.current.name  # ex: "us-east-1"
}

# Utilisation: Créer des noms uniques par région
resource "aws_s3_bucket" "logs" {
  bucket = "${var.project_name}-logs-${data.aws_region.current.name}"
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ DATA SOURCE: aws_iam_policy_document - Générer des policies               ║
╚═══════════════════════════════════════════════════════════════════════════╝

UTILITÉ:
────────
Créer des IAM policies de manière programmatique, plus lisible que du JSON.

# Générer une policy IAM
data "aws_iam_policy_document" "s3_read_only" {
  statement {
    sid    = "ReadOnlyAccess"
    effect = "Allow"
    
    actions = [
      "s3:GetObject",
      "s3:ListBucket"
    ]
    
    resources = [
      "arn:aws:s3:::my-bucket",
      "arn:aws:s3:::my-bucket/*"
    ]
  }
  
  statement {
    sid    = "DenyDelete"
    effect = "Deny"
    
    actions = [
      "s3:DeleteObject",
      "s3:DeleteBucket"
    ]
    
    resources = ["*"]
  }
}

# Utiliser la policy
resource "aws_iam_policy" "s3_read_only" {
  name   = "S3ReadOnly"
  policy = data.aws_iam_policy_document.s3_read_only.json
}

AVANTAGES vs JSON:
──────────────────
[OK] Plus lisible
[OK] Validation de syntaxe
[OK] Auto-complétion dans l'IDE
[OK] Facile de combiner plusieurs policies

COMBINER PLUSIEURS POLICIES:
────────────────────────────

data "aws_iam_policy_document" "base" {
  statement {
    actions   = ["s3:GetObject"]
    resources = ["*"]
  }
}

data "aws_iam_policy_document" "extended" {
  source_policy_documents = [
    data.aws_iam_policy_document.base.json
  ]
  
  statement {
    actions   = ["s3:PutObject"]
    resources = ["*"]
  }
}

# extended contient maintenant GetObject + PutObject


╔═══════════════════════════════════════════════════════════════════════════╗
║ DATA SOURCE: template_file - Fichiers de template (DEPRECATED)            ║
╚═══════════════════════════════════════════════════════════════════════════╝

[ATTENTION]  DEPRECATED: Utilisez templatefile() à la place

# ANCIENNE MÉTHODE (ne plus utiliser):
data "template_file" "user_data" {
  template = file("${path.module}/user_data.sh.tpl")
  
  vars = {
    project_name = var.project_name
    db_host      = aws_db_instance.main.address
  }
}

# NOUVELLE MÉTHODE (recommandée):
locals {
  user_data = templatefile("${path.module}/user_data.sh.tpl", {
    project_name = var.project_name
    db_host      = aws_db_instance.main.address
  })
}

resource "aws_instance" "web" {
  user_data = local.user_data
  # ...
}


═══════════════════════════════════════════════════════════════════════════════
[SORTIE] PARTIE 8: OUTPUTS - EXPOSER DES INFORMATIONS
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ POURQUOI LES OUTPUTS?                                                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

Les OUTPUTS permettent de:
1. Afficher des informations après terraform apply
2. Passer des valeurs à d'autres modules Terraform
3. Récupérer des valeurs dans des scripts (terraform output -json)
4. Documenter ce que votre infrastructure a créé

SYNTAXE:
────────

output "NOM" {
  description = "Description de l'output"
  value       = VALEUR
  sensitive   = true/false  # Optionnel
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ EXEMPLES D'OUTPUTS PRATIQUES                                              ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ OUTPUT SIMPLE: Afficher l'IP d'un serveur                                │
└───────────────────────────────────────────────────────────────────────────┘

output "server_ip" {
  description = "Adresse IP publique du serveur"
  value       = aws_instance.web.public_ip
}

# Après terraform apply:
# Outputs:
# server_ip = "54.123.45.67"


┌───────────────────────────────────────────────────────────────────────────┐
│ OUTPUT: Commande SSH prête à copier-coller                               │
└───────────────────────────────────────────────────────────────────────────┘

output "ssh_command" {
  description = "Commande pour se connecter au serveur"
  value       = "ssh -i ~/.ssh/mykey.pem ubuntu@${aws_instance.web.public_ip}"
}

# Outputs:
# ssh_command = "ssh -i ~/.ssh/mykey.pem ubuntu@54.123.45.67"
#
# Pratique! L'utilisateur peut directement copier-coller la commande


┌───────────────────────────────────────────────────────────────────────────┐
│ OUTPUT: Liste de valeurs (avec count ou for_each)                        │
└───────────────────────────────────────────────────────────────────────────┘

# Avec count
output "all_server_ips" {
  description = "IPs de tous les serveurs"
  value       = aws_instance.web[*].public_ip
}

# Outputs:
# all_server_ips = [
#   "54.123.45.67",
#   "54.123.45.68",
#   "54.123.45.69"
# ]

# Avec for_each
output "server_ips_by_name" {
  description = "IPs des serveurs par nom"
  value = {
    for name, instance in aws_instance.servers :
    name => instance.public_ip
  }
}

# Outputs:
# server_ips_by_name = {
#   "web"    = "54.123.45.67"
#   "api"    = "54.123.45.68"
#   "worker" = "54.123.45.69"
# }


┌───────────────────────────────────────────────────────────────────────────┐
│ OUTPUT: Objet complexe avec plusieurs informations                       │
└───────────────────────────────────────────────────────────────────────────┘

output "infrastructure_info" {
  description = "Informations complètes sur l'infrastructure"
  value = {
    vpc_id     = aws_vpc.main.id
    vpc_cidr   = aws_vpc.main.cidr_block
    subnet_ids = aws_subnet.public[*].id
    
    instances = {
      for idx, instance in aws_instance.web : idx => {
        id         = instance.id
        public_ip  = instance.public_ip
        private_ip = instance.private_ip
        az         = instance.availability_zone
      }
    }
    
    database = {
      endpoint = aws_db_instance.main.endpoint
      port     = aws_db_instance.main.port
    }
    
    load_balancer = {
      dns_name = aws_lb.main.dns_name
      zone_id  = aws_lb.main.zone_id
    }
  }
}


┌───────────────────────────────────────────────────────────────────────────┐
│ OUTPUT: URL de l'application                                             │
└───────────────────────────────────────────────────────────────────────────┘

output "application_url" {
  description = "URL pour accéder à l'application"
  value       = "https://${aws_lb.main.dns_name}"
}

# Outputs:
# application_url = "https://my-alb-1234567890.us-east-1.elb.amazonaws.com"


┌───────────────────────────────────────────────────────────────────────────┐
│ OUTPUT SENSIBLE: Mot de passe (caché par défaut)                         │
└───────────────────────────────────────────────────────────────────────────┘

output "database_password" {
  description = "Mot de passe de la base de données"
  value       = var.db_password
  sensitive   = true  # <- Important!
}

# Outputs:
# database_password = <sensitive>

# Pour voir la valeur:
# terraform output database_password
# ou
# terraform output -json


┌───────────────────────────────────────────────────────────────────────────┐
│ OUTPUT: Connection string formatée                                       │
└───────────────────────────────────────────────────────────────────────────┘

output "database_connection_string" {
  description = "String de connexion PostgreSQL"
  value       = "postgresql://${var.db_username}:${var.db_password}@${aws_db_instance.main.endpoint}/${var.db_name}"
  sensitive   = true
}

# Pratique pour le fichier .env de votre application:
# DATABASE_URL=postgresql://admin:password@prod-db.abc123.us-east-1.rds.amazonaws.com:5432/myapp


╔═══════════════════════════════════════════════════════════════════════════╗
║ UTILISER LES OUTPUTS DANS DES SCRIPTS                                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ BASH SCRIPT                                                               │
└───────────────────────────────────────────────────────────────────────────┘

#!/bin/bash
# Script de déploiement automatique

# Appliquer l'infrastructure
terraform apply -auto-approve

# Récupérer l'IP du serveur
SERVER_IP=$(terraform output -raw server_ip)

# Attendre que le serveur soit prêt
echo "Attente du démarrage du serveur..."
sleep 30

# Déployer l'application
echo "Déploiement de l'application sur $SERVER_IP..."
scp -i ~/.ssh/mykey.pem app.py ubuntu@$SERVER_IP:/home/ubuntu/

# Se connecter et lancer l'app
ssh -i ~/.ssh/mykey.pem ubuntu@$SERVER_IP << 'EOF'
cd /home/ubuntu
python3 app.py &
EOF

echo "[OK] Déploiement terminé!"
echo "Application accessible sur http://$SERVER_IP:8000"


┌───────────────────────────────────────────────────────────────────────────┐
│ PYTHON SCRIPT                                                             │
└───────────────────────────────────────────────────────────────────────────┘

#!/usr/bin/env python3
"""Script de post-déploiement"""

import json
import subprocess
import requests
import time

def get_terraform_outputs():
    """Récupère tous les outputs Terraform"""
    result = subprocess.run(
        ['terraform', 'output', '-json'],
        capture_output=True,
        text=True
    )
    return json.loads(result.stdout)

def wait_for_server(url, timeout=300):
    """Attend que le serveur réponde"""
    print(f"Attente du serveur {url}...")
    start = time.time()
    
    while time.time() - start < timeout:
        try:
            response = requests.get(url, timeout=5)
            if response.status_code == 200:
                print("[OK] Serveur prêt!")
                return True
        except requests.exceptions.RequestException:
            time.sleep(5)
    
    print("[X] Timeout!")
    return False

def main():
    # Récupérer les outputs
    outputs = get_terraform_outputs()
    
    app_url = outputs['application_url']['value']
    server_ips = outputs['all_server_ips']['value']
    
    print(f"Application URL: {app_url}")
    print(f"Serveurs: {', '.join(server_ips)}")
    
    # Attendre que l'application soit prête
    if wait_for_server(f"{app_url}/health"):
        print("[RAPIDE] Application déployée avec succès!")
        
        # Tests de santé
        print("\nTests de santé:")
        response = requests.get(f"{app_url}/health")
        print(response.json())
    else:
        print("[X] Échec du déploiement")
        return 1
    
    return 0

if __name__ == "__main__":
    exit(main())


╔═══════════════════════════════════════════════════════════════════════════╗
║ OUTPUTS ENTRE MODULES                                                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

Les outputs d'un module peuvent être utilisés par d'autres modules.

┌───────────────────────────────────────────────────────────────────────────┐
│ MODULE VPC (modules/vpc/outputs.tf)                                      │
└───────────────────────────────────────────────────────────────────────────┘

output "vpc_id" {
  description = "ID du VPC créé"
  value       = aws_vpc.main.id
}

output "public_subnet_ids" {
  description = "IDs des subnets publics"
  value       = aws_subnet.public[*].id
}

output "private_subnet_ids" {
  description = "IDs des subnets privés"
  value       = aws_subnet.private[*].id
}


┌───────────────────────────────────────────────────────────────────────────┐
│ ROOT MODULE (main.tf)                                                     │
└───────────────────────────────────────────────────────────────────────────┘

# Créer le VPC
module "vpc" {
  source = "./modules/vpc"
  
  project_name = var.project_name
  cidr_block   = "10.0.0.0/16"
}

# Utiliser les outputs du module VPC
module "ec2" {
  source = "./modules/ec2"
  
  vpc_id     = module.vpc.vpc_id              # <- Output du module VPC
  subnet_ids = module.vpc.public_subnet_ids   # <- Output du module VPC
}

module "rds" {
  source = "./modules/rds"
  
  vpc_id     = module.vpc.vpc_id              # <- Output du module VPC
  subnet_ids = module.vpc.private_subnet_ids  # <- Output du module VPC
}

# Exposer les outputs des modules vers l'extérieur
output "vpc_id" {
  value = module.vpc.vpc_id
}

output "instance_ids" {
  value = module.ec2.instance_ids
}

output "database_endpoint" {
  value = module.rds.endpoint
}


═══════════════════════════════════════════════════════════════════════════════
[PYTHON] PARTIE 9: DÉPLOYER DES APPLICATIONS PYTHON AVEC TERRAFORM
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ ARCHITECTURE TYPIQUE D'UNE APPLICATION PYTHON SUR AWS                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌─────────────────────────────────────────────────────────────────────────┐
│                    APPLICATION PYTHON SUR AWS                            │
│                                                                          │
│  Internet                                                                │
│     │                                                                    │
│     v                                                                    │
│ ┌────────────────┐                                                      │
│ │ Application    │                                                      │
│ │ Load Balancer  │  <- Distribue le trafic                             │
│ └────────┬───────┘                                                      │
│          │                                                               │
│          v                                                               │
│ ┌────────────────────────┐                                             │
│ │   Auto Scaling Group   │                                             │
│ │  ┌──────┐  ┌──────┐   │                                             │
│ │  │ EC2  │  │ EC2  │   │  <- Instances Python (Flask/Django/FastAPI) │
│ │  └──┬───┘  └──┬───┘   │                                             │
│ └─────┼─────────┼────────┘                                             │
│       │         │                                                       │
│       └────┬────┘                                                       │
│            │                                                            │
│            v                                                            │
│ ┌──────────────────┐     ┌──────────────────┐                         │
│ │   RDS PostgreSQL │     │  ElastiCache     │                         │
│ │                  │     │  Redis           │                         │
│ └──────────────────┘     └──────────────────┘                         │
│                                                                          │
│ ┌──────────────────┐     ┌──────────────────┐                         │
│ │   S3             │     │  CloudWatch      │                         │
│ │   (Static files) │     │  (Logs/Metrics)  │                         │
│ └──────────────────┘     └──────────────────┘                         │
└─────────────────────────────────────────────────────────────────────────┘


╔═══════════════════════════════════════════════════════════════════════════╗
║ EXEMPLE 1: APPLICATION FLASK SIMPLE                                       ║
╚═══════════════════════════════════════════════════════════════════════════╝

OBJECTIF:
─────────
Déployer une application Flask sur une instance EC2 avec:
• Un security group pour autoriser HTTP/HTTPS
• Une Elastic IP (IP fixe)
• User data pour installer Python et Flask
• Un bucket S3 pour les uploads

┌───────────────────────────────────────────────────────────────────────────┐
│ STRUCTURE DU PROJET                                                       │
└───────────────────────────────────────────────────────────────────────────┘

flask-terraform/
├── main.tf                  # Configuration principale
├── variables.tf             # Variables
├── outputs.tf               # Outputs
├── versions.tf              # Versions
├── user_data.sh             # Script d'installation
├── terraform.tfvars         # Valeurs des variables
└── app/                     # Application Flask
    ├── app.py
    ├── requirements.txt
    └── templates/


┌───────────────────────────────────────────────────────────────────────────┐
│ versions.tf                                                               │
└───────────────────────────────────────────────────────────────────────────┘

terraform {
  required_version = ">= 1.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
  
  default_tags {
    tags = {
      Project   = var.project_name
      ManagedBy = "Terraform"
    }
  }
}


┌───────────────────────────────────────────────────────────────────────────┐
│ variables.tf                                                              │
└───────────────────────────────────────────────────────────────────────────┘

variable "project_name" {
  description = "Nom du projet"
  type        = string
  default     = "flask-app"
}

variable "aws_region" {
  description = "Région AWS"
  type        = string
  default     = "us-east-1"
}

variable "instance_type" {
  description = "Type d'instance EC2"
  type        = string
  default     = "t2.micro"
  
  validation {
    condition     = contains(["t2.micro", "t2.small", "t2.medium"], var.instance_type)
    error_message = "Instance type doit être t2.micro, t2.small ou t2.medium."
  }
}

variable "key_name" {
  description = "Nom de la paire de clés SSH"
  type        = string
}

variable "allowed_ssh_cidr" {
  description = "CIDR autorisé pour SSH"
  type        = list(string)
  default     = ["0.0.0.0/0"]  # À restreindre en production!
}


┌───────────────────────────────────────────────────────────────────────────┐
│ main.tf                                                                   │
└───────────────────────────────────────────────────────────────────────────┘

# ═══════════════════════════════════════════════════════════════════════
# DATA SOURCES
# ═══════════════════════════════════════════════════════════════════════

# AMI Ubuntu la plus récente
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]  # Canonical
  
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }
  
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

# VPC par défaut (pour simplifier)
data "aws_vpc" "default" {
  default = true
}


# ═══════════════════════════════════════════════════════════════════════
# SECURITY GROUP
# ═══════════════════════════════════════════════════════════════════════

resource "aws_security_group" "flask_app" {
  name        = "${var.project_name}-sg"
  description = "Security group pour l'application Flask"
  vpc_id      = data.aws_vpc.default.id
  
  # HTTP (port 80)
  ingress {
    description = "HTTP depuis Internet"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  # HTTPS (port 443)
  ingress {
    description = "HTTPS depuis Internet"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  # Flask port (5000)
  ingress {
    description = "Flask depuis Internet"
    from_port   = 5000
    to_port     = 5000
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  # SSH (port 22)
  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = var.allowed_ssh_cidr
  }
  
  # Tout le trafic sortant autorisé
  egress {
    description = "Tout le trafic sortant"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  tags = {
    Name = "${var.project_name}-sg"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# S3 BUCKET (pour les uploads)
# ═══════════════════════════════════════════════════════════════════════

resource "aws_s3_bucket" "uploads" {
  bucket = "${var.project_name}-uploads-${data.aws_caller_identity.current.account_id}"
  
  tags = {
    Name = "${var.project_name}-uploads"
  }
}

# Bloquer l'accès public (sécurité)
resource "aws_s3_bucket_public_access_block" "uploads" {
  bucket = aws_s3_bucket.uploads.id
  
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# Versioning activé (backup automatique)
resource "aws_s3_bucket_versioning" "uploads" {
  bucket = aws_s3_bucket.uploads.id
  
  versioning_configuration {
    status = "Enabled"
  }
}

# Lifecycle rule (nettoyer les anciennes versions après 30 jours)
resource "aws_s3_bucket_lifecycle_configuration" "uploads" {
  bucket = aws_s3_bucket.uploads.id
  
  rule {
    id     = "delete-old-versions"
    status = "Enabled"
    
    noncurrent_version_expiration {
      noncurrent_days = 30
    }
  }
}

# Info sur le compte AWS
data "aws_caller_identity" "current" {}


# ═══════════════════════════════════════════════════════════════════════
# IAM ROLE (pour que l'EC2 puisse accéder au S3)
# ═══════════════════════════════════════════════════════════════════════

# Role IAM pour l'instance EC2
resource "aws_iam_role" "ec2_role" {
  name = "${var.project_name}-ec2-role"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      }
    ]
  })
}

# Policy pour accéder au bucket S3
resource "aws_iam_role_policy" "s3_access" {
  name = "s3-access"
  role = aws_iam_role.ec2_role.id
  
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:PutObject",
          "s3:DeleteObject"
        ]
        Resource = "${aws_s3_bucket.uploads.arn}/*"
      },
      {
        Effect = "Allow"
        Action = [
          "s3:ListBucket"
        ]
        Resource = aws_s3_bucket.uploads.arn
      }
    ]
  })
}

# Instance profile (attacher le role à l'instance)
resource "aws_iam_instance_profile" "ec2_profile" {
  name = "${var.project_name}-ec2-profile"
  role = aws_iam_role.ec2_role.name
}


# ═══════════════════════════════════════════════════════════════════════
# INSTANCE EC2
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "flask_app" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.instance_type
  key_name      = var.key_name
  
  vpc_security_group_ids = [aws_security_group.flask_app.id]
  iam_instance_profile   = aws_iam_instance_profile.ec2_profile.name
  
  # Disque racine
  root_block_device {
    volume_size = 20
    volume_type = "gp3"
    encrypted   = true
  }
  
  # User data: Script d'installation
  user_data = templatefile("${path.module}/user_data.sh", {
    project_name = var.project_name
    s3_bucket    = aws_s3_bucket.uploads.bucket
  })
  
  tags = {
    Name = "${var.project_name}-instance"
  }
  
  # Attendre que le role IAM soit créé
  depends_on = [
    aws_iam_role_policy.s3_access
  ]
}


# ═══════════════════════════════════════════════════════════════════════
# ELASTIC IP (IP publique fixe)
# ═══════════════════════════════════════════════════════════════════════

resource "aws_eip" "flask_app" {
  instance = aws_instance.flask_app.id
  domain   = "vpc"
  
  tags = {
    Name = "${var.project_name}-eip"
  }
}


┌───────────────────────────────────────────────────────────────────────────┐
│ user_data.sh - Script d'installation                                     │
└───────────────────────────────────────────────────────────────────────────┘

#!/bin/bash
# ═══════════════════════════════════════════════════════════════════════
# Script d'initialisation pour instance EC2
# Ce script est exécuté AU PREMIER DÉMARRAGE de l'instance
# ═══════════════════════════════════════════════════════════════════════

set -e  # Arrêter si une commande échoue

# ┌───────────────────────────────────────────────────────────────────┐
# │ 1. LOGS                                                            │
# └───────────────────────────────────────────────────────────────────┘

# Tout ce qui se passe est logué dans /var/log/user-data.log
exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1

echo "═══════════════════════════════════════════════════════════"
echo "Début de l'installation - $(date)"
echo "═══════════════════════════════════════════════════════════"


# ┌───────────────────────────────────────────────────────────────────┐
# │ 2. MISE À JOUR DU SYSTÈME                                         │
# └───────────────────────────────────────────────────────────────────┘

echo "Mise à jour du système..."
apt-get update
apt-get upgrade -y


# ┌───────────────────────────────────────────────────────────────────┐
# │ 3. INSTALLATION DE PYTHON ET DÉPENDANCES                         │
# └───────────────────────────────────────────────────────────────────┘

echo "Installation de Python 3.11..."
apt-get install -y \
    python3.11 \
    python3.11-venv \
    python3-pip \
    git \
    nginx \
    supervisor

# Créer un lien symbolique python -> python3.11
update-alternatives --install /usr/bin/python python /usr/bin/python3.11 1


# ┌───────────────────────────────────────────────────────────────────┐
# │ 4. CRÉER UN UTILISATEUR POUR L'APPLICATION                       │
# └───────────────────────────────────────────────────────────────────┘

echo "Création de l'utilisateur appuser..."
useradd -m -s /bin/bash appuser


# ┌───────────────────────────────────────────────────────────────────┐
# │ 5. CRÉER LA STRUCTURE DE L'APPLICATION                           │
# └───────────────────────────────────────────────────────────────────┘

echo "Création des dossiers..."
APP_DIR="/opt/${project_name}"
mkdir -p $APP_DIR
chown appuser:appuser $APP_DIR


# ┌───────────────────────────────────────────────────────────────────┐
# │ 6. APPLICATION FLASK SIMPLE                                       │
# └───────────────────────────────────────────────────────────────────┘

echo "Création de l'application Flask..."

# Créer requirements.txt
cat > $APP_DIR/requirements.txt <<'EOF'
flask==3.0.0
gunicorn==21.2.0
boto3==1.29.0
python-dotenv==1.0.0
EOF

# Créer l'application Flask
cat > $APP_DIR/app.py <<'EOF'
from flask import Flask, request, jsonify
import boto3
import os

app = Flask(__name__)

# Configuration S3
s3_client = boto3.client('s3')
S3_BUCKET = os.getenv('S3_BUCKET')

@app.route('/')
def home():
    return '''
    <!DOCTYPE html>
    <html>
    <head>
        <title>Flask App sur AWS</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                max-width: 800px;
                margin: 50px auto;
                padding: 20px;
            }
            h1 { color: #333; }
            .info { background: #f0f0f0; padding: 10px; border-radius: 5px; }
        </style>
    </head>
    <body>
        <h1>[BRAVO] Application Flask déployée avec Terraform!</h1>
        <div class="info">
            <p><strong>Status:</strong> [OK] Running</p>
            <p><strong>S3 Bucket:</strong> {}</p>
        </div>
        <h2>Endpoints disponibles:</h2>
        <ul>
            <li><a href="/health">/health</a> - Health check</li>
            <li><a href="/upload">/upload</a> - Upload vers S3</li>
        </ul>
    </body>
    </html>
    '''.format(S3_BUCKET)

@app.route('/health')
def health():
    return jsonify({
        'status': 'healthy',
        's3_bucket': S3_BUCKET
    })

@app.route('/upload', methods=['POST'])
def upload():
    if 'file' not in request.files:
        return jsonify({'error': 'No file part'}), 400
    
    file = request.files['file']
    if file.filename == '':
        return jsonify({'error': 'No selected file'}), 400
    
    try:
        s3_client.upload_fileobj(
            file,
            S3_BUCKET,
            file.filename
        )
        return jsonify({
            'message': 'File uploaded successfully',
            'filename': file.filename
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
EOF

chown -R appuser:appuser $APP_DIR


# ┌───────────────────────────────────────────────────────────────────┐
# │ 7. CRÉER L'ENVIRONNEMENT VIRTUEL PYTHON                          │
# └───────────────────────────────────────────────────────────────────┘

echo "Création de l'environnement virtuel..."
su - appuser -c "python -m venv $APP_DIR/venv"

echo "Installation des dépendances Python..."
su - appuser -c "source $APP_DIR/venv/bin/activate && pip install -r $APP_DIR/requirements.txt"


# ┌───────────────────────────────────────────────────────────────────┐
# │ 8. CONFIGURATION DE SUPERVISOR (gestionnaire de processus)       │
# └───────────────────────────────────────────────────────────────────┘

echo "Configuration de Supervisor..."

cat > /etc/supervisor/conf.d/${project_name}.conf <<EOF
[program:${project_name}]
directory=$APP_DIR
command=$APP_DIR/venv/bin/gunicorn --bind 0.0.0.0:5000 --workers 4 app:app
user=appuser
autostart=true
autorestart=true
stderr_logfile=/var/log/${project_name}.err.log
stdout_logfile=/var/log/${project_name}.out.log
environment=S3_BUCKET="${s3_bucket}"
EOF

# Redémarrer Supervisor
supervisorctl reread
supervisorctl update
supervisorctl start ${project_name}


# ┌───────────────────────────────────────────────────────────────────┐
# │ 9. CONFIGURATION DE NGINX (reverse proxy)                        │
# └───────────────────────────────────────────────────────────────────┘

echo "Configuration de Nginx..."

cat > /etc/nginx/sites-available/${project_name} <<'EOF'
server {
    listen 80;
    server_name _;
    
    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
EOF

# Activer le site
ln -sf /etc/nginx/sites-available/${project_name} /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default

# Tester la config et redémarrer
nginx -t
systemctl restart nginx


# ┌───────────────────────────────────────────────────────────────────┐
# │ 10. FINALISATION                                                  │
# └───────────────────────────────────────────────────────────────────┘

echo "═══════════════════════════════════════════════════════════"
echo "Installation terminée - $(date)"
echo "═══════════════════════════════════════════════════════════"
echo "Application accessible sur http://$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4)"


┌───────────────────────────────────────────────────────────────────────────┐
│ outputs.tf                                                                │
└───────────────────────────────────────────────────────────────────────────┘

output "instance_id" {
  description = "ID de l'instance EC2"
  value       = aws_instance.flask_app.id
}

output "public_ip" {
  description = "Adresse IP publique (Elastic IP)"
  value       = aws_eip.flask_app.public_ip
}

output "application_url" {
  description = "URL de l'application"
  value       = "http://${aws_eip.flask_app.public_ip}"
}

output "ssh_command" {
  description = "Commande SSH pour se connecter"
  value       = "ssh -i ~/.ssh/${var.key_name}.pem ubuntu@${aws_eip.flask_app.public_ip}"
}

output "s3_bucket_name" {
  description = "Nom du bucket S3 pour les uploads"
  value       = aws_s3_bucket.uploads.bucket
}

output "logs_command" {
  description = "Commande pour voir les logs de l'application"
  value       = "ssh -i ~/.ssh/${var.key_name}.pem ubuntu@${aws_eip.flask_app.public_ip} 'tail -f /var/log/${var.project_name}.out.log'"
}


┌───────────────────────────────────────────────────────────────────────────┐
│ terraform.tfvars                                                          │
└───────────────────────────────────────────────────────────────────────────┘

project_name = "my-flask-app"
aws_region   = "us-east-1"
instance_type = "t2.micro"
key_name      = "mykey"  # Remplacer par votre clé SSH

# Restreindre SSH à votre IP
allowed_ssh_cidr = ["1.2.3.4/32"]  # Remplacer par votre IP


╔═══════════════════════════════════════════════════════════════════════════╗
║ DÉPLOIEMENT ÉTAPE PAR ÉTAPE                                              ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 1: Créer une paire de clés SSH (si vous n'en avez pas)            │
└───────────────────────────────────────────────────────────────────────────┘

# Dans la console AWS ou via CLI:
aws ec2 create-key-pair --key-name mykey --query 'KeyMaterial' --output text > ~/.ssh/mykey.pem
chmod 400 ~/.ssh/mykey.pem


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 2: Initialiser Terraform                                           │
└───────────────────────────────────────────────────────────────────────────┘

cd flask-terraform
terraform init

# Output:
# Initializing the backend...
# Initializing provider plugins...
# - Finding hashicorp/aws versions matching "~> 5.0"...
# - Installing hashicorp/aws v5.25.0...
# Terraform has been successfully initialized!


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 3: Valider la configuration                                        │
└───────────────────────────────────────────────────────────────────────────┘

terraform validate

# Output:
# Success! The configuration is valid.


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 4: Voir le plan d'exécution                                        │
└───────────────────────────────────────────────────────────────────────────┘

terraform plan

# Output (résumé):
# Plan: 10 to add, 0 to change, 0 to destroy.
#
# Terraform va créer:
# - 1 Security Group
# - 1 S3 Bucket + configurations
# - 1 IAM Role + Policy + Instance Profile
# - 1 EC2 Instance
# - 1 Elastic IP


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 5: Appliquer la configuration                                      │
└───────────────────────────────────────────────────────────────────────────┘

terraform apply

# Terraform affiche le plan
# Do you want to perform these actions?
#   Enter a value: yes

# [HOURGLASS_WITH_FLOWING_SAND] Attend environ 2-3 minutes...

# Output:
# Apply complete! Resources: 10 added, 0 changed, 0 destroyed.
#
# Outputs:
#
# application_url = "http://54.123.45.67"
# instance_id = "i-1234567890abcdef0"
# logs_command = "ssh -i ~/.ssh/mykey.pem ubuntu@54.123.45.67 'tail -f /var/log/my-flask-app.out.log'"
# public_ip = "54.123.45.67"
# s3_bucket_name = "my-flask-app-uploads-123456789012"
# ssh_command = "ssh -i ~/.ssh/mykey.pem ubuntu@54.123.45.67"


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 6: Tester l'application                                            │
└───────────────────────────────────────────────────────────────────────────┘

# Récupérer l'URL
APP_URL=$(terraform output -raw application_url)

# Ouvrir dans le navigateur
open $APP_URL  # macOS
xdg-open $APP_URL  # Linux

# Ou tester avec curl
curl $APP_URL

# Tester le endpoint health
curl $APP_URL/health

# Output:
# {"s3_bucket":"my-flask-app-uploads-123456789012","status":"healthy"}


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 7: Se connecter en SSH (pour debugger)                             │
└───────────────────────────────────────────────────────────────────────────┘

# Copier la commande depuis les outputs
ssh -i ~/.ssh/mykey.pem ubuntu@54.123.45.67

# Une fois connecté:
# Voir les logs de l'application
tail -f /var/log/my-flask-app.out.log

# Voir les logs d'installation
cat /var/log/user-data.log

# Vérifier que l'app tourne
sudo supervisorctl status

# Output:
# my-flask-app                     RUNNING   pid 1234, uptime 0:05:23


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 8: Nettoyer (quand vous avez fini)                                 │
└───────────────────────────────────────────────────────────────────────────┘

terraform destroy

# Do you really want to destroy all resources?
#   Enter a value: yes

# [HOURGLASS_WITH_FLOWING_SAND] Attend quelques minutes...

# Output:
# Destroy complete! Resources: 10 destroyed.

# [OK] Toutes les ressources sont supprimées
# [OK] Vous ne payez plus rien!


═══════════════════════════════════════════════════════════════════════════════
[PACKAGE] PARTIE 10: MODULES - RÉUTILISER DU CODE
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ QU'EST-CE QU'UN MODULE TERRAFORM?                                         ║
╚═══════════════════════════════════════════════════════════════════════════╝

DÉFINITION:
───────────
Un MODULE est un conteneur pour plusieurs ressources Terraform utilisées ensemble.
C'est une façon d'organiser, de réutiliser et de partager du code Terraform.

ANALOGIE:
─────────
Imaginez que vous construisez des maisons:

┌──────────────────────────────────────────────────────────────────────┐
│ SANS MODULES:                                                         │
│ Chaque fois que vous construisez une maison, vous devez:            │
│ • Dessiner les plans depuis zéro                                     │
│ • Calculer les matériaux                                             │
│ • Expliquer chaque détail aux ouvriers                              │
│                                                                       │
│ AVEC MODULES:                                                         │
│ Vous créez un "plan type" de maison que vous pouvez:                │
│ • Réutiliser pour plusieurs maisons                                  │
│ • Adapter légèrement (3 chambres vs 4 chambres)                     │
│ • Partager avec d'autres constructeurs                              │
└──────────────────────────────────────────────────────────────────────┘

POURQUOI UTILISER DES MODULES?
───────────────────────────────

1. **RÉUTILISABILITÉ**
   • Écrire une fois, utiliser partout
   • Même configuration pour dev, staging, prod

2. **ORGANISATION**
   • Code structuré et lisible
   • Séparation des responsabilités

3. **MAINTENANCE**
   • Corriger un bug une seule fois
   • Mise à jour centralisée

4. **PARTAGE**
   • Publier sur Terraform Registry
   • Utiliser les modules de la communauté

5. **ABSTRACTION**
   • Cacher la complexité
   • Interface simple pour des configs complexes


╔═══════════════════════════════════════════════════════════════════════════╗
║ STRUCTURE D'UN MODULE                                                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

ANATOMIE D'UN MODULE:
─────────────────────

modules/
└── vpc/                    # Dossier du module
    ├── main.tf             # Ressources principales
    ├── variables.tf        # Variables d'entrée (INPUT)
    ├── outputs.tf          # Valeurs de sortie (OUTPUT)
    ├── versions.tf         # Versions requises
    └── README.md           # Documentation

IMPORTANT:
──────────
• TOUT dossier contenant des fichiers .tf est techniquement un module
• Votre configuration racine est aussi un module (le "root module")
• Les modules peuvent appeler d'autres modules


╔═══════════════════════════════════════════════════════════════════════════╗
║ CRÉER VOTRE PREMIER MODULE                                                ║
╚═══════════════════════════════════════════════════════════════════════════╝

EXEMPLE: Module VPC pour AWS
─────────────────────────────

Créons un module qui crée un VPC complet avec subnets publics et privés.

┌───────────────────────────────────────────────────────────────────────────┐
│ STRUCTURE DU PROJET                                                       │
└───────────────────────────────────────────────────────────────────────────┘

terraform-project/
├── main.tf                 # Configuration racine
├── variables.tf
├── outputs.tf
└── modules/
    └── vpc/                # Notre module VPC
        ├── main.tf
        ├── variables.tf
        ├── outputs.tf
        └── README.md


┌───────────────────────────────────────────────────────────────────────────┐
│ modules/vpc/variables.tf - Les ENTRÉES du module                         │
└───────────────────────────────────────────────────────────────────────────┘

# modules/vpc/variables.tf
# ═══════════════════════════════════════════════════════════════════════
# Variables d'entrée du module VPC
# Ces variables DOIVENT être fournies par l'appelant du module
# ═══════════════════════════════════════════════════════════════════════

variable "project_name" {
  description = "Nom du projet (utilisé pour nommer les ressources)"
  type        = string
}

variable "vpc_cidr" {
  description = "CIDR block pour le VPC"
  type        = string
  default     = "10.0.0.0/16"
  
  validation {
    condition     = can(cidrhost(var.vpc_cidr, 0))
    error_message = "vpc_cidr doit être un CIDR block valide."
  }
}

variable "availability_zones" {
  description = "Liste des zones de disponibilité"
  type        = list(string)
}

variable "public_subnet_cidrs" {
  description = "CIDR blocks pour les subnets publics"
  type        = list(string)
}

variable "private_subnet_cidrs" {
  description = "CIDR blocks pour les subnets privés"
  type        = list(string)
}

variable "enable_nat_gateway" {
  description = "Créer un NAT Gateway pour les subnets privés?"
  type        = bool
  default     = true
}

variable "single_nat_gateway" {
  description = "Utiliser un seul NAT Gateway pour tous les subnets privés?"
  type        = bool
  default     = false
}

variable "tags" {
  description = "Tags à appliquer à toutes les ressources"
  type        = map(string)
  default     = {}
}


┌───────────────────────────────────────────────────────────────────────────┐
│ modules/vpc/main.tf - La LOGIQUE du module                               │
└───────────────────────────────────────────────────────────────────────────┘

# modules/vpc/main.tf
# ═══════════════════════════════════════════════════════════════════════
# Module VPC - Crée un VPC complet avec subnets et routing
# ═══════════════════════════════════════════════════════════════════════

# ┌─────────────────────────────────────────────────────────────────────┐
# │ VPC Principal                                                        │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  tags = merge(
    var.tags,
    {
      Name = "${var.project_name}-vpc"
    }
  )
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Internet Gateway (pour les subnets publics)                         │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  
  tags = merge(
    var.tags,
    {
      Name = "${var.project_name}-igw"
    }
  )
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Subnets Publics                                                      │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_subnet" "public" {
  count = length(var.public_subnet_cidrs)
  
  vpc_id                  = aws_vpc.main.id
  cidr_block              = var.public_subnet_cidrs[count.index]
  availability_zone       = var.availability_zones[count.index]
  map_public_ip_on_launch = true
  
  tags = merge(
    var.tags,
    {
      Name = "${var.project_name}-public-${count.index + 1}"
      Type = "public"
    }
  )
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Subnets Privés                                                       │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_subnet" "private" {
  count = length(var.private_subnet_cidrs)
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]
  
  tags = merge(
    var.tags,
    {
      Name = "${var.project_name}-private-${count.index + 1}"
      Type = "private"
    }
  )
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Elastic IPs pour les NAT Gateways                                   │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_eip" "nat" {
  # Si single_nat_gateway = true, créer 1 seule EIP
  # Sinon, créer une EIP par subnet public
  count = var.enable_nat_gateway ? (var.single_nat_gateway ? 1 : length(var.public_subnet_cidrs)) : 0
  
  domain = "vpc"
  
  tags = merge(
    var.tags,
    {
      Name = "${var.project_name}-nat-eip-${count.index + 1}"
    }
  )
  
  depends_on = [aws_internet_gateway.main]
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ NAT Gateways (pour que les subnets privés accèdent à Internet)     │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_nat_gateway" "main" {
  count = var.enable_nat_gateway ? (var.single_nat_gateway ? 1 : length(var.public_subnet_cidrs)) : 0
  
  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id
  
  tags = merge(
    var.tags,
    {
      Name = "${var.project_name}-nat-${count.index + 1}"
    }
  )
  
  depends_on = [aws_internet_gateway.main]
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Route Table Publique                                                │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  
  tags = merge(
    var.tags,
    {
      Name = "${var.project_name}-public-rt"
      Type = "public"
    }
  )
}

# Route vers Internet via l'Internet Gateway
resource "aws_route" "public_internet" {
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.main.id
}

# Associer les subnets publics à la route table publique
resource "aws_route_table_association" "public" {
  count = length(var.public_subnet_cidrs)
  
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Route Tables Privées                                                │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_route_table" "private" {
  # Si single_nat_gateway = true, créer 1 seule route table
  # Sinon, créer une route table par subnet privé
  count = var.single_nat_gateway ? 1 : length(var.private_subnet_cidrs)
  
  vpc_id = aws_vpc.main.id
  
  tags = merge(
    var.tags,
    {
      Name = "${var.project_name}-private-rt-${count.index + 1}"
      Type = "private"
    }
  )
}

# Route vers Internet via le NAT Gateway
resource "aws_route" "private_internet" {
  count = var.enable_nat_gateway ? (var.single_nat_gateway ? 1 : length(var.private_subnet_cidrs)) : 0
  
  route_table_id         = aws_route_table.private[count.index].id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = aws_nat_gateway.main[count.index].id
}

# Associer les subnets privés aux route tables privées
resource "aws_route_table_association" "private" {
  count = length(var.private_subnet_cidrs)
  
  subnet_id = aws_subnet.private[count.index].id
  
  # Si single_nat_gateway, tous les subnets utilisent la même route table
  # Sinon, chaque subnet a sa propre route table
  route_table_id = var.single_nat_gateway ? aws_route_table.private[0].id : aws_route_table.private[count.index].id
}


┌───────────────────────────────────────────────────────────────────────────┐
│ modules/vpc/outputs.tf - Les SORTIES du module                           │
└───────────────────────────────────────────────────────────────────────────┘

# modules/vpc/outputs.tf
# ═══════════════════════════════════════════════════════════════════════
# Outputs du module VPC
# Ces valeurs sont EXPOSÉES à l'appelant du module
# ═══════════════════════════════════════════════════════════════════════

output "vpc_id" {
  description = "ID du VPC créé"
  value       = aws_vpc.main.id
}

output "vpc_cidr" {
  description = "CIDR block du VPC"
  value       = aws_vpc.main.cidr_block
}

output "public_subnet_ids" {
  description = "Liste des IDs des subnets publics"
  value       = aws_subnet.public[*].id
}

output "private_subnet_ids" {
  description = "Liste des IDs des subnets privés"
  value       = aws_subnet.private[*].id
}

output "public_subnet_cidrs" {
  description = "Liste des CIDR blocks des subnets publics"
  value       = aws_subnet.public[*].cidr_block
}

output "private_subnet_cidrs" {
  description = "Liste des CIDR blocks des subnets privés"
  value       = aws_subnet.private[*].cidr_block
}

output "nat_gateway_ids" {
  description = "IDs des NAT Gateways (si créés)"
  value       = aws_nat_gateway.main[*].id
}

output "internet_gateway_id" {
  description = "ID de l'Internet Gateway"
  value       = aws_internet_gateway.main.id
}


┌───────────────────────────────────────────────────────────────────────────┐
│ modules/vpc/README.md - Documentation                                     │
└───────────────────────────────────────────────────────────────────────────┘

# Module VPC

Ce module crée un VPC AWS complet avec subnets publics et privés.

## Fonctionnalités

- VPC avec CIDR personnalisable
- Subnets publics (avec Internet Gateway)
- Subnets privés (avec NAT Gateway optionnel)
- Route tables configurées automatiquement
- Support pour plusieurs zones de disponibilité

## Utilisation

```hcl
module "vpc" {
  source = "./modules/vpc"
  
  project_name = "mon-projet"
  vpc_cidr     = "10.0.0.0/16"
  
  availability_zones = ["us-east-1a", "us-east-1b"]
  
  public_subnet_cidrs  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnet_cidrs = ["10.0.11.0/24", "10.0.12.0/24"]
  
  enable_nat_gateway = true
  single_nat_gateway = false
  
  tags = {
    Environment = "production"
  }
}
```

## Variables

| Nom | Description | Type | Défaut | Requis |
|-----|-------------|------|--------|--------|
| project_name | Nom du projet | string | - | oui |
| vpc_cidr | CIDR du VPC | string | "10.0.0.0/16" | non |
| availability_zones | Liste des AZs | list(string) | - | oui |
| public_subnet_cidrs | CIDRs des subnets publics | list(string) | - | oui |
| private_subnet_cidrs | CIDRs des subnets privés | list(string) | - | oui |
| enable_nat_gateway | Créer NAT Gateway? | bool | true | non |
| single_nat_gateway | Un seul NAT Gateway? | bool | false | non |

## Outputs

| Nom | Description |
|-----|-------------|
| vpc_id | ID du VPC |
| public_subnet_ids | IDs des subnets publics |
| private_subnet_ids | IDs des subnets privés |


╔═══════════════════════════════════════════════════════════════════════════╗
║ UTILISER LE MODULE                                                        ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ Configuration racine (main.tf)                                            │
└───────────────────────────────────────────────────────────────────────────┘

# main.tf
# ═══════════════════════════════════════════════════════════════════════
# Configuration racine utilisant le module VPC
# ═══════════════════════════════════════════════════════════════════════

terraform {
  required_version = ">= 1.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

# ┌─────────────────────────────────────────────────────────────────────┐
# │ DATA SOURCES                                                         │
# └─────────────────────────────────────────────────────────────────────┘

data "aws_availability_zones" "available" {
  state = "available"
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ UTILISATION DU MODULE VPC                                           │
# └─────────────────────────────────────────────────────────────────────┘

module "vpc" {
  source = "./modules/vpc"  # <- Chemin vers le module
  
  # Variables requises
  project_name       = var.project_name
  availability_zones = slice(data.aws_availability_zones.available.names, 0, 2)
  
  # Configuration du VPC
  vpc_cidr             = "10.0.0.0/16"
  public_subnet_cidrs  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnet_cidrs = ["10.0.11.0/24", "10.0.12.0/24"]
  
  # NAT Gateway (un par AZ en production, un seul en dev)
  enable_nat_gateway = var.environment == "prod" ? true : true
  single_nat_gateway = var.environment == "prod" ? false : true
  
  # Tags
  tags = {
    Environment = var.environment
    ManagedBy   = "Terraform"
  }
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ UTILISER LES OUTPUTS DU MODULE                                      │
# └─────────────────────────────────────────────────────────────────────┘

# Créer une instance EC2 dans les subnets publics du module
resource "aws_instance" "web" {
  count = 2
  
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  
  # Utiliser les outputs du module VPC
  subnet_id = module.vpc.public_subnet_ids[count.index]
  
  tags = {
    Name = "web-${count.index}"
  }
}

# Créer une base de données dans les subnets privés
resource "aws_db_subnet_group" "main" {
  name       = "${var.project_name}-db-subnet"
  subnet_ids = module.vpc.private_subnet_ids  # <- Output du module
  
  tags = {
    Name = "${var.project_name}-db-subnet"
  }
}


COMMENT ÇA MARCHE?
──────────────────

1. **APPEL DU MODULE**
   ```hcl
   module "vpc" {
     source = "./modules/vpc"
     # ...
   }
   ```
   
   Terraform lit tous les fichiers .tf dans ./modules/vpc/

2. **PASSAGE DE VARIABLES**
   ```hcl
   project_name = var.project_name
   vpc_cidr     = "10.0.0.0/16"
   ```
   
   Ces valeurs sont passées AUX variables du module

3. **UTILISATION DES OUTPUTS**
   ```hcl
   subnet_id = module.vpc.public_subnet_ids[0]
   ```
   
   Syntaxe: module.NOM_MODULE.NOM_OUTPUT


╔═══════════════════════════════════════════════════════════════════════════╗
║ MODULES MULTIPLES - COMPOSER DES MODULES                                  ║
╚═══════════════════════════════════════════════════════════════════════════╝

Vous pouvez utiliser plusieurs modules ensemble pour construire des
architectures complexes.

# main.tf
# ═══════════════════════════════════════════════════════════════════════
# Architecture complète avec plusieurs modules
# ═══════════════════════════════════════════════════════════════════════

# Module VPC
module "vpc" {
  source = "./modules/vpc"
  
  project_name       = var.project_name
  availability_zones = data.aws_availability_zones.available.names
  vpc_cidr           = "10.0.0.0/16"
  public_subnet_cidrs  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnet_cidrs = ["10.0.11.0/24", "10.0.12.0/24"]
}

# Module EC2
module "web_servers" {
  source = "./modules/ec2"
  
  project_name = var.project_name
  vpc_id       = module.vpc.vpc_id           # <- Utilise l'output du module VPC
  subnet_ids   = module.vpc.public_subnet_ids
  
  instance_count = 3
  instance_type  = "t2.micro"
}

# Module RDS
module "database" {
  source = "./modules/rds"
  
  project_name = var.project_name
  vpc_id       = module.vpc.vpc_id           # <- Utilise l'output du module VPC
  subnet_ids   = module.vpc.private_subnet_ids
  
  # Autoriser l'accès depuis les instances web
  allowed_security_groups = [module.web_servers.security_group_id]
  
  db_name     = "myapp"
  db_username = "admin"
  db_password = var.db_password
}

# Module Load Balancer
module "alb" {
  source = "./modules/alb"
  
  project_name = var.project_name
  vpc_id       = module.vpc.vpc_id
  subnet_ids   = module.vpc.public_subnet_ids
  
  # Cibler les instances du module web_servers
  target_instance_ids = module.web_servers.instance_ids
}


DÉPENDANCES ENTRE MODULES:
───────────────────────────

Terraform résout automatiquement les dépendances entre modules.

┌──────────────────────────────────────────────────────────────────────┐
│ ORDRE D'EXÉCUTION (géré automatiquement par Terraform):             │
│                                                                       │
│ 1. module.vpc (créé en premier, pas de dépendances)                │
│ 2. module.web_servers (dépend de vpc)                              │
│ 3. module.database (dépend de vpc ET web_servers)                  │
│ 4. module.alb (dépend de vpc ET web_servers)                       │
└──────────────────────────────────────────────────────────────────────┘


╔═══════════════════════════════════════════════════════════════════════════╗
║ MODULES DU TERRAFORM REGISTRY                                             ║
╚═══════════════════════════════════════════════════════════════════════════╝

Le Terraform Registry (registry.terraform.io) contient des milliers de
modules créés par la communauté.

POURQUOI UTILISER DES MODULES DU REGISTRY?
───────────────────────────────────────────

[OK] Testés et maintenus par la communauté
[OK] Bonnes pratiques intégrées
[OK] Documentation complète
[OK] Gain de temps énorme

EXEMPLE: Module VPC officiel AWS
─────────────────────────────────

Au lieu de créer notre propre module VPC, nous pouvons utiliser le module
officiel qui a des centaines de stars et est très bien maintenu:

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.2"  # <- Toujours spécifier la version!
  
  name = "my-vpc"
  cidr = "10.0.0.0/16"
  
  azs             = ["us-east-1a", "us-east-1b", "us-east-1c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
  
  enable_nat_gateway = true
  enable_vpn_gateway = false
  
  tags = {
    Terraform   = "true"
    Environment = "dev"
  }
}

SYNTAXE POUR MODULES DU REGISTRY:
──────────────────────────────────

source = "NAMESPACE/NAME/PROVIDER"

Exemples:
• terraform-aws-modules/vpc/aws
• terraform-aws-modules/ec2-instance/aws
• terraform-google-modules/network/google

COMMENT TROUVER DES MODULES?
─────────────────────────────

1. Aller sur https://registry.terraform.io
2. Chercher le type de ressource (ex: "vpc", "ec2", "rds")
3. Filtrer par provider (AWS, GCP, Azure)
4. Regarder:
   - Nombre de téléchargements
   - Date de dernière mise à jour
   - Nombre de stars GitHub
   - Qualité de la documentation

MODULES POPULAIRES POUR AWS:
─────────────────────────────

# VPC
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.2"
}

# EC2 Instance
module "ec2_instance" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  version = "5.5.0"
}

# RDS
module "db" {
  source  = "terraform-aws-modules/rds/aws"
  version = "6.3.0"
}

# EKS (Kubernetes)
module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "19.16.0"
}

# S3 Bucket
module "s3_bucket" {
  source  = "terraform-aws-modules/s3-bucket/aws"
  version = "3.15.1"
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ MODULES DEPUIS GITHUB                                                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

Vous pouvez aussi utiliser des modules directement depuis GitHub:

# Module depuis GitHub (branche main)
module "vpc" {
  source = "github.com/votre-org/terraform-modules//vpc"
  # Le // indique le sous-dossier dans le repo
}

# Module depuis un tag spécifique (recommandé)
module "vpc" {
  source = "github.com/votre-org/terraform-modules//vpc?ref=v1.2.3"
}

# Module depuis un commit spécifique
module "vpc" {
  source = "github.com/votre-org/terraform-modules//vpc?ref=abcd1234"
}

# Module privé avec SSH
module "vpc" {
  source = "git::ssh://git@github.com/votre-org/terraform-modules.git//vpc?ref=v1.2.3"
}


╔═══════════════════════════════════════════════════════════════════════════╗
║ MODULES LOCAUX vs DISTANTS                                                ║
╚═══════════════════════════════════════════════════════════════════════════╝

MODULES LOCAUX:
───────────────

module "vpc" {
  source = "./modules/vpc"          # Chemin relatif
  source = "../shared-modules/vpc"   # Dossier parent
  source = "/abs/path/to/module"     # Chemin absolu
}

AVANTAGES:
[OK] Développement rapide
[OK] Pas besoin de versionning
[OK] Facile à debugger

INCONVÉNIENTS:
[X] Pas réutilisable entre projets
[X] Pas de versioning
[X] Difficile à partager

MODULES DISTANTS:
─────────────────

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.2"
}

AVANTAGES:
[OK] Réutilisable partout
[OK] Versionné
[OK] Partageable
[OK] Testé par la communauté

INCONVÉNIENTS:
[X] Plus lent (téléchargement)
[X] Debugging plus complexe

RECOMMENDATION:
───────────────

• **Développement**: Modules locaux
• **Production**: Modules distants versionnés


╔═══════════════════════════════════════════════════════════════════════════╗
║ BONNES PRATIQUES POUR LES MODULES                                         ║
╚═══════════════════════════════════════════════════════════════════════════╝

1. **UN MODULE = UNE RESPONSABILITÉ**
   
   [OK] BON:
   • Module "vpc" -> Crée un VPC
   • Module "ec2" -> Crée des instances EC2
   • Module "rds" -> Crée une base de données
   
   [X] MAUVAIS:
   • Module "infrastructure" -> Crée TOUT


2. **TOUJOURS DOCUMENTER**
   
   Créez un README.md avec:
   • Description
   • Exemple d'utilisation
   • Liste des variables
   • Liste des outputs


3. **VALIDER LES INPUTS**
   
   ```hcl
   variable "instance_count" {
     type = number
     
     validation {
       condition     = var.instance_count >= 1 && var.instance_count <= 10
       error_message = "instance_count doit être entre 1 et 10."
     }
   }
   ```


4. **FOURNIR DES VALEURS PAR DÉFAUT RAISONNABLES**
   
   ```hcl
   variable "instance_type" {
     type    = string
     default = "t2.micro"  # Valeur économique par défaut
   }
   ```


5. **EXPOSER SUFFISAMMENT D'OUTPUTS**
   
   Les modules doivent exposer toutes les informations potentiellement utiles:
   
   ```hcl
   output "vpc_id" { value = aws_vpc.main.id }
   output "vpc_arn" { value = aws_vpc.main.arn }
   output "vpc_cidr" { value = aws_vpc.main.cidr_block }
   # etc.
   ```


6. **VERSIONNER VOS MODULES**
   
   Utilisez des tags Git pour les versions:
   
   ```bash
   git tag v1.0.0
   git push origin v1.0.0
   ```
   
   Puis référencez la version:
   ```hcl
   module "vpc" {
     source = "github.com/org/modules//vpc?ref=v1.0.0"
   }
   ```


7. **TESTER VOS MODULES**
   
   Créez un dossier `examples/` avec des configurations de test:
   
   ```
   modules/vpc/
   ├── main.tf
   ├── variables.tf
   ├── outputs.tf
   ├── README.md
   └── examples/
       ├── simple/
       │   └── main.tf
       └── complete/
           └── main.tf
   ```


═══════════════════════════════════════════════════════════════════════════════
[ARCHIVE] PARTIE 11: STATE MANAGEMENT - LE CERVEAU DE TERRAFORM
═══════════════════════════════════════════════════════════════════════════════

(À suivre dans la prochaine partie...)
═══════════════════════════════════════════════════════════════════════════════
[ARCHIVE] PARTIE 11: STATE MANAGEMENT - LE CERVEAU DE TERRAFORM
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ LE STATE - RAPPEL DES CONCEPTS FONDAMENTAUX                               ║
╚═══════════════════════════════════════════════════════════════════════════╝

Le STATE (terraform.tfstate) est la MÉMOIRE de Terraform.

QUE CONTIENT LE STATE?
──────────────────────

```json
{
  "version": 4,
  "terraform_version": "1.6.0",
  "resources": [
    {
      "type": "aws_instance",
      "name": "web",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "attributes": {
            "id": "i-1234567890abcdef0",
            "ami": "ami-12345678",
            "instance_type": "t2.micro",
            "public_ip": "54.123.45.67",
            "private_ip": "10.0.1.10",
            ...
          }
        }
      ]
    }
  ]
}
```

POURQUOI C'EST CRITIQUE?
─────────────────────────

Sans le state, Terraform ne saurait pas:
• Quelles ressources il a créées
• Leurs IDs dans le cloud
• Les dépendances entre ressources
• Ce qui a changé depuis le dernier apply

ANALOGIE:
─────────

Le state est comme le relevé bancaire de votre infrastructure:
• Il liste tous vos "achats" (ressources créées)
• Il connaît les détails de chaque achat
• Sans lui, impossible de savoir ce que vous possédez


╔═══════════════════════════════════════════════════════════════════════════╗
║ BACKENDS - OÙ STOCKER LE STATE                                            ║
╚═══════════════════════════════════════════════════════════════════════════╝

Par défaut, Terraform stocke le state LOCALEMENT dans terraform.tfstate.

┌───────────────────────────────────────────────────────────────────────────┐
│ LOCAL BACKEND (par défaut)                                               │
└───────────────────────────────────────────────────────────────────────────┘

terraform {
  backend "local" {
    path = "terraform.tfstate"  # Optionnel (c'est le défaut)
  }
}

AVANTAGES:
[OK] Simple
[OK] Rapide
[OK] Pas de configuration

INCONVÉNIENTS:
[X] Pas partageable (fichier local)
[X] Pas de locking (risque de corruption)
[X] Pas de versioning
[X] Contient des secrets en clair
[X] Peut être perdu (crash disque)

QUAND UTILISER?
• Projets personnels
• Prototypage rapide
• Tests locaux

[ATTENTION]  NE JAMAIS UTILISER EN PRODUCTION OU EN ÉQUIPE!


┌───────────────────────────────────────────────────────────────────────────┐
│ S3 BACKEND (recommandé pour AWS)                                         │
└───────────────────────────────────────────────────────────────────────────┘

Stocker le state dans un bucket S3 avec verrouillage DynamoDB.

ÉTAPE 1: Créer l'infrastructure de backend
───────────────────────────────────────────

# backend-setup.tf
# ═══════════════════════════════════════════════════════════════════════
# Configuration initiale du backend (à créer UNE SEULE FOIS)
# ═══════════════════════════════════════════════════════════════════════

# Bucket S3 pour stocker le state
resource "aws_s3_bucket" "terraform_state" {
  bucket = "mon-projet-terraform-state"  # Nom unique globalement
  
  # Empêcher la suppression accidentelle
  lifecycle {
    prevent_destroy = true
  }
  
  tags = {
    Name = "Terraform State Bucket"
  }
}

# Activer le versioning (backup automatique!)
resource "aws_s3_bucket_versioning" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  
  versioning_configuration {
    status = "Enabled"
  }
}

# Chiffrement au repos
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# Bloquer l'accès public (sécurité!)
resource "aws_s3_bucket_public_access_block" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# Table DynamoDB pour le locking
resource "aws_dynamodb_table" "terraform_lock" {
  name         = "terraform-state-lock"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  
  attribute {
    name = "LockID"
    type = "S"
  }
  
  tags = {
    Name = "Terraform State Lock Table"
  }
}

POURQUOI DynamoDB?
──────────────────

DynamoDB fournit un mécanisme de LOCKING:
• Empêche deux personnes de modifier l'infrastructure en même temps
• Évite la corruption du state
• Libère automatiquement le lock après apply

WORKFLOW DE CRÉATION:
─────────────────────

1. Créer backend-setup.tf (code ci-dessus)
2. terraform init
3. terraform apply (crée le bucket et la table)
4. Noter le nom du bucket et de la table
5. [ATTENTION]  NE JAMAIS DÉTRUIRE ces ressources!

ÉTAPE 2: Configurer le backend dans votre projet
─────────────────────────────────────────────────

# versions.tf ou backend.tf
terraform {
  required_version = ">= 1.0"
  
  # Configuration du backend S3
  backend "s3" {
    bucket         = "mon-projet-terraform-state"
    key            = "prod/terraform.tfstate"  # Chemin dans le bucket
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

ORGANISATION DES KEYS:
──────────────────────

Utilisez une structure de chemins claire:

mon-projet-terraform-state/
├── prod/
│   ├── terraform.tfstate           # Production
│   └── terraform.tfstate.backup
├── staging/
│   └── terraform.tfstate           # Staging
├── dev/
│   └── terraform.tfstate           # Dev
└── modules/
    ├── vpc/
    │   └── terraform.tfstate       # Module VPC
    └── ec2/
        └── terraform.tfstate       # Module EC2

INITIALISER AVEC LE BACKEND:
────────────────────────────

# Première fois: migrer du local vers S3
terraform init

# Terraform demande:
# Do you want to copy existing state to the new backend?
#   Enter a value: yes

# [OK] Le state local est copié dans S3
# [OK] terraform.tfstate local peut être supprimé

AVANTAGES DU BACKEND S3:
────────────────────────

[OK] Partageable en équipe
[OK] Versioning automatique (backup)
[OK] Locking (via DynamoDB)
[OK] Chiffré au repos
[OK] Durable (99.999999999% de durabilité)
[OK] Audit (CloudTrail)


┌───────────────────────────────────────────────────────────────────────────┐
│ TERRAFORM CLOUD BACKEND (solution SaaS)                                  │
└───────────────────────────────────────────────────────────────────────────┘

Terraform Cloud est une solution SaaS de HashiCorp pour gérer le state.

CONFIGURATION:
──────────────

terraform {
  cloud {
    organization = "mon-organisation"
    
    workspaces {
      name = "mon-projet-prod"
    }
  }
}

AVANTAGES:
──────────

[OK] Tout géré par HashiCorp
[OK] Interface web pour voir le state
[OK] Historique des runs
[OK] Contrôle d'accès (RBAC)
[OK] Variables chiffrées
[OK] Intégration VCS (GitHub, GitLab)
[OK] Remote execution
[OK] Policy as Code (Sentinel)

INCONVÉNIENTS:
──────────────

[X] Coût (gratuit jusqu'à 5 utilisateurs)
[X] Dépendance externe
[X] Moins de contrôle

QUAND UTILISER?
───────────────

• Équipes de toute taille
• Besoin de gouvernance
• Intégration CI/CD
• Compliance/audit


╔═══════════════════════════════════════════════════════════════════════════╗
║ STATE LOCKING - ÉVITER LES CONFLITS                                       ║
╚═══════════════════════════════════════════════════════════════════════════╝

Le LOCKING empêche les modifications concurrentes du state.

COMMENT ÇA MARCHE?
──────────────────

┌──────────────────────────────────────────────────────────────────────┐
│ SANS LOCKING:                                                         │
│                                                                       │
│ Alice fait:                  Bob fait:                               │
│ terraform plan               terraform plan                          │
│ [lit le state]               [lit le state]                         │
│ terraform apply              terraform apply                         │
│ [modifie le state]           [modifie le state]                     │
│                                                                       │
│ [X] PROBLÈME: Les deux ont lu le MÊME state initial                  │
│ [X] Les modifications de Bob ÉCRASENT celles d'Alice                 │
│ [X] State CORROMPU!                                                   │
└──────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────┐
│ AVEC LOCKING:                                                         │
│                                                                       │
│ Alice fait:                  Bob fait:                               │
│ terraform apply              terraform apply                         │
│ [acquiert le lock]           [tente d'acquérir le lock]             │
│ [lit le state]               [X] BLOQUÉ: "Lock déjà pris"            │
│ [modifie]                    [attend...]                             │
│ [libère le lock]             [OK] Acquiert le lock                     │
│                              [lit le state MIS À JOUR]               │
│                              [modifie]                                │
│                              [libère le lock]                         │
└──────────────────────────────────────────────────────────────────────┘

BACKENDS AVEC LOCKING:
──────────────────────

[OK] S3 (avec DynamoDB)
[OK] Terraform Cloud
[OK] Azure Blob Storage
[OK] Google Cloud Storage
[OK] Consul
[OK] Kubernetes

[X] Local (pas de locking)

FORCER LE DÉVERROUILLAGE:
──────────────────────────

Si un process Terraform crash, le lock peut rester actif.

# Voir les infos du lock
terraform force-unlock -force LOCK_ID

# Exemple:
terraform force-unlock -force 1234abcd-5678-efgh-9012-ijklmnop3456

[ATTENTION]  À utiliser UNIQUEMENT si vous êtes SÛR que personne d'autre ne travaille!


╔═══════════════════════════════════════════════════════════════════════════╗
║ STATE ISOLATION - SÉPARER LES ENVIRONNEMENTS                              ║
╚═══════════════════════════════════════════════════════════════════════════╝

POURQUOI SÉPARER?
─────────────────

Vous ne voulez PAS que:
• Un terraform destroy en dev détruise la prod
• Des tests cassent la staging
• Des erreurs se propagent entre environnements

MÉTHODE 1: States séparés (recommandé)
───────────────────────────────────────

Une configuration Terraform = Un state

Structure:
environments/
├── dev/
│   ├── main.tf
│   ├── variables.tf
│   └── terraform.tfvars
├── staging/
│   ├── main.tf
│   ├── variables.tf
│   └── terraform.tfvars
└── prod/
    ├── main.tf
    ├── variables.tf
    └── terraform.tfvars

Chaque environnement a son propre state:
• dev/terraform.tfstate
• staging/terraform.tfstate
• prod/terraform.tfstate

AVANTAGES:
[OK] Isolation totale
[OK] Impossible de casser prod depuis dev
[OK] Configurations différentes par env

INCONVÉNIENTS:
[X] Duplication de code
[X] Plus difficile à maintenir

MÉTHODE 2: Workspaces
──────────────────────

Un seul code, plusieurs states via les workspaces.

# Créer des workspaces
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod

# Lister les workspaces
terraform workspace list

# Output:
#   default
#   dev
# * staging  <- workspace actuel (*)
#   prod

# Changer de workspace
terraform workspace select prod

UTILISATION DANS LE CODE:
──────────────────────────

resource "aws_instance" "web" {
  ami           = "ami-12345678"
  
  # Instance type selon le workspace
  instance_type = terraform.workspace == "prod" ? "t3.large" : "t2.micro"
  
  tags = {
    Name        = "web-${terraform.workspace}"
    Environment = terraform.workspace
  }
}

# Accéder au workspace actuel
locals {
  environment = terraform.workspace
  is_prod     = terraform.workspace == "prod"
}

BACKEND S3 AVEC WORKSPACES:
───────────────────────────

terraform {
  backend "s3" {
    bucket = "mon-projet-terraform-state"
    key    = "terraform.tfstate"
    region = "us-east-1"
    
    # Les workspaces créent automatiquement des préfixes:
    # env:/dev/terraform.tfstate
    # env:/staging/terraform.tfstate
    # env:/prod/terraform.tfstate
    workspace_key_prefix = "env"
  }
}

AVANTAGES:
[OK] Un seul code
[OK] Facile de changer d'environnement
[OK] States séparés automatiquement

INCONVÉNIENTS:
[X] Risque de confusion (quel workspace?)
[X] Difficile d'avoir des configs très différentes
[X] Facile de se tromper de workspace

RECOMMENDATION:
───────────────

• **Petits projets / prototypes**: Workspaces
• **Production / équipes**: States séparés


╔═══════════════════════════════════════════════════════════════════════════╗
║ STATE REMOTE DATA - PARTAGER ENTRE PROJETS                                ║
╚═══════════════════════════════════════════════════════════════════════════╝

Vous avez plusieurs projets Terraform et vous voulez partager des infos?

EXEMPLE:
────────

Projet "networking" crée le VPC.
Projet "application" utilise ce VPC.

Comment l'application trouve-t-elle l'ID du VPC?

SOLUTION: terraform_remote_state
─────────────────────────────────

┌───────────────────────────────────────────────────────────────────────────┐
│ PROJET 1: networking/                                                    │
└───────────────────────────────────────────────────────────────────────────┘

# networking/main.tf
terraform {
  backend "s3" {
    bucket = "mon-projet-terraform-state"
    key    = "networking/terraform.tfstate"
    region = "us-east-1"
  }
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  
  tags = {
    Name = "main-vpc"
  }
}

# IMPORTANT: Exposer les infos via outputs
output "vpc_id" {
  value = aws_vpc.main.id
}

output "vpc_cidr" {
  value = aws_vpc.main.cidr_block
}


┌───────────────────────────────────────────────────────────────────────────┐
│ PROJET 2: application/                                                   │
└───────────────────────────────────────────────────────────────────────────┘

# application/main.tf
terraform {
  backend "s3" {
    bucket = "mon-projet-terraform-state"
    key    = "application/terraform.tfstate"  # État différent
    region = "us-east-1"
  }
}

# Lire le state du projet networking
data "terraform_remote_state" "networking" {
  backend = "s3"
  
  config = {
    bucket = "mon-projet-terraform-state"
    key    = "networking/terraform.tfstate"  # Même que dans networking/
    region = "us-east-1"
  }
}

# Utiliser les outputs du projet networking
resource "aws_instance" "web" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  
  # Utiliser le VPC créé par networking
  subnet_id = data.terraform_remote_state.networking.outputs.vpc_id
}

# Accéder aux autres outputs
output "networking_vpc_cidr" {
  value = data.terraform_remote_state.networking.outputs.vpc_cidr
}

WORKFLOW:
─────────

1. Déployer le projet networking
   ```bash
   cd networking/
   terraform apply
   ```

2. Les outputs sont stockés dans le state S3

3. Déployer le projet application
   ```bash
   cd application/
   terraform apply
   ```

4. Terraform lit automatiquement le state de networking


╔═══════════════════════════════════════════════════════════════════════════╗
║ BACKUP ET RESTAURATION DU STATE                                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

[ATTENTION]  LE STATE EST CRITIQUE - TOUJOURS AVOIR DES BACKUPS!

┌───────────────────────────────────────────────────────────────────────────┐
│ BACKUP AUTOMATIQUE (S3 avec versioning)                                  │
└───────────────────────────────────────────────────────────────────────────┘

Avec S3 et versioning activé:
• Chaque modification crée une nouvelle version
• Les anciennes versions sont conservées
• Restauration facile

# Lister les versions dans S3
aws s3api list-object-versions \
  --bucket mon-projet-terraform-state \
  --prefix prod/terraform.tfstate

# Télécharger une version spécifique
aws s3api get-object \
  --bucket mon-projet-terraform-state \
  --key prod/terraform.tfstate \
  --version-id VERSION_ID \
  terraform.tfstate.backup


┌───────────────────────────────────────────────────────────────────────────┐
│ BACKUP MANUEL                                                             │
└───────────────────────────────────────────────────────────────────────────┘

# Télécharger le state actuel
terraform state pull > terraform.tfstate.backup.$(date +%Y%m%d_%H%M%S)

# Exemple de script de backup quotidien
#!/bin/bash
# backup-terraform-state.sh

DATE=$(date +%Y%m%d)
BACKUP_DIR="/backups/terraform"
mkdir -p $BACKUP_DIR

cd /path/to/terraform/project
terraform state pull > $BACKUP_DIR/terraform.tfstate.$DATE

# Garder seulement les 30 derniers jours
find $BACKUP_DIR -name "terraform.tfstate.*" -mtime +30 -delete

echo "Backup créé: $BACKUP_DIR/terraform.tfstate.$DATE"


┌───────────────────────────────────────────────────────────────────────────┐
│ RESTAURATION DU STATE                                                     │
└───────────────────────────────────────────────────────────────────────────┘

[ATTENTION]  ATTENTION: Opération DANGEREUSE!

# 1. Faire un backup du state actuel (au cas où)
terraform state pull > terraform.tfstate.before_restore

# 2. Restaurer depuis un backup
terraform state push terraform.tfstate.backup

# 3. Vérifier que tout est OK
terraform plan

# Si le plan montre des changements inattendus, c'est que le backup
# n'était pas le bon. Restaurez le state précédent:
terraform state push terraform.tfstate.before_restore


╔═══════════════════════════════════════════════════════════════════════════╗
║ SÉCURITÉ DU STATE                                                          ║
╚═══════════════════════════════════════════════════════════════════════════╝

[ATTENTION]  LE STATE CONTIENT DES SECRETS EN CLAIR!

SECRETS DANS LE STATE:
──────────────────────

• Mots de passe de bases de données
• Clés API
• Certificats SSL
• Clés privées SSH
• Tokens d'authentification

EXEMPLE:
────────

resource "aws_db_instance" "main" {
  password = "mon_super_password"  # <- Dans votre code
  # ...
}

# Dans le state:
{
  "type": "aws_db_instance",
  "attributes": {
    "password": "mon_super_password",  # <- EN CLAIR!
    ...
  }
}

MESURES DE SÉCURITÉ:
────────────────────

1. **CHIFFREMENT AU REPOS**
   
   ```hcl
   # S3 Backend avec chiffrement
   resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
     bucket = aws_s3_bucket.terraform_state.id
     
     rule {
       apply_server_side_encryption_by_default {
         sse_algorithm = "AES256"
       }
     }
   }
   ```

2. **CHIFFREMENT EN TRANSIT**
   
   Terraform utilise HTTPS pour communiquer avec S3.

3. **CONTRÔLE D'ACCÈS**
   
   ```hcl
   # Policy IAM restrictive
   {
     "Version": "2012-10-17",
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "AWS": [
             "arn:aws:iam::123456789012:role/TerraformRole"
           ]
         },
         "Action": [
           "s3:GetObject",
           "s3:PutObject"
         ],
         "Resource": "arn:aws:s3:::mon-projet-terraform-state/*"
       }
     ]
   }
   ```

4. **AUDIT**
   
   Activer CloudTrail pour auditer les accès au state.

5. **NE JAMAIS COMMITER LE STATE DANS GIT**
   
   ```gitignore
   # .gitignore
   *.tfstate
   *.tfstate.*
   *.tfstate.backup
   ```

6. **UTILISER DES SECRETS MANAGERS**
   
   Plutôt que:
   ```hcl
   password = "mon_password"
   ```
   
   Utiliser:
   ```hcl
   data "aws_secretsmanager_secret_version" "db_password" {
     secret_id = "prod/db/password"
   }
   
   password = jsondecode(data.aws_secretsmanager_secret_version.db_password.secret_string)["password"]
   ```


═══════════════════════════════════════════════════════════════════════════════
═══════════════════════════════════════════════════════════════════════════════
[PRO] PARTIE 12: EXEMPLES PRATIQUES COMPLETS
═══════════════════════════════════════════════════════════════════════════════

Cette section présente des architectures complètes et production-ready.

╔═══════════════════════════════════════════════════════════════════════════╗
║ EXEMPLE 1: APPLICATION DJANGO AVEC AUTO-SCALING                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

ARCHITECTURE:
─────────────

```
Internet
    │
    v
┌──────────────┐
│     ALB      │  <- Application Load Balancer
└──────┬───────┘
       │
       v
┌──────────────┐
│ Auto Scaling │
│    Group     │
│ ┌──┐ ┌──┐   │  <- 2-10 instances Django
│ │EC│ │EC│   │
│ │2 │ │2 │   │
│ └┬─┘ └┬─┘   │
└──┼────┼─────┘
   │    │
   v    v
┌──────────┐    ┌──────────┐    ┌─────────┐
│   RDS    │    │ElastiCache│    │   S3    │
│PostgreSQL│    │  Redis    │    │ Static  │
└──────────┘    └──────────┘    └─────────┘
```

STRUCTURE DU PROJET:
────────────────────

django-terraform/
├── main.tf                 # Configuration principale
├── variables.tf            # Variables
├── outputs.tf              # Outputs
├── versions.tf             # Versions
├── terraform.tfvars        # Valeurs
├── user_data.sh           # Script d'installation
└── modules/
    ├── vpc/               # Module VPC
    ├── alb/               # Module Load Balancer
    ├── asg/               # Module Auto Scaling
    ├── rds/               # Module Database
    └── elasticache/       # Module Cache


┌───────────────────────────────────────────────────────────────────────────┐
│ versions.tf                                                               │
└───────────────────────────────────────────────────────────────────────────┘

terraform {
  required_version = ">= 1.6.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  
  # Backend S3 pour production
  backend "s3" {
    bucket         = "mon-projet-terraform-state"
    key            = "django-app/prod/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}

provider "aws" {
  region = var.aws_region
  
  default_tags {
    tags = {
      Project     = var.project_name
      Environment = var.environment
      ManagedBy   = "Terraform"
    }
  }
}


┌───────────────────────────────────────────────────────────────────────────┐
│ variables.tf                                                              │
└───────────────────────────────────────────────────────────────────────────┘

# ═══════════════════════════════════════════════════════════════════════
# Variables pour l'application Django avec Auto-Scaling
# ═══════════════════════════════════════════════════════════════════════

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Général                                                              │
# └─────────────────────────────────────────────────────────────────────┘

variable "project_name" {
  description = "Nom du projet"
  type        = string
  default     = "django-app"
}

variable "environment" {
  description = "Environnement (dev, staging, prod)"
  type        = string
  
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment doit être dev, staging ou prod."
  }
}

variable "aws_region" {
  description = "Région AWS"
  type        = string
  default     = "us-east-1"
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Réseau                                                               │
# └─────────────────────────────────────────────────────────────────────┘

variable "vpc_cidr" {
  description = "CIDR block pour le VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "availability_zones_count" {
  description = "Nombre de zones de disponibilité à utiliser"
  type        = number
  default     = 2
  
  validation {
    condition     = var.availability_zones_count >= 2
    error_message = "Il faut au moins 2 AZs pour la haute disponibilité."
  }
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Application (EC2/ASG)                                                │
# └─────────────────────────────────────────────────────────────────────┘

variable "instance_type" {
  description = "Type d'instance EC2"
  type        = string
  default     = "t3.small"
}

variable "asg_min_size" {
  description = "Nombre minimum d'instances"
  type        = number
  default     = 2
}

variable "asg_max_size" {
  description = "Nombre maximum d'instances"
  type        = number
  default     = 10
}

variable "asg_desired_capacity" {
  description = "Nombre souhaité d'instances"
  type        = number
  default     = 2
}

variable "key_name" {
  description = "Nom de la paire de clés SSH"
  type        = string
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Base de données (RDS)                                                │
# └─────────────────────────────────────────────────────────────────────┘

variable "db_engine" {
  description = "Moteur de base de données"
  type        = string
  default     = "postgres"
}

variable "db_engine_version" {
  description = "Version du moteur"
  type        = string
  default     = "15.4"
}

variable "db_instance_class" {
  description = "Type d'instance RDS"
  type        = string
  default     = "db.t3.micro"
}

variable "db_allocated_storage" {
  description = "Stockage alloué en GB"
  type        = number
  default     = 20
}

variable "db_name" {
  description = "Nom de la base de données"
  type        = string
  default     = "djangodb"
}

variable "db_username" {
  description = "Nom d'utilisateur de la base de données"
  type        = string
  default     = "dbadmin"
}

variable "db_password" {
  description = "Mot de passe de la base de données"
  type        = string
  sensitive   = true
}

variable "db_multi_az" {
  description = "Activer Multi-AZ pour haute disponibilité"
  type        = bool
  default     = false
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Cache (ElastiCache Redis)                                            │
# └─────────────────────────────────────────────────────────────────────┘

variable "redis_node_type" {
  description = "Type de nœud Redis"
  type        = string
  default     = "cache.t3.micro"
}

variable "redis_num_cache_nodes" {
  description = "Nombre de nœuds Redis"
  type        = number
  default     = 1
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ Django                                                                │
# └─────────────────────────────────────────────────────────────────────┘

variable "django_secret_key" {
  description = "Django SECRET_KEY"
  type        = string
  sensitive   = true
}

variable "django_allowed_hosts" {
  description = "Django ALLOWED_HOSTS"
  type        = list(string)
  default     = ["*"]
}


┌───────────────────────────────────────────────────────────────────────────┐
│ main.tf - Configuration principale                                       │
└───────────────────────────────────────────────────────────────────────────┘

# ═══════════════════════════════════════════════════════════════════════
# Configuration principale de l'application Django
# ═══════════════════════════════════════════════════════════════════════

# ┌─────────────────────────────────────────────────────────────────────┐
# │ DATA SOURCES                                                         │
# └─────────────────────────────────────────────────────────────────────┘

data "aws_availability_zones" "available" {
  state = "available"
}

data "aws_ami" "amazon_linux_2023" {
  most_recent = true
  owners      = ["amazon"]
  
  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
  
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

data "aws_caller_identity" "current" {}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ LOCALS                                                               │
# └─────────────────────────────────────────────────────────────────────┘

locals {
  azs = slice(data.aws_availability_zones.available.names, 0, var.availability_zones_count)
  
  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "Terraform"
  }
  
  # Subnets CIDR calculation
  public_subnets  = [for i in range(var.availability_zones_count) : cidrsubnet(var.vpc_cidr, 8, i)]
  private_subnets = [for i in range(var.availability_zones_count) : cidrsubnet(var.vpc_cidr, 8, i + 10)]
  database_subnets = [for i in range(var.availability_zones_count) : cidrsubnet(var.vpc_cidr, 8, i + 20)]
}


# ═══════════════════════════════════════════════════════════════════════
# VPC ET NETWORKING
# ═══════════════════════════════════════════════════════════════════════

module "vpc" {
  source = "./modules/vpc"
  
  project_name       = var.project_name
  vpc_cidr           = var.vpc_cidr
  availability_zones = local.azs
  
  public_subnet_cidrs   = local.public_subnets
  private_subnet_cidrs  = local.private_subnets
  database_subnet_cidrs = local.database_subnets
  
  enable_nat_gateway = true
  single_nat_gateway = var.environment != "prod"
  
  tags = local.common_tags
}


# ═══════════════════════════════════════════════════════════════════════
# SECURITY GROUPS
# ═══════════════════════════════════════════════════════════════════════

# Security Group pour l'ALB
resource "aws_security_group" "alb" {
  name        = "${var.project_name}-alb-sg"
  description = "Security group pour Application Load Balancer"
  vpc_id      = module.vpc.vpc_id
  
  ingress {
    description = "HTTP depuis Internet"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    description = "HTTPS depuis Internet"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  egress {
    description = "Tout le trafic sortant"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-alb-sg"
  })
}

# Security Group pour les instances EC2
resource "aws_security_group" "app" {
  name        = "${var.project_name}-app-sg"
  description = "Security group pour les instances Django"
  vpc_id      = module.vpc.vpc_id
  
  ingress {
    description     = "HTTP depuis ALB"
    from_port       = 8000
    to_port         = 8000
    protocol        = "tcp"
    security_groups = [aws_security_group.alb.id]
  }
  
  ingress {
    description = "SSH pour administration"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/16"]  # Seulement depuis le VPC
  }
  
  egress {
    description = "Tout le trafic sortant"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-app-sg"
  })
}

# Security Group pour RDS
resource "aws_security_group" "rds" {
  name        = "${var.project_name}-rds-sg"
  description = "Security group pour RDS PostgreSQL"
  vpc_id      = module.vpc.vpc_id
  
  ingress {
    description     = "PostgreSQL depuis instances app"
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.app.id]
  }
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-rds-sg"
  })
}

# Security Group pour ElastiCache
resource "aws_security_group" "redis" {
  name        = "${var.project_name}-redis-sg"
  description = "Security group pour ElastiCache Redis"
  vpc_id      = module.vpc.vpc_id
  
  ingress {
    description     = "Redis depuis instances app"
    from_port       = 6379
    to_port         = 6379
    protocol        = "tcp"
    security_groups = [aws_security_group.app.id]
  }
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-redis-sg"
  })
}


# ═══════════════════════════════════════════════════════════════════════
# S3 BUCKET POUR FICHIERS STATIQUES
# ═══════════════════════════════════════════════════════════════════════

resource "aws_s3_bucket" "static" {
  bucket = "${var.project_name}-static-${data.aws_caller_identity.current.account_id}"
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-static"
  })
}

resource "aws_s3_bucket_public_access_block" "static" {
  bucket = aws_s3_bucket.static.id
  
  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

resource "aws_s3_bucket_policy" "static" {
  bucket = aws_s3_bucket.static.id
  
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "PublicReadGetObject"
        Effect    = "Allow"
        Principal = "*"
        Action    = "s3:GetObject"
        Resource  = "${aws_s3_bucket.static.arn}/*"
      }
    ]
  })
  
  depends_on = [aws_s3_bucket_public_access_block.static]
}

resource "aws_s3_bucket" "media" {
  bucket = "${var.project_name}-media-${data.aws_caller_identity.current.account_id}"
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-media"
  })
}

resource "aws_s3_bucket_versioning" "media" {
  bucket = aws_s3_bucket.media.id
  
  versioning_configuration {
    status = "Enabled"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# IAM ROLE POUR EC2
# ═══════════════════════════════════════════════════════════════════════

resource "aws_iam_role" "ec2_role" {
  name = "${var.project_name}-ec2-role"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      }
    ]
  })
  
  tags = local.common_tags
}

# Policy pour accès S3
resource "aws_iam_role_policy" "s3_access" {
  name = "s3-access"
  role = aws_iam_role.ec2_role.id
  
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:PutObject",
          "s3:GetObject",
          "s3:DeleteObject",
          "s3:ListBucket"
        ]
        Resource = [
          aws_s3_bucket.static.arn,
          "${aws_s3_bucket.static.arn}/*",
          aws_s3_bucket.media.arn,
          "${aws_s3_bucket.media.arn}/*"
        ]
      }
    ]
  })
}

# Policy pour CloudWatch Logs
resource "aws_iam_role_policy_attachment" "cloudwatch_logs" {
  role       = aws_iam_role.ec2_role.name
  policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
}

resource "aws_iam_instance_profile" "ec2_profile" {
  name = "${var.project_name}-ec2-profile"
  role = aws_iam_role.ec2_role.name
  
  tags = local.common_tags
}


# ═══════════════════════════════════════════════════════════════════════
# RDS POSTGRESQL
# ═══════════════════════════════════════════════════════════════════════

resource "aws_db_subnet_group" "main" {
  name       = "${var.project_name}-db-subnet"
  subnet_ids = module.vpc.database_subnet_ids
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-db-subnet"
  })
}

resource "aws_db_instance" "main" {
  identifier     = "${var.project_name}-db"
  engine         = var.db_engine
  engine_version = var.db_engine_version
  instance_class = var.db_instance_class
  
  allocated_storage     = var.db_allocated_storage
  storage_type          = "gp3"
  storage_encrypted     = true
  
  db_name  = var.db_name
  username = var.db_username
  password = var.db_password
  
  multi_az               = var.db_multi_az
  db_subnet_group_name   = aws_db_subnet_group.main.name
  vpc_security_group_ids = [aws_security_group.rds.id]
  
  backup_retention_period = 7
  backup_window          = "03:00-04:00"
  maintenance_window     = "mon:04:00-mon:05:00"
  
  skip_final_snapshot       = var.environment != "prod"
  final_snapshot_identifier = var.environment == "prod" ? "${var.project_name}-final-snapshot" : null
  
  enabled_cloudwatch_logs_exports = ["postgresql"]
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-db"
  })
}


# ═══════════════════════════════════════════════════════════════════════
# ELASTICACHE REDIS
# ═══════════════════════════════════════════════════════════════════════

resource "aws_elasticache_subnet_group" "main" {
  name       = "${var.project_name}-redis-subnet"
  subnet_ids = module.vpc.private_subnet_ids
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-redis-subnet"
  })
}

resource "aws_elasticache_cluster" "redis" {
  cluster_id           = "${var.project_name}-redis"
  engine               = "redis"
  node_type            = var.redis_node_type
  num_cache_nodes      = var.redis_num_cache_nodes
  parameter_group_name = "default.redis7"
  engine_version       = "7.0"
  port                 = 6379
  
  subnet_group_name    = aws_elasticache_subnet_group.main.name
  security_group_ids   = [aws_security_group.redis.id]
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-redis"
  })
}


# ═══════════════════════════════════════════════════════════════════════
# APPLICATION LOAD BALANCER
# ═══════════════════════════════════════════════════════════════════════

resource "aws_lb" "main" {
  name               = "${var.project_name}-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = module.vpc.public_subnet_ids
  
  enable_deletion_protection = var.environment == "prod"
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-alb"
  })
}

resource "aws_lb_target_group" "app" {
  name     = "${var.project_name}-tg"
  port     = 8000
  protocol = "HTTP"
  vpc_id   = module.vpc.vpc_id
  
  health_check {
    enabled             = true
    healthy_threshold   = 2
    unhealthy_threshold = 2
    timeout             = 5
    interval            = 30
    path                = "/health/"
    matcher             = "200"
  }
  
  deregistration_delay = 30
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-tg"
  })
}

resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.main.arn
  port              = 80
  protocol          = "HTTP"
  
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.app.arn
  }
}


# ═══════════════════════════════════════════════════════════════════════
# LAUNCH TEMPLATE
# ═══════════════════════════════════════════════════════════════════════

resource "aws_launch_template" "app" {
  name_prefix   = "${var.project_name}-"
  image_id      = data.aws_ami.amazon_linux_2023.id
  instance_type = var.instance_type
  key_name      = var.key_name
  
  iam_instance_profile {
    name = aws_iam_instance_profile.ec2_profile.name
  }
  
  vpc_security_group_ids = [aws_security_group.app.id]
  
  user_data = base64encode(templatefile("${path.module}/user_data.sh", {
    project_name      = var.project_name
    db_host           = aws_db_instance.main.address
    db_port           = aws_db_instance.main.port
    db_name           = var.db_name
    db_username       = var.db_username
    db_password       = var.db_password
    redis_endpoint    = aws_elasticache_cluster.redis.cache_nodes[0].address
    redis_port        = aws_elasticache_cluster.redis.cache_nodes[0].port
    s3_static_bucket  = aws_s3_bucket.static.bucket
    s3_media_bucket   = aws_s3_bucket.media.bucket
    django_secret_key = var.django_secret_key
    allowed_hosts     = join(",", concat(var.django_allowed_hosts, [aws_lb.main.dns_name]))
  }))
  
  monitoring {
    enabled = true
  }
  
  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"
    http_put_response_hop_limit = 1
  }
  
  tag_specifications {
    resource_type = "instance"
    tags = merge(local.common_tags, {
      Name = "${var.project_name}-instance"
    })
  }
  
  lifecycle {
    create_before_destroy = true
  }
}


# ═══════════════════════════════════════════════════════════════════════
# AUTO SCALING GROUP
# ═══════════════════════════════════════════════════════════════════════

resource "aws_autoscaling_group" "app" {
  name = "${var.project_name}-asg"
  
  min_size         = var.asg_min_size
  max_size         = var.asg_max_size
  desired_capacity = var.asg_desired_capacity
  
  health_check_type         = "ELB"
  health_check_grace_period = 300
  
  vpc_zone_identifier = module.vpc.private_subnet_ids
  target_group_arns   = [aws_lb_target_group.app.arn]
  
  launch_template {
    id      = aws_launch_template.app.id
    version = "$Latest"
  }
  
  enabled_metrics = [
    "GroupDesiredCapacity",
    "GroupInServiceInstances",
    "GroupMinSize",
    "GroupMaxSize",
    "GroupTotalInstances"
  ]
  
  tag {
    key                 = "Name"
    value               = "${var.project_name}-asg-instance"
    propagate_at_launch = true
  }
  
  dynamic "tag" {
    for_each = local.common_tags
    content {
      key                 = tag.key
      value               = tag.value
      propagate_at_launch = true
    }
  }
  
  lifecycle {
    create_before_destroy = true
    ignore_changes        = [desired_capacity]
  }
}


# ═══════════════════════════════════════════════════════════════════════
# AUTO SCALING POLICIES
# ═══════════════════════════════════════════════════════════════════════

# Policy pour augmenter (scale up)
resource "aws_autoscaling_policy" "scale_up" {
  name                   = "${var.project_name}-scale-up"
  scaling_adjustment     = 1
  adjustment_type        = "ChangeInCapacity"
  cooldown               = 300
  autoscaling_group_name = aws_autoscaling_group.app.name
}

# CloudWatch Alarm pour déclencher scale up
resource "aws_cloudwatch_metric_alarm" "cpu_high" {
  alarm_name          = "${var.project_name}-cpu-high"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = 120
  statistic           = "Average"
  threshold           = 70
  
  dimensions = {
    AutoScalingGroupName = aws_autoscaling_group.app.name
  }
  
  alarm_description = "CPU utilization high - scale up"
  alarm_actions     = [aws_autoscaling_policy.scale_up.arn]
}

# Policy pour diminuer (scale down)
resource "aws_autoscaling_policy" "scale_down" {
  name                   = "${var.project_name}-scale-down"
  scaling_adjustment     = -1
  adjustment_type        = "ChangeInCapacity"
  cooldown               = 300
  autoscaling_group_name = aws_autoscaling_group.app.name
}

# CloudWatch Alarm pour déclencher scale down
resource "aws_cloudwatch_metric_alarm" "cpu_low" {
  alarm_name          = "${var.project_name}-cpu-low"
  comparison_operator = "LessThanThreshold"
  evaluation_periods  = 2
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = 120
  statistic           = "Average"
  threshold           = 20
  
  dimensions = {
    AutoScalingGroupName = aws_autoscaling_group.app.name
  }
  
  alarm_description = "CPU utilization low - scale down"
  alarm_actions     = [aws_autoscaling_policy.scale_down.arn]
}


┌───────────────────────────────────────────────────────────────────────────┐
│ user_data.sh - Script d'installation Django                              │
└───────────────────────────────────────────────────────────────────────────┘

#!/bin/bash
# ═══════════════════════════════════════════════════════════════════════
# Script d'initialisation pour instance EC2 Django
# ═══════════════════════════════════════════════════════════════════════

set -e

exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1

echo "═══════════════════════════════════════════════════════════"
echo "Début de l'installation Django - $(date)"
echo "═══════════════════════════════════════════════════════════"

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Mise à jour du système                                               │
# └─────────────────────────────────────────────────────────────────────┘

dnf update -y

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Installation de Python et dépendances                                │
# └─────────────────────────────────────────────────────────────────────┘

dnf install -y \
    python3.11 \
    python3.11-pip \
    python3.11-devel \
    postgresql15 \
    postgresql15-devel \
    gcc \
    git \
    nginx \
    supervisor

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Créer utilisateur pour l'application                                 │
# └─────────────────────────────────────────────────────────────────────┘

useradd -m -s /bin/bash django

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Créer la structure de l'application                                  │
# └─────────────────────────────────────────────────────────────────────┘

APP_DIR="/opt/${project_name}"
mkdir -p $APP_DIR
chown django:django $APP_DIR

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Créer un projet Django minimal                                       │
# └─────────────────────────────────────────────────────────────────────┘

cat > $APP_DIR/requirements.txt <<EOF
Django==4.2.7
psycopg2-binary==2.9.9
django-redis==5.4.0
gunicorn==21.2.0
boto3==1.29.7
django-storages==1.14.2
python-dotenv==1.0.0
EOF

# Installer les dépendances
su - django -c "python3.11 -m venv $APP_DIR/venv"
su - django -c "source $APP_DIR/venv/bin/activate && pip install -r $APP_DIR/requirements.txt"

# Créer le projet Django
su - django -c "source $APP_DIR/venv/bin/activate && cd $APP_DIR && django-admin startproject myproject ."

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Configuration Django                                                  │
# └─────────────────────────────────────────────────────────────────────┘

cat > $APP_DIR/myproject/settings_prod.py <<'SETTINGS'
from .settings import *
import os

# Security
DEBUG = False
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
ALLOWED_HOSTS = os.environ.get('DJANGO_ALLOWED_HOSTS', '').split(',')

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('DB_NAME'),
        'USER': os.environ.get('DB_USER'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': os.environ.get('DB_HOST'),
        'PORT': os.environ.get('DB_PORT'),
        'CONN_MAX_AGE': 600,
    }
}

# Cache (Redis)
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': f"redis://{os.environ.get('REDIS_HOST')}:{os.environ.get('REDIS_PORT')}/1",
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}

# Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'

# Static files (S3)
AWS_STORAGE_BUCKET_NAME = os.environ.get('S3_STATIC_BUCKET')
AWS_S3_REGION_NAME = 'us-east-1'
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/static/'
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

# Media files (S3)
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_MEDIA_BUCKET_NAME = os.environ.get('S3_MEDIA_BUCKET')
MEDIA_URL = f'https://{AWS_MEDIA_BUCKET_NAME}.s3.amazonaws.com/'

# Logging
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'INFO',
    },
}
SETTINGS

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Créer une vue de health check                                        │
# └─────────────────────────────────────────────────────────────────────┘

cat > $APP_DIR/myproject/views.py <<'VIEWS'
from django.http import JsonResponse
from django.core.cache import cache
from django.db import connection

def health_check(request):
    """Health check endpoint pour ALB"""
    try:
        # Test database
        with connection.cursor() as cursor:
            cursor.execute("SELECT 1")
        
        # Test cache
        cache.set('health_check', 'ok', 10)
        cache_status = cache.get('health_check')
        
        return JsonResponse({
            'status': 'healthy',
            'database': 'ok',
            'cache': 'ok' if cache_status else 'error'
        })
    except Exception as e:
        return JsonResponse({
            'status': 'unhealthy',
            'error': str(e)
        }, status=500)
VIEWS

# Ajouter la route
cat > $APP_DIR/myproject/urls_health.py <<'URLS'
from django.urls import path
from . import views

urlpatterns = [
    path('health/', views.health_check),
]
URLS

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Fichier d'environnement                                              │
# └─────────────────────────────────────────────────────────────────────┘

cat > $APP_DIR/.env <<EOF
DJANGO_SETTINGS_MODULE=myproject.settings_prod
DJANGO_SECRET_KEY=${django_secret_key}
DJANGO_ALLOWED_HOSTS=${allowed_hosts}
DB_NAME=${db_name}
DB_USER=${db_username}
DB_PASSWORD=${db_password}
DB_HOST=${db_host}
DB_PORT=${db_port}
REDIS_HOST=${redis_endpoint}
REDIS_PORT=${redis_port}
S3_STATIC_BUCKET=${s3_static_bucket}
S3_MEDIA_BUCKET=${s3_media_bucket}
EOF

chown django:django $APP_DIR/.env
chmod 600 $APP_DIR/.env

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Migrations et collectstatic                                          │
# └─────────────────────────────────────────────────────────────────────┘

su - django -c "cd $APP_DIR && source venv/bin/activate && source .env && python manage.py migrate"
su - django -c "cd $APP_DIR && source venv/bin/activate && source .env && python manage.py collectstatic --noinput"

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Configuration Supervisor                                              │
# └─────────────────────────────────────────────────────────────────────┘

cat > /etc/supervisor/conf.d/${project_name}.conf <<EOF
[program:${project_name}]
directory=$APP_DIR
command=$APP_DIR/venv/bin/gunicorn --bind 0.0.0.0:8000 --workers 4 --timeout 60 myproject.wsgi:application
user=django
autostart=true
autorestart=true
stderr_logfile=/var/log/${project_name}.err.log
stdout_logfile=/var/log/${project_name}.out.log
environment=PATH="$APP_DIR/venv/bin"
EOF

supervisorctl reread
supervisorctl update
supervisorctl start ${project_name}

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Configuration Nginx                                                   │
# └─────────────────────────────────────────────────────────────────────┘

cat > /etc/nginx/conf.d/${project_name}.conf <<'NGINX'
upstream django {
    server 127.0.0.1:8000;
}

server {
    listen 80;
    server_name _;
    
    client_max_body_size 10M;
    
    location / {
        proxy_pass http://django;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
NGINX

nginx -t
systemctl enable nginx
systemctl restart nginx

echo "═══════════════════════════════════════════════════════════"
echo "Installation terminée - $(date)"
echo "═══════════════════════════════════════════════════════════"


┌───────────────────────────────────────────────────────────────────────────┐
│ outputs.tf                                                                │
└───────────────────────────────────────────────────────────────────────────┘

output "alb_dns_name" {
  description = "DNS name du Load Balancer"
  value       = aws_lb.main.dns_name
}

output "application_url" {
  description = "URL de l'application"
  value       = "http://${aws_lb.main.dns_name}"
}

output "rds_endpoint" {
  description = "Endpoint de la base de données"
  value       = aws_db_instance.main.endpoint
  sensitive   = true
}

output "redis_endpoint" {
  description = "Endpoint Redis"
  value       = "${aws_elasticache_cluster.redis.cache_nodes[0].address}:${aws_elasticache_cluster.redis.cache_nodes[0].port}"
}

output "s3_static_bucket" {
  description = "Bucket S3 pour les fichiers statiques"
  value       = aws_s3_bucket.static.bucket
}

output "s3_media_bucket" {
  description = "Bucket S3 pour les médias"
  value       = aws_s3_bucket.media.bucket
}


┌───────────────────────────────────────────────────────────────────────────┐
│ terraform.tfvars.example                                                  │
└───────────────────────────────────────────────────────────────────────────┘

project_name = "mydjango"
environment  = "prod"
aws_region   = "us-east-1"

# Network
vpc_cidr                 = "10.0.0.0/16"
availability_zones_count = 2

# Application
instance_type        = "t3.small"
asg_min_size         = 2
asg_max_size         = 10
asg_desired_capacity = 2
key_name             = "mykey"

# Database
db_instance_class    = "db.t3.micro"
db_allocated_storage = 20
db_name              = "djangodb"
db_username          = "dbadmin"
db_password          = "CHANGE_ME"  # [ATTENTION]  À mettre dans AWS Secrets Manager!
db_multi_az          = true

# Cache
redis_node_type       = "cache.t3.micro"
redis_num_cache_nodes = 1

# Django
django_secret_key    = "CHANGE_ME"  # [ATTENTION]  À générer avec django
django_allowed_hosts = ["*"]


╔═══════════════════════════════════════════════════════════════════════════╗
║ DÉPLOIEMENT                                                                ║
╚═══════════════════════════════════════════════════════════════════════════╝

# 1. Initialiser
terraform init

# 2. Plan
terraform plan -out=tfplan

# 3. Apply
terraform apply tfplan

# 4. Attendre 5-10 minutes que tout se mette en place

# 5. Tester
ALB_DNS=$(terraform output -raw alb_dns_name)
curl http://$ALB_DNS/health/

# Output attendu:
# {"status":"healthy","database":"ok","cache":"ok"}


╔═══════════════════════════════════════════════════════════════════════════╗
║ EXEMPLE 2: API FASTAPI SUR AWS LAMBDA                                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

ARCHITECTURE SERVERLESS:
────────────────────────

```
Internet
    │
    v
┌─────────────┐
│ API Gateway │
└──────┬──────┘
       │
       v
┌─────────────┐      ┌──────────┐
│   Lambda    │──────│ DynamoDB │
│  (FastAPI)  │      └──────────┘
└─────────────┘
       │
       v
┌─────────────┐
│ CloudWatch  │
│    Logs     │
└─────────────┘
```

Cette architecture est **serverless** : pas de serveurs à gérer!

(Code complet pour Lambda + API Gateway disponible sur demande)


═══════════════════════════════════════════════════════════════════════════════

Voilà la PARTIE 12 complète avec un exemple Django production-ready incluant:
- VPC avec haute disponibilité
- Auto Scaling Group
- Application Load Balancer
- RDS PostgreSQL
- ElastiCache Redis
- S3 pour statiques et médias
- IAM roles et policies
- CloudWatch monitoring

Le code est fonctionnel et peut être déployé directement!


═══════════════════════════════════════════════════════════════════════════════
[OUTIL] PARTIE 13: TROUBLESHOOTING ET BONNES PRATIQUES
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ ERREURS COURANTES ET SOLUTIONS                                            ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ ERREUR 1: State Lock                                                     │
└───────────────────────────────────────────────────────────────────────────┘

ERREUR:
```
Error: Error acquiring the state lock

Error message: ConditionalCheckFailedException: The conditional request failed
Lock Info:
  ID:        abc123-456def-789ghi
  Path:      mon-bucket/terraform.tfstate
  Operation: OperationTypeApply
  Who:       alice@exemple.com
  Version:   1.6.0
  Created:   2024-01-15 14:30:25
```

CAUSES:
• Un autre utilisateur fait un apply en même temps
• Un process Terraform a crashé sans libérer le lock
• Problème réseau pendant l'apply

SOLUTIONS:
1. Attendre que l'autre utilisateur finisse
2. Vérifier qu'aucun process terraform ne tourne
3. Si vous êtes SÛR que personne ne travaille:
   ```bash
   terraform force-unlock abc123-456def-789ghi
   ```


┌───────────────────────────────────────────────────────────────────────────┐
│ ERREUR 2: Credentials AWS invalides                                      │
└───────────────────────────────────────────────────────────────────────────┘

ERREUR:
```
Error: error configuring Terraform AWS Provider: error validating 
provider credentials: error calling sts:GetCallerIdentity: 
InvalidClientTokenId: The security token included in the request is invalid
```

CAUSES:
• Credentials AWS manquantes ou incorrectes
• Credentials expirées (temporary credentials)
• Mauvaise région

SOLUTIONS:
```bash
# Vérifier les credentials
aws sts get-caller-identity

# Si erreur, configurer:
aws configure
# ou
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_DEFAULT_REGION="us-east-1"
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ERREUR 3: Ressource déjà existe                                          │
└───────────────────────────────────────────────────────────────────────────┘

ERREUR:
```
Error: Error creating Security Group: InvalidGroup.Duplicate: 
The security group 'my-sg' already exists
```

CAUSES:
• Vous essayez de créer une ressource qui existe déjà
• Nom de ressource en conflit

SOLUTIONS:
1. Importer la ressource existante:
   ```bash
   terraform import aws_security_group.my_sg sg-12345678
   ```

2. Ou changer le nom dans votre configuration


┌───────────────────────────────────────────────────────────────────────────┐
│ ERREUR 4: Circular dependency                                            │
└───────────────────────────────────────────────────────────────────────────┘

ERREUR:
```
Error: Cycle: aws_security_group.a, aws_security_group.b
```

CAUSE:
Deux ressources dépendent l'une de l'autre.

EXEMPLE PROBLÉMATIQUE:
```hcl
resource "aws_security_group" "a" {
  ingress {
    security_groups = [aws_security_group.b.id]
  }
}

resource "aws_security_group" "b" {
  ingress {
    security_groups = [aws_security_group.a.id]
  }
}
```

SOLUTION:
Utiliser des règles séparées:
```hcl
resource "aws_security_group" "a" {
  name = "sg-a"
}

resource "aws_security_group" "b" {
  name = "sg-b"
}

resource "aws_security_group_rule" "a_to_b" {
  type                     = "ingress"
  from_port                = 443
  to_port                  = 443
  protocol                 = "tcp"
  security_group_id        = aws_security_group.b.id
  source_security_group_id = aws_security_group.a.id
}
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ DEBUGGING - ACTIVER LES LOGS                                              ║
╚═══════════════════════════════════════════════════════════════════════════╝

NIVEAUX DE LOG:
───────────────

```bash
# TRACE: Le plus verbeux (tout)
export TF_LOG=TRACE

# DEBUG: Informations de debugging
export TF_LOG=DEBUG

# INFO: Informations générales
export TF_LOG=INFO

# WARN: Avertissements uniquement
export TF_LOG=WARN

# ERROR: Erreurs uniquement
export TF_LOG=ERROR

# Désactiver
unset TF_LOG
```

SAUVEGARDER LES LOGS:
─────────────────────

```bash
# Dans un fichier
export TF_LOG=DEBUG
export TF_LOG_PATH=./terraform-debug.log
terraform apply
```

LOGS PAR COMPOSANT:
───────────────────

```bash
# Provider AWS seulement
export TF_LOG_PROVIDER=DEBUG

# Core Terraform seulement
export TF_LOG_CORE=DEBUG
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ BONNES PRATIQUES - CHECKLIST COMPLÈTE                                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ 1. ORGANISATION DU CODE                                                   │
└───────────────────────────────────────────────────────────────────────────┘

[OK] Séparer les fichiers par fonction
   • main.tf: Ressources principales
   • variables.tf: Variables
   • outputs.tf: Outputs
   • versions.tf: Versions et providers

[OK] Un module = Une responsabilité

[OK] Nommer clairement les ressources
   • Préfixe avec le projet: "${var.project_name}-web-sg"
   • Suffixe avec le type: "web-instance", "db-subnet"

[OK] Utiliser des commentaires
   ```hcl
   # Créer le VPC principal
   resource "aws_vpc" "main" {
     # CIDR: 10.0.0.0/16 = 65,536 IPs
     cidr_block = "10.0.0.0/16"
   }
   ```


┌───────────────────────────────────────────────────────────────────────────┐
│ 2. VERSIONING                                                             │
└───────────────────────────────────────────────────────────────────────────┘

[OK] Fixer les versions de Terraform
   ```hcl
   terraform {
     required_version = ">= 1.6.0, < 2.0.0"
   }
   ```

[OK] Fixer les versions des providers
   ```hcl
   required_providers {
     aws = {
       source  = "hashicorp/aws"
       version = "~> 5.0"  # 5.x mais pas 6.0
     }
   }
   ```

[OK] Commiter le fichier .terraform.lock.hcl
   C'est le "package-lock.json" de Terraform


┌───────────────────────────────────────────────────────────────────────────┐
│ 3. VARIABLES ET SECRETS                                                   │
└───────────────────────────────────────────────────────────────────────────┘

[OK] Toujours documenter les variables
   ```hcl
   variable "instance_type" {
     description = "Type d'instance EC2 (t2.micro, t2.small, etc.)"
     type        = string
     default     = "t2.micro"
   }
   ```

[OK] Valider les variables
   ```hcl
   validation {
     condition     = var.instance_count >= 1
     error_message = "Il faut au moins 1 instance."
   }
   ```

[OK] NE JAMAIS hardcoder de secrets
   [X] MAUVAIS:
   ```hcl
   password = "mon_password"
   ```
   
   [OK] BON:
   ```hcl
   data "aws_secretsmanager_secret_version" "db" {
     secret_id = "prod/db/password"
   }
   ```

[OK] Marquer les variables sensibles
   ```hcl
   variable "db_password" {
     type      = string
     sensitive = true
   }
   ```


┌───────────────────────────────────────────────────────────────────────────┐
│ 4. STATE MANAGEMENT                                                       │
└───────────────────────────────────────────────────────────────────────────┘

[OK] Utiliser un backend distant en production
   • S3 + DynamoDB pour AWS
   • Terraform Cloud pour tous les providers

[OK] Activer le versioning du backend
   ```hcl
   resource "aws_s3_bucket_versioning" "state" {
     versioning_configuration {
       status = "Enabled"
     }
   }
   ```

[OK] Chiffrer le state
   ```hcl
   terraform {
     backend "s3" {
       encrypt = true
     }
   }
   ```

[OK] Séparer les states par environnement
   • dev/terraform.tfstate
   • staging/terraform.tfstate
   • prod/terraform.tfstate

[OK] NE JAMAIS éditer le state à la main
   Utiliser terraform state move, rm, etc.


┌───────────────────────────────────────────────────────────────────────────┐
│ 5. SÉCURITÉ                                                               │
└───────────────────────────────────────────────────────────────────────────┘

[OK] Principle of Least Privilege
   Donner le minimum de permissions nécessaires

[OK] Utiliser des IAM roles plutôt que des clés
   ```hcl
   provider "aws" {
     assume_role {
       role_arn = "arn:aws:iam::123456789012:role/TerraformRole"
     }
   }
   ```

[OK] Activer le chiffrement
   • RDS: storage_encrypted = true
   • S3: server-side encryption
   • EBS: encrypted = true

[OK] Ne pas exposer des ressources publiquement
   ```hcl
   # Vérifier avant de déployer
   cidr_blocks = ["0.0.0.0/0"]  # [ATTENTION] Tout Internet!
   ```

[OK] Utiliser des Security Groups restrictifs

[OK] Scanner votre code avec checkov ou tfsec
   ```bash
   pip install checkov
   checkov -d .
   ```


┌───────────────────────────────────────────────────────────────────────────┐
│ 6. TESTS                                                                  │
└───────────────────────────────────────────────────────────────────────────┘

[OK] Toujours faire terraform validate
   ```bash
   terraform validate
   ```

[OK] Toujours faire terraform fmt
   ```bash
   terraform fmt -recursive
   ```

[OK] Toujours faire terraform plan avant apply
   ```bash
   terraform plan -out=tfplan
   terraform apply tfplan
   ```

[OK] Tester dans un environnement de dev d'abord

[OK] Utiliser terraform plan dans votre CI/CD
   ```yaml
   # GitHub Actions
   - name: Terraform Plan
     run: terraform plan -no-color
   ```


┌───────────────────────────────────────────────────────────────────────────┐
│ 7. CI/CD                                                                  │
└───────────────────────────────────────────────────────────────────────────┘

[OK] Automatiser terraform fmt check
   ```yaml
   - name: Check formatting
     run: terraform fmt -check -recursive
   ```

[OK] Automatiser terraform validate
   ```yaml
   - name: Validate
     run: terraform validate
   ```

[OK] Commenter les Pull Requests avec le plan
   Les développeurs voient ce qui va changer

[OK] Exiger une revue de code avant apply

[OK] Ne jamais auto-approve en production
   Toujours une validation manuelle


┌───────────────────────────────────────────────────────────────────────────┐
│ 8. DOCUMENTATION                                                          │
└───────────────────────────────────────────────────────────────────────────┘

[OK] README.md à la racine
   • Prérequis
   • Comment déployer
   • Architecture
   • Contacts

[OK] terraform-docs pour générer la doc
   ```bash
   terraform-docs markdown table . > README.md
   ```

[OK] Diagrammes d'architecture
   Utilisez draw.io, Lucidchart, ou Diagrams as Code

[OK] Documenter les décisions importantes
   Pourquoi ce choix? Quelles alternatives?


┌───────────────────────────────────────────────────────────────────────────┐
│ 9. PERFORMANCES                                                           │
└───────────────────────────────────────────────────────────────────────────┘

[OK] Utiliser -parallelism pour les gros projets
   ```bash
   terraform apply -parallelism=20
   ```

[OK] Diviser en modules pour le parallélisme

[OK] Utiliser -target pour les tests rapides
   ```bash
   terraform apply -target=aws_instance.test
   ```

[OK] Éviter les boucles imbriquées
   [X] for_each dans for_each = lent


┌───────────────────────────────────────────────────────────────────────────┐
│ 10. MAINTENANCE                                                           │
└───────────────────────────────────────────────────────────────────────────┘

[OK] Mettre à jour régulièrement
   • Terraform
   • Providers
   • Modules

[OK] Lire les CHANGELOG avant de mettre à jour

[OK] Tester les mises à jour en dev d'abord

[OK] Utiliser Dependabot ou Renovate
   Automatise les PRs de mise à jour


╔═══════════════════════════════════════════════════════════════════════════╗
║ WORKFLOW RECOMMANDÉ POUR UNE ÉQUIPE                                       ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌────────────────────────────────────────────────────────────────────────┐
│ WORKFLOW COMPLET:                                                       │
│                                                                         │
│ 1. DÉVELOPPEMENT (développeur)                                        │
│    • Créer une branche Git                                            │
│    • Écrire/modifier du code Terraform                                │
│    • terraform fmt                                                     │
│    • terraform validate                                                │
│    • terraform plan (vérifier localement)                             │
│    • Commit & Push                                                     │
│                                                                         │
│ 2. PULL REQUEST                                                        │
│    • CI run: fmt check, validate, plan                                │
│    • Bot commente le PR avec le plan                                  │
│    • Revue de code par un collègue                                    │
│    • Approbation requise                                              │
│                                                                         │
│ 3. MERGE (automatique ou manuel)                                      │
│    • terraform plan (une dernière fois)                               │
│    • Attendre approbation manuelle (production)                       │
│    • terraform apply                                                   │
│    • Tests post-déploiement                                           │
│    • Notification (Slack, email)                                       │
│                                                                         │
│ 4. MONITORING                                                          │
│    • Vérifier les métriques                                           │
│    • Vérifier les logs                                                │
│    • Alertes en cas de problème                                       │
└────────────────────────────────────────────────────────────────────────┘


╔═══════════════════════════════════════════════════════════════════════════╗
║ OUTILS COMPLÉMENTAIRES                                                    ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ FORMATAGE ET LINTING                                                      │
└───────────────────────────────────────────────────────────────────────────┘

**terraform fmt**
• Formateur officiel de Terraform

**tflint**
• Linter avancé avec règles personnalisables
```bash
brew install tflint
tflint --init
tflint
```


┌───────────────────────────────────────────────────────────────────────────┐
│ SÉCURITÉ                                                                  │
└───────────────────────────────────────────────────────────────────────────┘

**checkov**
• Scanner de sécurité et compliance
```bash
pip install checkov
checkov -d .
```

**tfsec**
• Scanner de sécurité spécialisé Terraform
```bash
brew install tfsec
tfsec .
```

**terrascan**
• Scanner multi-cloud
```bash
brew install terrascan
terrascan scan
```


┌───────────────────────────────────────────────────────────────────────────┐
│ COÛTS                                                                     │
└───────────────────────────────────────────────────────────────────────────┘

**infracost**
• Estimation des coûts cloud
```bash
brew install infracost
infracost breakdown --path .
```

**Cost estimation avant apply:**
```bash
infracost diff --path . --compare-to tfplan.json
```


┌───────────────────────────────────────────────────────────────────────────┐
│ DOCUMENTATION                                                             │
└───────────────────────────────────────────────────────────────────────────┘

**terraform-docs**
• Génération automatique de documentation
```bash
brew install terraform-docs
terraform-docs markdown table . > README.md
```


┌───────────────────────────────────────────────────────────────────────────┐
│ VISUALISATION                                                             │
└───────────────────────────────────────────────────────────────────────────┘

**terraform graph**
• Génère un graphe des dépendances
```bash
terraform graph | dot -Tpng > graph.png
```

**Rover (Terraform visualizer)**
• Interface graphique pour visualiser
```bash
rover -zipFile=plan.zip
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ RESSOURCES POUR CONTINUER À APPRENDRE                                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

[DOCS] DOCUMENTATION OFFICIELLE:
───────────────────────────

• Terraform Documentation: https://www.terraform.io/docs
• AWS Provider: https://registry.terraform.io/providers/hashicorp/aws
• Terraform Registry: https://registry.terraform.io
• Learn Terraform: https://learn.hashicorp.com/terraform

[GUIDE] LIVRES:
──────────

• "Terraform: Up & Running" par Yevgeniy Brikman
• "Terraform in Action" par Scott Winkler
• "Infrastructure as Code" par Kief Morris

[COURS] COURS EN LIGNE:
──────────────────

• HashiCorp Learn (gratuit)
• A Cloud Guru
• Linux Academy
• Udemy (plusieurs cours Terraform)

[TROPHEE] CERTIFICATIONS:
──────────────────

• HashiCorp Certified: Terraform Associate
  https://www.hashicorp.com/certification/terraform-associate

[UTILISATEURS] COMMUNAUTÉ:
──────────────

• HashiCorp Discuss: https://discuss.hashicorp.com
• Reddit: r/Terraform
• Stack Overflow: tag [terraform]
• Discord/Slack communities

[VIDEO_CAMERA] YOUTUBE:
───────────

• HashiCorp (chaîne officielle)
• TechWorld with Nana
• FreeCodeCamp

[LIEN] BLOGS ET ARTICLES:
─────────────────────

• HashiCorp Blog: https://www.hashicorp.com/blog
• AWS Blog (tag: Terraform)
• Medium (tag: Terraform)


╔═══════════════════════════════════════════════════════════════════════════╗
║ CONCLUSION                                                                ║
╚═══════════════════════════════════════════════════════════════════════════╝

Félicitations ! [BRAVO]

Vous avez maintenant une compréhension COMPLÈTE de Terraform, de ses concepts
fondamentaux aux architectures de production avancées.

POINTS CLÉS À RETENIR:
──────────────────────

1. **Infrastructure as Code**
   • L'infrastructure est du code
   • Versionné, testé, revu
   • Reproductible et documenté

2. **Le State est critique**
   • Toujours utiliser un backend distant
   • Activer versioning et chiffrement
   • Sauvegardes régulières

3. **Les Modules pour la réutilisabilité**
   • DRY (Don't Repeat Yourself)
   • Partager et collaborer
   • Utiliser le Registry

4. **Sécurité d'abord**
   • Jamais de secrets hardcodés
   • Principle of Least Privilege
   • Scanner régulièrement

5. **Tester, tester, tester**
   • Plan avant Apply
   • Environnements séparés
   • CI/CD automatisé

PROCHAINES ÉTAPES:
──────────────────

1. **Pratiquer** avec de vrais projets
2. **Contribuer** à des modules open source
3. **Passer la certification** HashiCorp
4. **Partager** vos connaissances

BON COURAGE DANS VOTRE AVENTURE TERRAFORM! [RAPIDE]

Si vous avez des questions, n'hésitez pas à:
• Consulter la documentation officielle
• Poser des questions sur les forums
• Rejoindre la communauté

L'Infrastructure as Code est l'avenir du DevOps, et vous êtes maintenant
équipé pour en faire partie!

═══════════════════════════════════════════════════════════════════════════════
                            FIN DU GUIDE TERRAFORM
                        Merci d'avoir lu jusqu'au bout!
═══════════════════════════════════════════════════════════════════════════════