# Fichier: python_cheats/cheatsheets/terraform_ultra_detaille.txt
# Terraform pour Développeurs Python - Guide ULTRA-DÉTAILLÉ
# Ce guide explique le POURQUOI, le COMMENT et le QUAND de CHAQUE concept
# Version: 2.0 - Mise à jour 2025


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

PARTIE 1: FONDAMENTAUX
├── 1.1  Qu'est-ce que Terraform?
├── 1.2  Infrastructure as Code (IaC)
├── 1.3  Architecture et Fonctionnement
├── 1.4  Installation Multi-Plateforme
└── 1.5  Premiers Pas - Hello World

PARTIE 2: CONCEPTS CORE
├── 2.1  Providers (AWS, GCP, Azure, etc.)
├── 2.2  Resources - Créer des Ressources
├── 2.3  Data Sources - Lire des Données
├── 2.4  Variables - Paramétrage
├── 2.5  Outputs - Exposer des Valeurs
├── 2.6  Locals - Variables Calculées
└── 2.7  Modules - Réutilisabilité

PARTIE 3: STATE MANAGEMENT
├── 3.1  Le State - Concept Fondamental
├── 3.2  Backends Locaux et Distants
├── 3.3  State Locking
├── 3.4  Workspaces
├── 3.5  State Import/Export
└── 3.6  Migrations de State

PARTIE 4: LANGAGE HCL AVANCÉ
├── 4.1  Syntaxe HCL Complète
├── 4.2  Expressions et Opérateurs
├── 4.3  Fonctions Built-in (50+ fonctions)
├── 4.4  Conditions et Ternaires
├── 4.5  Boucles (for, for_each, count)
├── 4.6  Dynamic Blocks
└── 4.7  Templates et Heredoc

PARTIE 5: META-ARGUMENTS
├── 5.1  depends_on - Dépendances
├── 5.2  count - Multiplication de Ressources
├── 5.3  for_each - Itération Avancée
├── 5.4  lifecycle - Cycle de Vie
├── 5.5  provider - Provider Spécifique
└── 5.6  provisioners - Configuration Post-Création

PARTIE 6: PROVIDERS EN PROFONDEUR
├── 6.1  Provider AWS - Complet
├── 6.2  Provider GCP
├── 6.3  Provider Azure
├── 6.4  Provider Kubernetes
├── 6.5  Provider Docker
├── 6.6  Créer son Propre Provider
└── 6.7  Multi-Provider Architectures

PARTIE 7: MODULES AVANCÉS
├── 7.1  Structure de Modules
├── 7.2  Input Variables
├── 7.3  Output Values
├── 7.4  Module Sources (local, git, registry)
├── 7.5  Versioning de Modules
├── 7.6  Module Composition
└── 7.7  Publier sur Terraform Registry

PARTIE 8: SÉCURITÉ
├── 8.1  Secrets Management
├── 8.2  Vault Integration
├── 8.3  IAM Best Practices
├── 8.4  Encryption (at rest & in transit)
├── 8.5  Security Scanning (checkov, tfsec)
├── 8.6  Compliance as Code
└── 8.7  Audit Logging

PARTIE 9: CI/CD
├── 9.1  GitHub Actions
├── 9.2  GitLab CI/CD
├── 9.3  Jenkins
├── 9.4  Azure DevOps
├── 9.5  Terraform Cloud
├── 9.6  Atlantis
└── 9.7  Policy as Code (Sentinel, OPA)

PARTIE 10: ARCHITECTURES PRODUCTION
├── 10.1 Application Web 3-Tier
├── 10.2 Microservices sur Kubernetes
├── 10.3 Data Pipeline (Big Data)
├── 10.4 Serverless Architecture
├── 10.5 Multi-Cloud Setup
├── 10.6 Disaster Recovery
└── 10.7 Blue-Green Deployment

PARTIE 11: DÉPLOIEMENTS PYTHON
├── 11.1 Django sur AWS (EC2, RDS, S3)
├── 11.2 Flask sur GCP (Cloud Run)
├── 11.3 FastAPI sur Lambda
├── 11.4 Celery Workers
├── 11.5 Jupyter Notebooks (SageMaker)
├── 11.6 ML Models Deployment
└── 11.7 Data Science Infrastructure

PARTIE 12: PERFORMANCE & OPTIMISATION
├── 12.1 Parallélisme
├── 12.2 Caching
├── 12.3 State Optimization
├── 12.4 Provider Upgrades
├── 12.5 Refactoring
└── 12.6 Cost Optimization

PARTIE 13: TESTING
├── 13.1 Unit Tests (Terratest)
├── 13.2 Integration Tests
├── 13.3 Policy Tests
├── 13.4 Smoke Tests
└── 13.5 Test Automation

PARTIE 14: TROUBLESHOOTING
├── 14.1 Erreurs Communes
├── 14.2 Debugging Techniques
├── 14.3 Logs Analysis
├── 14.4 State Recovery
└── 14.5 Provider Issues

PARTIE 15: OUTILS COMPLÉMENTAIRES
├── 15.1 terraform-docs
├── 15.2 tflint
├── 15.3 checkov
├── 15.4 infracost
├── 15.5 terragrunt
├── 15.6 rover
└── 15.7 terraform-compliance

ANNEXES
├── A. Commandes Complètes (tous les flags)
├── B. Fonctions HCL Référence
├── C. Providers Registry
├── D. Patterns & Anti-Patterns
├── E. Migration Guides
├── F. Glossaire
└── G. Ressources & Certification


═══════════════════════════════════════════════════════════════════════════════
[GUIDE] PARTIE 1: FONDAMENTAUX DE TERRAFORM
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ 1.1 QU'EST-CE QUE TERRAFORM? - INTRODUCTION COMPLÈTE                      ║
╚═══════════════════════════════════════════════════════════════════════════╝

DÉFINITION TECHNIQUE:
─────────────────────

Terraform est un outil open-source d'Infrastructure as Code (IaC) créé par 
HashiCorp en 2014. Il permet de définir, provisionner et gérer l'infrastructure 
cloud et on-premise à travers des fichiers de configuration déclaratifs.

ANALOGIE POUR COMPRENDRE:
─────────────────────────

┌────────────────────────────────────────────────────────────────────────┐
│ SANS TERRAFORM (Approche Manuelle):                                    │
│                                                                        │
│ Vous êtes architecte et voulez construire 100 maisons identiques:      │
│   • Vous devez aller sur chaque chantier                               │
│   • Expliquer aux ouvriers ce qu'il faut faire                         │
│   • Vérifier manuellement que tout est conforme                        │
│   • Répéter le processus 100 fois                                      │
│   • Risque d'erreurs et d'incohérences                                 │
│                                                                        │
│ AVEC TERRAFORM (Approche IaC):                                         │
│                                                                        │
│ Vous créez un plan détaillé (code) UNE SEULE FOIS:                     │
│   • Le plan décrit EXACTEMENT ce que vous voulez                       │
│   • Terraform lit le plan et coordonne la construction                 │
│   • Les 100 maisons sont IDENTIQUES                                    │
│   • Reproductible à l'infini                                           │
│   • Traçable et versionné (Git)                                        │
└────────────────────────────────────────────────────────────────────────┘


POURQUOI TERRAFORM? (Les 10 Raisons Majeures)
──────────────────────────────────────────────

1. REPRODUCTIBILITÉ PARFAITE
   ────────────────────────────
   
   PROBLÈME: En cliquant dans des consoles, impossible de recréer exactement
   la même infrastructure.
   
   SOLUTION TERRAFORM:
   ```hcl
   # Ce code crée TOUJOURS la même infrastructure
   resource "aws_instance" "web" {
     ami           = "ami-0c55b159cbfafe1f0"
     instance_type = "t2.micro"
     
     tags = {
       Name = "web-server-001"
     }
   }
   ```
   
   RÉSULTAT:
   • Même infrastructure en dev, staging, prod
   • Onboarding rapide: nouveau dev = terraform apply
   • Disaster recovery: terraform apply = tout reconstruit

2. VERSIONNAGE (GIT) DE L'INFRASTRUCTURE
   ──────────────────────────────────────
   
   AVANT: Documentation Word obsolète ou inexistante
   
   AVEC TERRAFORM + GIT:
   ```bash
   # Historique complet des changements
   git log --oneline
   
   # Qui a ajouté cette instance et pourquoi?
   git blame main.tf
   
   # Revenir à l'état de hier
   git checkout HEAD~1
   terraform apply
   ```
   
   AVANTAGES:
   • Chaque changement est documenté (commit message)
   • Revenir en arrière si problème
   • Voir l'évolution de l'infrastructure
   • Audit trail complet

3. COLLABORATION EN ÉQUIPE
   ────────────────────────
   
   WORKFLOW TERRAFORM + GIT:
   
   ```
   Alice:                          Bob:
   ┌──────────────┐               ┌──────────────┐
   │ git checkout │               │ git checkout │
   │  -b feature  │               │  -b bugfix   │
   └──────┬───────┘               └──────┬───────┘
          │                               │
          v                               v
   ┌──────────────┐               ┌──────────────┐
   │ Modifie code │               │ Modifie code │
   │ terraform fmt│               │ terraform fmt│
   │ terraform    │               │ terraform    │
   │   validate   │               │   validate   │
   └──────┬───────┘               └──────┬───────┘
          │                               │
          v                               v
   ┌──────────────┐               ┌──────────────┐
   │ Pull Request │               │ Pull Request │
   └──────┬───────┘               └──────┬───────┘
          │                               │
          └────────────┬──────────────────┘
                       v
              ┌─────────────────┐
              │  Code Review    │
              │  Approbation    │
              └────────┬────────┘
                       v
              ┌─────────────────┐
              │  Merge & Deploy │
              └─────────────────┘
   ```
   
   BÉNÉFICES:
   • Revue de code avant déploiement
   • Tests automatisés (CI)
   • Pas de conflits (merge resolution)
   • Traçabilité (qui a fait quoi)

4. DOCUMENTATION AUTO-GÉNÉRÉE
   ───────────────────────────
   
   Le code Terraform EST la documentation:
   
   ```hcl
   # Ce code documente l'infrastructure
   resource "aws_instance" "web" {
     ami           = "ami-0c55b159cbfafe1f0"  # Ubuntu 22.04
     instance_type = "t2.micro"                # 1 vCPU, 1GB RAM
     
     vpc_security_group_ids = [
       aws_security_group.web.id              # Autorise HTTP/HTTPS
     ]
     
     root_block_device {
       volume_size = 20                        # 20 GB SSD
       encrypted   = true                      # Chiffré au repos
     }
     
     tags = {
       Name        = "web-server-001"
       Environment = "production"
       Owner       = "team-backend"
     }
   }
   ```
   
   COMPARÉ À:
   • Documentation Word: Obsolète après 1 semaine
   • Wiki: Personne ne le met à jour
   • Screenshots: Dépassés immédiatement
   
   TERRAFORM:
   • Documentation toujours à jour
   • Impossible d'avoir code != réalité
   • Auto-générable (terraform-docs)

5. PRÉVENTION D'ERREURS (Drift Detection)
   ───────────────────────────────────────
   
   PROBLÈME: Quelqu'un modifie l'infrastructure manuellement
   
   DÉTECTION AUTOMATIQUE:
   ```bash
   # Terraform détecte les changements manuels
   terraform plan
   
   # Output:
   # Note: Objects have changed outside of Terraform
   #
   # aws_instance.web has changed:
   # ~ instance_type = "t2.micro" -> "t2.small"  (changed in console)
   #
   # Unless you have made equivalent changes to your configuration,
   # or ignored the relevant attributes using ignore_changes,
   # the following plan may include actions to undo or respond to these changes.
   ```
   
   RÉSULTAT:
   • Alertes sur changements non autorisés
   • Peut remettre en conformité automatiquement
   • Évite les "surprises" en production

6. MULTI-CLOUD & VENDOR NEUTRALITY
   ────────────────────────────────
   
   MÊME SYNTAXE pour tous les providers:
   
   ```hcl
   # AWS
   resource "aws_instance" "web" {
     ami           = "ami-123"
     instance_type = "t2.micro"
   }
   
   # GCP (même structure!)
   resource "google_compute_instance" "web" {
     machine_type = "f1-micro"
     boot_disk {
       initialize_params {
         image = "debian-cloud/debian-11"
       }
     }
   }
   
   # Azure (même structure!)
   resource "azurerm_linux_virtual_machine" "web" {
     size = "Standard_B1s"
     source_image_reference {
       publisher = "Canonical"
       offer     = "0001-com-ubuntu-server-jammy"
     }
   }
   ```
   
   AVANTAGES:
   • Pas de lock-in fournisseur
   • Même compétences pour tous les clouds
   • Migration facile entre providers
   • Multi-cloud hybride possible

7. DRY (Don't Repeat Yourself) - MODULES
   ──────────────────────────────────────
   
   SANS MODULES (duplication):
   ```hcl
   # Dev environment
   resource "aws_instance" "dev_web" { ... }
   resource "aws_db_instance" "dev_db" { ... }
   resource "aws_vpc" "dev_vpc" { ... }
   # ... 50 lignes ...
   
   # Staging environment (COPY-PASTE!)
   resource "aws_instance" "staging_web" { ... }
   resource "aws_db_instance" "staging_db" { ... }
   resource "aws_vpc" "staging_vpc" { ... }
   # ... 50 lignes DUPLIQUÉES ...
   
   # Prod environment (COPY-PASTE!)
   resource "aws_instance" "prod_web" { ... }
   resource "aws_db_instance" "prod_db" { ... }
   resource "aws_vpc" "prod_vpc" { ... }
   # ... 50 lignes DUPLIQUÉES ...
   ```
   
   AVEC MODULES (réutilisation):
   ```hcl
   module "dev" {
     source = "./modules/environment"
     env    = "dev"
     size   = "small"
   }
   
   module "staging" {
     source = "./modules/environment"
     env    = "staging"
     size   = "medium"
   }
   
   module "prod" {
     source = "./modules/environment"
     env    = "prod"
     size   = "large"
   }
   ```
   
   RÉSULTAT:
   • Code réduit de 70%
   • Bug fixé une fois = fixé partout
   • Maintenance simplifiée

8. PLANIFICATION AVANT EXÉCUTION
   ──────────────────────────────
   
   TERRAFORM PLAN = Simulation:
   
   ```bash
   terraform plan
   
   # Output: AVANT de faire quoi que ce soit
   #
   # Terraform will perform the following actions:
   #
   #   # aws_instance.web will be created
   #   + resource "aws_instance" "web" {
   #       + ami                    = "ami-123"
   #       + instance_type          = "t2.micro"
   #       + id                     = (known after apply)
   #       + public_ip              = (known after apply)
   #     }
   #
   #   # aws_security_group.old will be destroyed
   #   - resource "aws_security_group" "old" {
   #       - id   = "sg-12345678" -> null
   #       - name = "old-sg" -> null
   #     }
   #
   # Plan: 1 to add, 0 to change, 1 to destroy.
   #
   # Cost estimate: +$14.60/month
   ```
   
   COMPARAISON:
   • Console AWS: Cliquer = changement immédiat (danger!)
   • Terraform: Voir AVANT de faire (sécurité!)
   
   AVANTAGES:
   • Validation par l'équipe
   • Estimation des coûts
   • Détection d'erreurs avant impact
   • Annulation possible avant apply

9. GESTION DES DÉPENDANCES AUTOMATIQUE
   ────────────────────────────────────
   
   TERRAFORM COMPREND L'ORDRE:
   
   ```hcl
   # 1. Créer le VPC en premier
   resource "aws_vpc" "main" {
     cidr_block = "10.0.0.0/16"
   }
   
   # 2. Créer le subnet (dépend du VPC)
   resource "aws_subnet" "public" {
     vpc_id     = aws_vpc.main.id  # <- Dépendance détectée!
     cidr_block = "10.0.1.0/24"
   }
   
   # 3. Créer l'instance (dépend du subnet)
   resource "aws_instance" "web" {
     subnet_id = aws_subnet.public.id  # <- Dépendance détectée!
     ami       = "ami-123"
   }
   ```
   
   TERRAFORM FAIT AUTOMATIQUEMENT:
   ```
   Step 1: Create aws_vpc.main
   Step 2: Create aws_subnet.public (wait for VPC)
   Step 3: Create aws_instance.web (wait for subnet)
   ```
   
   RÉSULTAT:
   • Pas besoin de gérer l'ordre manuellement
   • Parallélisation automatique quand possible
   • Rollback intelligent en cas d'erreur

10. ÉCOSYSTÈME RICHE (3000+ Providers)
    ────────────────────────────────────
    
    TOUT est gérable avec Terraform:
    
    ```
    CLOUDS:
    • AWS (1000+ ressources)
    • Google Cloud
    • Azure
    • DigitalOcean
    • OVH
    • Alibaba Cloud
    
    ORCHESTRATION:
    • Kubernetes
    • Docker
    • Nomad
    • ECS/EKS
    
    MONITORING:
    • Datadog
    • New Relic
    • Grafana
    • PagerDuty
    
    DNS:
    • Cloudflare
    • Route53
    • DNSimple
    
    CI/CD:
    • GitHub
    • GitLab
    • CircleCI
    • Jenkins
    
    DATABASES:
    • MongoDB Atlas
    • PostgreSQL
    • MySQL
    
    SaaS:
    • Stripe
    • Twilio
    • SendGrid
    • Auth0
    
    Et 3000+ autres!
    ```
    
    EXEMPLE MULTI-PROVIDER:
    ```hcl
    # Tout dans le même projet Terraform
    
    # Infrastructure AWS
    resource "aws_instance" "web" { ... }
    
    # DNS Cloudflare
    resource "cloudflare_record" "www" { ... }
    
    # Monitoring Datadog
    resource "datadog_monitor" "web" { ... }
    
    # Notifier PagerDuty
    resource "pagerduty_service" "web" { ... }
    
    # Code GitHub
    resource "github_repository" "app" { ... }
    ```


QUAND UTILISER TERRAFORM?
──────────────────────────

[OK] UTILISEZ TERRAFORM SI:

1. Infrastructure cloud (AWS, GCP, Azure)
2. Besoin de reproduire des environnements
3. Travail en équipe
4. Infrastructure critique (prod)
5. Compliance/audit requis
6. Multi-cloud ou hybrid cloud
7. Infrastructure évolutive (scale)
8. Besoin de CI/CD pour infra

[X] N'UTILISEZ PAS TERRAFORM SI:

1. Infrastructure très simple (1-2 ressources)
2. Changements ultra-fréquents (plusieurs fois/heure)
3. Prototypage rapide ponctuel
4. Pas de compétences techniques dans l'équipe
5. Infrastructure legacy impossible à coder

ALTERNATIVES À TERRAFORM:
──────────────────────────

┌────────────────────────────────────────────────────────────────────┐
│ Outil              │ Langage    │ Cas d'usage                      │
├────────────────────┼────────────┼──────────────────────────────────┤
│ Terraform          │ HCL        │ Multi-cloud, déclaratif          │
│ CloudFormation     │ JSON/YAML  │ AWS uniquement                   │
│ Pulumi             │ Python/JS  │ Programmation, multi-cloud       │
│ Ansible            │ YAML       │ Configuration, provisioning      │
│ AWS CDK            │ Python/TS  │ AWS, programmation               │
│ Bicep              │ Bicep      │ Azure uniquement                 │
└────────────────────────────────────────────────────────────────────┘


╔═══════════════════════════════════════════════════════════════════════════╗
║ 1.2 INFRASTRUCTURE AS CODE (IaC) - PHILOSOPHIE                            ║
╚═══════════════════════════════════════════════════════════════════════════╝

DÉFINITION FORMELLE:
────────────────────

Infrastructure as Code (IaC) est une approche de gestion et de provisionnement
de l'infrastructure informatique à travers des fichiers de définition lisibles
par machine plutôt que par configuration manuelle ou outils interactifs.

LES 4 PRINCIPES FONDAMENTAUX DE L'IaC:
───────────────────────────────────────

1. DÉCLARATIF vs IMPÉRATIF
   ────────────────────────

┌────────────────────────────────────────────────────────────────────────┐
│ IMPÉRATIF (Scripting traditionnel):                                    │
│ "Comment faire étape par étape"                                        │
│                                                                        │
│ Exemple script bash:                                                   │
│   #!/bin/bash                                                          │
│   # Étape 1: Créer un VPC                                              │
│   VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 \               │
│             --query 'Vpc.VpcId' --output text)                         │
│                                                                        │
│   # Étape 2: Créer un subnet                                           │
│   SUBNET_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID \                 │
│                --cidr-block 10.0.1.0/24 \                              │
│                --query 'Subnet.SubnetId' --output text)                │
│                                                                        │
│   # Étape 3: Créer une instance                                        │
│   INSTANCE_ID=$(aws ec2 run-instances --image-id ami-123 \             │
│                  --subnet-id $SUBNET_ID \                              │
│                  --query 'Instances[0].InstanceId' --output text)      │
│                                                                        │
│ PROBLÈMES:                                                             │
│   [X] Si relancé, crée des doublons                                     │
│   [X] Difficile de gérer l'état actuel                                  │
│   [X] Rollback complexe                                                 │
│   [X] Gestion des erreurs manuelles                                     │
└────────────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────────────┐
│ DÉCLARATIF (Terraform):                                                │
│ "Ce que je veux avoir à la fin"                                        │
│                                                                        │
│ Terraform code:                                                        │
│   resource "aws_vpc" "main" {                                          │
│     cidr_block = "10.0.0.0/16"                                         │
│   }                                                                    │
│                                                                        │
│   resource "aws_subnet" "public" {                                     │
│     vpc_id     = aws_vpc.main.id                                       │
│     cidr_block = "10.0.1.0/24"                                         │
│   }                                                                    │
│                                                                        │
│   resource "aws_instance" "web" {                                      │
│     ami       = "ami-123"                                              │
│     subnet_id = aws_subnet.public.id                                   │
│   }                                                                    │
│                                                                        │
│ AVANTAGES:                                                             │
│   [OK] Idempotent (relancer = même résultat)                             │
│   [OK] Terraform gère l'état                                             │
│   [OK] Rollback automatique                                              │
│   [OK] Détection de drift                                                │
│   [OK] Lisible et maintenable                                            │
└────────────────────────────────────────────────────────────────────────┘

2. IDEMPOTENCE
   ───────────

DÉFINITION: Une opération est idempotente si l'exécuter plusieurs fois
produit le même résultat qu'une seule exécution.

EXEMPLE:

```hcl
# Ce code est idempotent
resource "aws_instance" "web" {
  ami           = "ami-123"
  instance_type = "t2.micro"
  
  tags = {
    Name = "web-server"
  }
}
```

COMPORTEMENT:

```bash
# Première exécution
terraform apply
# -> Crée l'instance

# Deuxième exécution
terraform apply
# -> Ne fait RIEN (instance déjà existe)

# Troisième exécution
terraform apply
# -> Ne fait RIEN (instance déjà existe)
```

AVANTAGES:
• Sûr de relancer
• Pas de doublons
• Convergence vers l'état désiré

3. IMMUTABILITÉ
   ────────────

PRINCIPE: Au lieu de modifier une ressource existante, on la détruit
et on en crée une nouvelle.

COMPARAISON:

```
APPROCHE MUTABLE (Ansible, scripts):
┌─────────────┐
│  Server v1  │
└─────────────┘
      v update packages
┌─────────────┐
│  Server v1  │ (modifié en place)
└─────────────┘
      v install nginx
┌─────────────┐
│  Server v1  │ (re-modifié)
└─────────────┘

PROBLÈMES:
• État inconnu (quelles modifs?)
• Difficult à reproduire
• Drift possible

APPROCHE IMMUTABLE (Terraform):
┌─────────────┐
│  Server v1  │
└─────────────┘
      v change AMI
┌─────────────┐
│  Server v2  │ <- Nouvelle instance
└─────────────┘
      v destroy old
┌─────────────┐
│  Server v2  │ <- Seule survivante
└─────────────┘

AVANTAGES:
• État connu et prévisible
• Reproductible
• Pas de drift
```

TERRAFORM IMPLÉMENTE L'IMMUTABILITÉ:

```hcl
resource "aws_instance" "web" {
  ami           = "ami-OLD"  # -> Change to ami-NEW
  instance_type = "t2.micro"
}

# terraform apply:
# 1. Crée nouvelle instance avec ami-NEW
# 2. Détruit ancienne instance avec ami-OLD
```

4. VERSIONING
   ──────────

TOUT le code infrastructure est dans Git:

```bash
# Structure type
infrastructure/
├── .git/                    # Historique complet
├── main.tf                  # Config principale
├── variables.tf             # Variables
├── outputs.tf              # Outputs
└── modules/                # Modules réutilisables
    ├── vpc/
    ├── ec2/
    └── rds/
```

WORKFLOW GIT STANDARD:

```bash
# Créer une feature branch
git checkout -b feature/add-monitoring

# Modifier l'infrastructure
vim main.tf

# Commit
git add .
git commit -m "Add CloudWatch monitoring for web servers"

# Push et create Pull Request
git push origin feature/add-monitoring

# Code review -> Merge -> Deploy
```

AVANTAGES:
• Historique complet des changements
• Revue de code obligatoire
• Rollback facile: git revert
• Blame: qui a fait quoi quand
• Branches: tester sans risque


LES 3 CATÉGORIES D'IaC:
───────────────────────

1. AD-HOC SCRIPTS
   ──────────────
   
   Bash, Python, PowerShell scripts
   
   AVANTAGES:
   • Rapide pour prototyper
   • Pas de courbe d'apprentissage
   
   INCONVÉNIENTS:
   • Pas idempotent
   • Difficult à maintenir
   • Pas de gestion d'état

2. CONFIGURATION MANAGEMENT
   ────────────────────────
   
   Ansible, Chef, Puppet, SaltStack
   
   FOCUS: Configuration de serveurs existants
   
   EXEMPLE ANSIBLE:
   ```yaml
   - name: Install nginx
     apt:
       name: nginx
       state: present
   ```
   
   AVANTAGES:
   • Bon pour configuration applicative
   • Inventory management
   
   INCONVÉNIENTS:
   • Moins bon pour provisioning infra
   • Peut être mutable

3. INFRASTRUCTURE PROVISIONING
   ───────────────────────────
   
   Terraform, CloudFormation, Pulumi
   
   FOCUS: Créer/détruire infrastructure
   
   EXEMPLE TERRAFORM:
   ```hcl
   resource "aws_instance" "web" {
     ami           = "ami-123"
     instance_type = "t2.micro"
   }
   ```
   
   AVANTAGES:
   • Déclaratif et immutable
   • Gestion d'état
   • Multi-provider
   
   QUAND L'UTILISER:
   • Créer VPCs, subnets, instances
   • Gérer infrastructure cloud
   • Orchestration complexe


╔═══════════════════════════════════════════════════════════════════════════╗
║ 1.3 ARCHITECTURE ET FONCTIONNEMENT DE TERRAFORM                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

COMPOSANTS PRINCIPAUX:
──────────────────────

```
┌───────────────────────────────────────────────────────────────────┐
│                     TERRAFORM ARCHITECTURE                        │
└───────────────────────────────────────────────────────────────────┘

┌──────────────────┐
│  Configuration   │  <- .tf files (HCL)
│   Files (.tf)    │     • main.tf
└────────┬─────────┘     • variables.tf
         │               • outputs.tf
         v
┌──────────────────┐
│  Terraform CLI   │  <- terraform apply, plan, etc.
└────────┬─────────┘
         │
         v
┌──────────────────┐
│  Terraform Core  │  <- Graph engine, State management
└────────┬─────────┘
         │
         ├─────────────────────┬─────────────────────┐
         v                     v                     v
┌──────────────┐      ┌──────────────┐     ┌──────────────┐
│  Provider    │      │  Provider    │     │  Provider    │
│    AWS       │      │    GCP       │     │   Azure      │
└──────┬───────┘      └──────┬───────┘     └──────┬───────┘
       │                     │                     │
       v                     v                     v
┌──────────────┐      ┌──────────────┐     ┌──────────────┐
│   AWS API    │      │   GCP API    │     │  Azure API   │
└──────────────┘      └──────────────┘     └──────────────┘
       │                     │                     │
       v                     v                     v
┌──────────────┐      ┌──────────────┐     ┌──────────────┐
│     AWS      │      │     GCP      │     │    Azure     │
│    Cloud     │      │    Cloud     │     │    Cloud     │
└──────────────┘      └──────────────┘     └──────────────┘
```

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

1. TERRAFORM CLI
   ─────────────
   
   Interface en ligne de commande.
   
   COMMANDES PRINCIPALES:
   ```bash
   terraform init      # Initialiser
   terraform plan      # Prévisualiser
   terraform apply     # Appliquer
   terraform destroy   # Détruire
   terraform fmt       # Formater
   terraform validate  # Valider
   ```

2. TERRAFORM CORE
   ──────────────
   
   Le cerveau de Terraform.
   
   RESPONSABILITÉS:
   • Parser les fichiers .tf (HCL)
   • Construire le graphe de dépendances
   • Gérer le state
   • Calculer les différences (plan)
   • Orchestrer les providers
   
   ALGORITHME SIMPLIFIÉ:
   ```
   1. Lire config (.tf files)
   2. Lire state (terraform.tfstate)
   3. Interroger providers (état réel dans cloud)
   4. Calculer diff: desired - current = plan
   5. Exécuter le plan via providers
   6. Mettre à jour le state
   ```

3. PROVIDERS
   ─────────
   
   Plugins qui implémentent l'API d'un service.
   
   RÔLE:
   • Traduire HCL -> Appels API
   • Gérer l'authentification
   • CRUD des ressources
   
   EXEMPLE PROVIDER AWS:
   ```
   Terraform dit: "Crée une instance EC2"
   Provider AWS:
   1. Authentification (credentials)
   2. Appel AWS API: ec2.RunInstances()
   3. Retour à Terraform: instance_id, public_ip, etc.
   ```

4. STATE
   ─────
   
   Fichier terraform.tfstate (JSON)
   
   CONTENU:
   ```json
   {
     "version": 4,
     "resources": [
       {
         "type": "aws_instance",
         "name": "web",
         "instances": [{
           "attributes": {
             "id": "i-1234567890abcdef0",
             "ami": "ami-123",
             "public_ip": "54.123.45.67",
             ...
           }
         }]
       }
     ]
   }
   ```
   
   POURQUOI LE STATE EST CRITIQUE:
   • Mapping: Code <-> Ressources réelles
   • Performance: Évite d'interroger l'API chaque fois
   • Dépendances: Connaît l'ordre de création
   • Collaboration: Partage de l'état en équipe


LE WORKFLOW COMPLET (Étape par Étape):
───────────────────────────────────────

```
┌─────────────────────────────────────────────────────────────────────┐
│ 1. ÉCRITURE DU CODE                                                 │
└─────────────────────────────────────────────────────────────────────┘

Développeur écrit:
┌──────────────────────────────────────┐
│ # main.tf                            │
│ resource "aws_instance" "web" {      │
│   ami           = "ami-123"          │
│   instance_type = "t2.micro"         │
│ }                                    │
└──────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│ 2. TERRAFORM INIT                                                   │
└─────────────────────────────────────────────────────────────────────┘

$ terraform init

Terraform:
1. Parse main.tf
2. Identifie providers nécessaires (aws)
3. Télécharge provider depuis registry.terraform.io
4. Crée .terraform/ directory
5. Crée .terraform.lock.hcl (version lock)


┌─────────────────────────────────────────────────────────────────────┐
│ 3. TERRAFORM PLAN                                                   │
└─────────────────────────────────────────────────────────────────────┘

$ terraform plan

Terraform:
1. Parse tous les .tf files
2. Lit terraform.tfstate (si existe)
3. Pour chaque ressource:
   a. Appelle provider pour obtenir état actuel
   b. Compare desired (code) vs current (API)
   c. Calcule les actions nécessaires
4. Construit le graphe de dépendances
5. Affiche le plan d'exécution

Output:
┌───────────────────────────────────────────┐
│ Plan: 1 to add, 0 to change, 0 to destroy │
│                                           │
│ + aws_instance.web                        │
│     ami:           "ami-123"              │
│     instance_type: "t2.micro"             │
│     id:            (known after apply)    │
│     public_ip:     (known after apply)    │
└───────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│ 4. TERRAFORM APPLY                                                  │
└─────────────────────────────────────────────────────────────────────┘

$ terraform apply

Terraform:
1. Refait le plan (sécurité)
2. Demande confirmation: "yes"
3. Pour chaque action dans le plan:
   a. Appelle provider.Create/Update/Delete()
   b. Provider fait l'appel API correspondant
   c. Provider retourne les attributs de la ressource
   d. Terraform met à jour le state
4. Affiche le résumé

Output:
┌───────────────────────────────────────────┐
│ aws_instance.web: Creating...             │
│ aws_instance.web: Still creating... [10s] │
│ aws_instance.web: Creation complete [34s] │
│                                           │
│ Apply complete! Resources: 1 added        │
│                                           │
│ Outputs:                                  │
│ instance_id = "i-1234567890abcdef0"       │
│ public_ip   = "54.123.45.67"              │
└───────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│ 5. STATE UPDATE                                                     │
└─────────────────────────────────────────────────────────────────────┘

terraform.tfstate mis à jour:
┌─────────────────────────────────────┐
│ {                                   │
│   "resources": [{                   │
│     "type": "aws_instance",         │
│     "name": "web",                  │
│     "instances": [{                 │
│       "attributes": {               │
│         "id": "i-123...",           │
│         "ami": "ami-123",           │
│         "public_ip": "54.123.45.67",│
│         ...                         │
│       }                             │
│     }]                              │
│   }]                                │
│ }                                   │
└─────────────────────────────────────┘
```


LE GRAPHE DE DÉPENDANCES:
──────────────────────────

Terraform construit un DAG (Directed Acyclic Graph).

EXEMPLE:

```hcl
# Code
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "public" {
  vpc_id     = aws_vpc.main.id  # <- Dépend de VPC
  cidr_block = "10.0.1.0/24"
}

resource "aws_instance" "web" {
  subnet_id = aws_subnet.public.id  # <- Dépend de subnet
  ami       = "ami-123"
}

resource "aws_instance" "api" {
  subnet_id = aws_subnet.public.id  # <- Dépend de subnet
  ami       = "ami-456"
}
```

GRAPHE GÉNÉRÉ:

```
┌──────────────┐
│   aws_vpc    │
│     main     │
└──────┬───────┘
       │
       v
┌──────────────┐
│  aws_subnet  │
│    public    │
└──────┬───────┘
       │
       ├─────────────────┐
       v                 v
┌──────────────┐  ┌──────────────┐
│aws_instance  │  │aws_instance  │
│     web      │  │     api      │
└──────────────┘  └──────────────┘
```

ORDRE D'EXÉCUTION:

```
Phase 1: Create aws_vpc.main
Phase 2: Create aws_subnet.public (wait for VPC)
Phase 3: Create aws_instance.web AND aws_instance.api (parallel!)
```

AVANTAGES:
• Ordre optimal automatique
• Parallélisation quand possible
• Détection de cycles (erreur si cycle)


(Continué dans le prochain fichier...)

═══════════════════════════════════════════════════════════════════════════════
[NOTE] NOTES:
═══════════════════════════════════════════════════════════════════════════════

Ce fichier contient les 3 premières sections de la PARTIE 1.
Le guide complet sera divisé en plusieurs fichiers pour maintenir
la lisibilité et la performance.

PROCHAINS FICHIERS:
• terraform_part1_installation.txt (1.4 Installation)
• terraform_part1_premiers_pas.txt (1.5 Hello World)
• terraform_part2_concepts.txt (Partie 2 complète)
• terraform_part3_state.txt (Partie 3 complète)
• ... etc

Chaque fichier est autonome mais référence les autres pour la continuité.

═══════════════════════════════════════════════════════════════════════════════

# Fichier: python_cheats/cheatsheets/terraform_part1_installation.txt
# Terraform - PARTIE 1.4 & 1.5: INSTALLATION ET PREMIERS PAS
# Guide Ultra-Détaillé pour Grands Débutants


═══════════════════════════════════════════════════════════════════════════════
[OUTIL] PARTIE 1.4: INSTALLATION MULTI-PLATEFORME
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ PRÉREQUIS SYSTÈME                                                         ║
╚═══════════════════════════════════════════════════════════════════════════╝

REQUIREMENTS MINIMAUX:
──────────────────────

• CPU: 1 core (2+ recommandé)
• RAM: 512 MB (2GB+ recommandé)
• Disque: 100 MB pour Terraform + espace pour providers
• OS: Windows 10+, macOS 10.13+, Linux (toute distro récente)
• Connexion Internet (pour télécharger providers)

CONNAISSANCES REQUISES:
───────────────────────

[OK] Ligne de commande de base (cd, ls, mkdir)
[OK] Éditeur de texte (VS Code, Vim, Nano, etc.)
[OK] Git (optionnel mais recommandé)
[OK] Compte cloud (AWS/GCP/Azure) pour pratiquer

PAS DE CONNAISSANCES REQUISES EN:
──────────────────────────────────

[X] Programmation avancée
[X] Réseau complexe
[X] Sysadmin expert
[X] DevOps expérience


╔═══════════════════════════════════════════════════════════════════════════╗
║ INSTALLATION SUR macOS                                                    ║
╚═══════════════════════════════════════════════════════════════════════════╝

MÉTHODE 1: HOMEBREW (Recommandée)
──────────────────────────────────

┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 1: Installer Homebrew (si pas déjà installé)                       │
└───────────────────────────────────────────────────────────────────────────┘

# Vérifier si Homebrew est installé
brew --version

# Si erreur "command not found", installer Homebrew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Suivre les instructions à l'écran
# Ajouter Homebrew au PATH si demandé:
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

POURQUOI HOMEBREW?
• Package manager officiel macOS
• Gère les mises à jour automatiquement
• Installation en une commande
• Désinstallation propre


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 2: Installer Terraform via Homebrew                                │
└───────────────────────────────────────────────────────────────────────────┘

# Installer Terraform
brew tap hashicorp/tap
brew install hashicorp/tap/terraform

# Vérifier l'installation
terraform --version

# Output attendu:
# Terraform v1.7.0
# on darwin_arm64 (ou darwin_amd64 selon votre Mac)

EXPLICATIONS:
• brew tap: Ajoute le repository officiel HashiCorp
• brew install: Télécharge et installe Terraform
• terraform --version: Vérifie que l'installation a réussi


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 3: Configuration du Shell (Autocomplétion)                          │
└───────────────────────────────────────────────────────────────────────────┘

# Pour Zsh (shell par défaut sur macOS récents)
terraform -install-autocomplete

# Redémarrer le shell
source ~/.zshrc

# Tester l'autocomplétion
terraform [TAB][TAB]
# Devrait afficher: apply, destroy, init, plan, etc.

POURQUOI L'AUTOCOMPLÉTION?
• Gain de temps énorme
• Découverte des commandes
• Moins d'erreurs de frappe


MÉTHODE 2: TÉLÉCHARGEMENT MANUEL
─────────────────────────────────

SI vous ne voulez pas utiliser Homebrew:

┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 1: Télécharger le binaire                                           │
└───────────────────────────────────────────────────────────────────────────┘

# Aller sur: https://www.terraform.io/downloads
# Télécharger terraform_VERSION_darwin_arm64.zip (M1/M2/M3)
# ou terraform_VERSION_darwin_amd64.zip (Intel)

# Ou via ligne de commande:
TERRAFORM_VERSION="1.7.0"
ARCH="arm64"  # ou "amd64" pour Intel

curl -O "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_darwin_${ARCH}.zip"


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 2: Extraire et installer                                            │
└───────────────────────────────────────────────────────────────────────────┘

# Extraire
unzip terraform_${TERRAFORM_VERSION}_darwin_${ARCH}.zip

# Déplacer vers /usr/local/bin (dans le PATH)
sudo mv terraform /usr/local/bin/

# Donner les permissions d'exécution
sudo chmod +x /usr/local/bin/terraform

# Vérifier
terraform --version


┌───────────────────────────────────────────────────────────────────────────┐
│ TROUBLESHOOTING macOS                                                     │
└───────────────────────────────────────────────────────────────────────────┘

ERREUR: "terraform cannot be opened because the developer cannot be verified"

SOLUTION:
# Autoriser l'exécution
sudo xattr -d com.apple.quarantine /usr/local/bin/terraform

# Ou via Préférences Système:
# Sécurité et confidentialité -> Général -> Autoriser


ERREUR: "command not found: terraform"

SOLUTION:
# Vérifier que /usr/local/bin est dans le PATH
echo $PATH | grep "/usr/local/bin"

# Si pas présent, ajouter au PATH
echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc


╔═══════════════════════════════════════════════════════════════════════════╗
║ INSTALLATION SUR LINUX                                                    ║
╚═══════════════════════════════════════════════════════════════════════════╝

MÉTHODE 1: PACKAGE MANAGER (Recommandée)
─────────────────────────────────────────

┌───────────────────────────────────────────────────────────────────────────┐
│ 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 officiel 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. Mettre à jour et installer
sudo apt update
sudo apt install terraform

# 4. Vérifier
terraform --version

EXPLICATIONS LIGNE PAR LIGNE:
──────────────────────────────

Ligne 1-2: wget télécharge la clé GPG
• GPG = signature cryptographique
• Vérifie que les packages viennent bien de HashiCorp
• Sécurité: évite les packages malveillants

Ligne 3-5: echo ajoute le repository
• $(lsb_release -cs) = détecte automatiquement votre version Ubuntu
• Exemples: jammy (22.04), focal (20.04), noble (24.04)

Ligne 6-7: Installation classique
• apt update = rafraîchit la liste des packages
• apt install = installe Terraform


┌───────────────────────────────────────────────────────────────────────────┐
│ FEDORA / RHEL / CENTOS                                                    │
└───────────────────────────────────────────────────────────────────────────┘

# 1. Ajouter le repository HashiCorp
sudo dnf config-manager --add-repo \
    https://rpm.releases.hashicorp.com/fedora/hashicorp.repo

# 2. Installer Terraform
sudo dnf install terraform

# 3. Vérifier
terraform --version


┌───────────────────────────────────────────────────────────────────────────┐
│ ARCH LINUX                                                                │
└───────────────────────────────────────────────────────────────────────────┘

# Terraform est dans les repositories officiels
sudo pacman -S terraform

# Vérifier
terraform --version


MÉTHODE 2: TÉLÉCHARGEMENT MANUEL (Toutes distros)
──────────────────────────────────────────────────

┌───────────────────────────────────────────────────────────────────────────┐
│ INSTALLATION MANUELLE UNIVERSELLE                                         │
└───────────────────────────────────────────────────────────────────────────┘

# 1. Télécharger
TERRAFORM_VERSION="1.7.0"
wget "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip"

# 2. Extraire
unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip

# 3. Déplacer vers /usr/local/bin
sudo mv terraform /usr/local/bin/

# 4. Donner les permissions
sudo chmod +x /usr/local/bin/terraform

# 5. Vérifier
terraform --version

# 6. Nettoyer
rm terraform_${TERRAFORM_VERSION}_linux_amd64.zip


┌───────────────────────────────────────────────────────────────────────────┐
│ AUTOCOMPLÉTION LINUX                                                      │
└───────────────────────────────────────────────────────────────────────────┘

# Pour Bash
terraform -install-autocomplete
source ~/.bashrc

# Pour Zsh
terraform -install-autocomplete
source ~/.zshrc

# Pour Fish
echo 'complete -c terraform -f -a "(terraform -help | string match -r -- \'^  \' | string trim)"' > ~/.config/fish/completions/terraform.fish


╔═══════════════════════════════════════════════════════════════════════════╗
║ INSTALLATION SUR WINDOWS                                                  ║
╚═══════════════════════════════════════════════════════════════════════════╝

MÉTHODE 1: CHOCOLATEY (Recommandée)
────────────────────────────────────

Chocolatey = Package manager pour Windows (comme Homebrew sur Mac)

┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 1: Installer Chocolatey                                            │
└───────────────────────────────────────────────────────────────────────────┘

# 1. Ouvrir PowerShell en tant qu'administrateur
# Clic droit sur l'icône PowerShell -> "Exécuter en tant qu'administrateur"

# 2. Copier-coller cette commande:
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

# 3. Attendre la fin de l'installation

# 4. Vérifier
choco --version


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 2: Installer Terraform via Chocolatey                               │
└───────────────────────────────────────────────────────────────────────────┘

# Dans PowerShell (toujours en admin)
choco install terraform

# Confirmer avec 'y' quand demandé

# Vérifier
terraform --version

# Output attendu:
# Terraform v1.7.0
# on windows_amd64


MÉTHODE 2: INSTALLATION MANUELLE
─────────────────────────────────

┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 1: Télécharger                                                     │
└───────────────────────────────────────────────────────────────────────────┘

# Aller sur: https://www.terraform.io/downloads
# Télécharger: terraform_VERSION_windows_amd64.zip

# Ou via PowerShell:
$TerraformVersion = "1.7.0"
Invoke-WebRequest -Uri "https://releases.hashicorp.com/terraform/$TerraformVersion/terraform_${TerraformVersion}_windows_amd64.zip" -OutFile "terraform.zip"


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 2: Extraire et configurer le PATH                                  │
└───────────────────────────────────────────────────────────────────────────┘

# 1. Créer un dossier pour Terraform
New-Item -ItemType Directory -Path "C:\terraform" -Force

# 2. Extraire le ZIP
Expand-Archive -Path "terraform.zip" -DestinationPath "C:\terraform" -Force

# 3. Ajouter au PATH (persistant)
# Via PowerShell (en admin):
[Environment]::SetEnvironmentVariable(
    "Path",
    [Environment]::GetEnvironmentVariable("Path", "Machine") + ";C:\terraform",
    "Machine"
)

# 4. Redémarrer PowerShell (fermer et réouvrir)

# 5. Vérifier
terraform --version


MÉTHODE 3: WINDOWS SUBSYSTEM FOR LINUX (WSL)
─────────────────────────────────────────────

SI vous utilisez WSL:

# 1. Ouvrir WSL (Ubuntu)
wsl

# 2. Suivre les instructions Linux Ubuntu ci-dessus
# (Identique à une vraie installation Ubuntu)


┌───────────────────────────────────────────────────────────────────────────┐
│ TROUBLESHOOTING WINDOWS                                                   │
└───────────────────────────────────────────────────────────────────────────┘

ERREUR: "terraform is not recognized as an internal or external command"

SOLUTION 1: Vérifier le PATH
# PowerShell
$env:Path -split ';' | Select-String terraform

SOLUTION 2: Ajouter manuellement au PATH
# Interface graphique:
# 1. Rechercher "Variables d'environnement" dans Windows
# 2. Variables système -> Path -> Modifier
# 3. Nouveau -> C:\terraform
# 4. OK -> OK
# 5. Redémarrer PowerShell


ERREUR: "Execution policy restricts..."

SOLUTION:
# PowerShell (admin)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser


╔═══════════════════════════════════════════════════════════════════════════╗
║ VÉRIFICATION DE L'INSTALLATION                                            ║
╚═══════════════════════════════════════════════════════════════════════════╝

COMMANDES DE VÉRIFICATION:
──────────────────────────

# 1. Version de Terraform
terraform --version
terraform version  # Alternative

# Output attendu:
# Terraform v1.7.0
# on darwin_arm64 (ou votre plateforme)

# 2. Aide générale
terraform --help
terraform -h  # Forme courte

# 3. Aide sur une commande spécifique
terraform init --help
terraform plan --help

# 4. Autocomplétion (si installée)
terraform [TAB][TAB]

# Devrait afficher toutes les commandes disponibles


STRUCTURE DES FICHIERS TERRAFORM:
──────────────────────────────────

Après installation, Terraform est juste UN binaire:

```
/usr/local/bin/terraform (macOS/Linux)
C:\terraform\terraform.exe (Windows)
```

PAS de fichiers de configuration globaux par défaut.
Tout se passe dans vos projets (dossiers avec .tf files).


╔═══════════════════════════════════════════════════════════════════════════╗
║ CONFIGURATION DE L'ÉDITEUR DE CODE                                        ║
╚═══════════════════════════════════════════════════════════════════════════╝

VISUAL STUDIO CODE (Recommandé)
────────────────────────────────

┌───────────────────────────────────────────────────────────────────────────┐
│ EXTENSIONS ESSENTIELLES                                                   │
└───────────────────────────────────────────────────────────────────────────┘

1. HASHICORP TERRAFORM
   ───────────────────
   
   Installer depuis VS Code Marketplace:
   • Nom: HashiCorp Terraform
   • ID: hashicorp.terraform
   
   FONCTIONNALITÉS:
   [OK] Coloration syntaxique
   [OK] Autocomplétion intelligente
   [OK] Validation en temps réel
   [OK] Snippets de code
   [OK] Formatage automatique
   [OK] Intégration terraform fmt
   [OK] Documentation au survol
   
   CONFIGURATION:
   ```json
   // settings.json
   {
     "terraform.languageServer.enable": true,
     "terraform.experimentalFeatures.validateOnSave": true,
     "editor.formatOnSave": true,
     "[terraform]": {
       "editor.defaultFormatter": "hashicorp.terraform",
       "editor.formatOnSave": true
     }
   }
   ```

2. TERRAFORM AUTOCOMPLETE (Optionnel)
   ───────────────────────────────────
   
   Complétion avancée des ressources AWS/GCP/Azure


┌───────────────────────────────────────────────────────────────────────────┐
│ SNIPPETS UTILES                                                           │
└───────────────────────────────────────────────────────────────────────────┘

Créer vos snippets personnalisés:
Fichier -> Préférences -> Extraits utilisateur -> terraform.json

```json
{
  "Terraform Resource": {
    "prefix": "tfres",
    "body": [
      "resource \"${1:type}\" \"${2:name}\" {",
      "  ${3:# configuration}",
      "}"
    ],
    "description": "Create a Terraform resource"
  },
  
  "Terraform Variable": {
    "prefix": "tfvar",
    "body": [
      "variable \"${1:name}\" {",
      "  description = \"${2:Description}\"",
      "  type        = ${3:string}",
      "  default     = \"${4:default_value}\"",
      "}"
    ],
    "description": "Create a Terraform variable"
  }
}
```

UTILISATION:
• Tapez "tfres" puis TAB -> crée un bloc resource
• Tapez "tfvar" puis TAB -> crée une variable


VIM / NEOVIM
────────────

```vim
" Dans ~/.vimrc ou ~/.config/nvim/init.vim

" Plugin manager (vim-plug)
call plug#begin()
  Plug 'hashivim/vim-terraform'
  Plug 'vim-syntastic/syntastic'
call plug#end()

" Configuration Terraform
let g:terraform_align=1
let g:terraform_fmt_on_save=1

" Syntastic pour validation
let g:syntastic_terraform_checkers = ['tflint']
```


INTELLIJ / PYCHARM
──────────────────

Plugin: HashiCorp Terraform / HCL Language Support

Installation:
1. File -> Settings -> Plugins
2. Rechercher "Terraform"
3. Installer "HashiCorp Terraform / HCL Language Support"
4. Redémarrer l'IDE


═══════════════════════════════════════════════════════════════════════════════
[RAPIDE] PARTIE 1.5: PREMIERS PAS - HELLO WORLD TERRAFORM
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ PROJET 1: HELLO WORLD LOCAL (Pas de Cloud)                                ║
╚═══════════════════════════════════════════════════════════════════════════╝

OBJECTIF: Créer un fichier local pour comprendre le workflow Terraform.

┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 1: Créer la structure du projet                                     │
└───────────────────────────────────────────────────────────────────────────┘

# 1. Créer un dossier pour le projet
mkdir terraform-hello-world
cd terraform-hello-world

# 2. Créer le fichier de configuration
touch main.tf

# Structure finale:
# terraform-hello-world/
# └── main.tf

POURQUOI "main.tf"?
• Convention standard
• Terraform lit automatiquement tous les *.tf
• Nom clair et reconnaissable


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 2: Écrire le code Terraform                                        │
└───────────────────────────────────────────────────────────────────────────┘

Ouvrir main.tf dans votre éditeur et ajouter:

```hcl
# main.tf
# ═══════════════════════════════════════════════════════════════════════
# Premier projet Terraform: Créer un fichier local
# ═══════════════════════════════════════════════════════════════════════

# ┌─────────────────────────────────────────────────────────────────────┐
# │ BLOC 1: Configuration Terraform                                      │
# └─────────────────────────────────────────────────────────────────────┘

terraform {
  # Version minimale de Terraform requise
  required_version = ">= 1.0"
  
  # Providers nécessaires pour ce projet
  required_providers {
    local = {
      source  = "hashicorp/local"  # Provider pour fichiers locaux
      version = "~> 2.4"           # Version 2.4.x
    }
  }
}

# EXPLICATION LIGNE PAR LIGNE:
# ────────────────────────────
# required_version: Spécifie quelle version de Terraform utiliser
#   ">= 1.0" = version 1.0 ou supérieure
#   Pourquoi? Pour éviter les incompatibilités
#
# required_providers: Liste des plugins nécessaires
#   local = provider pour créer des fichiers sur votre machine
#   source = où télécharger le provider (registry.terraform.io)
#   version = "~> 2.4" signifie: 2.4.0, 2.4.1, 2.4.99 OK, mais PAS 2.5.0


# ┌─────────────────────────────────────────────────────────────────────┐
# │ BLOC 2: Ressource - Créer un fichier                                │
# └─────────────────────────────────────────────────────────────────────┘

resource "local_file" "hello" {
  # Contenu du fichier
  content  = <<-EOT
    ╔════════════════════════════════════════╗
    ║   HELLO WORLD FROM TERRAFORM!          ║
    ║                                        ║
    ║   Ceci est mon premier fichier créé    ║
    ║   avec Infrastructure as Code.         ║
    ║                                        ║
    ║   Date: ${timestamp()}                 ║
    ╚════════════════════════════════════════╝
  EOT
  
  # Où créer le fichier
  filename = "${path.module}/hello-terraform.txt"
  
  # Permissions du fichier (format Unix)
  # 0644 = rw-r--r-- (propriétaire: lecture+écriture, autres: lecture)
  file_permission = "0644"
}

# EXPLICATION DÉTAILLÉE:
# ──────────────────────
# resource: Mot-clé pour définir une ressource
#
# "local_file": Type de ressource
#   Format: PROVIDER_RESOURCETYPE
#   local = provider, file = type de ressource
#
# "hello": Nom que VOUS choisissez
#   Utilisé pour référencer cette ressource ailleurs
#   Référence: local_file.hello
#
# content: Le contenu à mettre dans le fichier
#   <<-EOT ... EOT = heredoc (multi-lignes)
#   ${timestamp()} = fonction Terraform (heure actuelle)
#
# filename: Où créer le fichier
#   ${path.module} = chemin du dossier actuel
#   Résultat: ./hello-terraform.txt
#
# file_permission: Permissions Unix du fichier
#   0644 = propriétaire peut lire/écrire, autres: lecture seule


# ┌─────────────────────────────────────────────────────────────────────┐
# │ BLOC 3: Outputs - Afficher des informations                         │
# └─────────────────────────────────────────────────────────────────────┘

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
}

output "file_id" {
  description = "ID unique du fichier (hash MD5 du contenu)"
  value       = local_file.hello.id
}

# EXPLICATION DES OUTPUTS:
# ────────────────────────
# output: Affiche des valeurs après apply
#   Comme "print()" en Python
#
# description: Documentation (optionnel mais recommandé)
#
# value: Quelle valeur afficher
#   local_file.hello.filename = accède au chemin du fichier
#   local_file.hello.content = accède au contenu
#   local_file.hello.id = hash MD5 du contenu
#
# FORMAT DE RÉFÉRENCE:
#   TYPE.NOM.ATTRIBUT
#   local_file.hello.filename
#   └─TYPE─┘ └NOM┘ └ATTRIBUT┘
```

SAUVEGARDER le fichier (Ctrl+S ou Cmd+S).


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

```bash
terraform init
```

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

1. **Analyse vos fichiers .tf**
   • Lit main.tf
   • Identifie les providers nécessaires (local)

2. **Télécharge les providers**
   • Va sur registry.terraform.io
   • Télécharge hashicorp/local v2.4.x
   • Stocke dans .terraform/providers/

3. **Crée les fichiers de verrouillage**
   • .terraform.lock.hcl
   • Fige les versions exactes utilisées

4. **Prépare le backend**
   • Backend = où stocker le state
   • Par défaut: local (terraform.tfstate)

OUTPUT ATTENDU:
───────────────

```
Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/local versions matching "~> 2.4"...
- Installing hashicorp/local v2.4.1...
- Installed hashicorp/local v2.4.1 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```

FICHIERS CRÉÉS:
───────────────

```
terraform-hello-world/
├── main.tf                      # Votre code
├── .terraform/                  # Dossier des providers (ne pas commiter)
│   └── providers/
│       └── registry.terraform.io/
│           └── hashicorp/
│               └── local/
│                   └── 2.4.1/
│                       └── darwin_arm64/
│                           └── terraform-provider-local_v2.4.1
└── .terraform.lock.hcl          # Versions figées (À COMMITER dans Git!)
```

IMPORTANT:
• .terraform/ -> Ajouter à .gitignore (gros, régénérable)
• .terraform.lock.hcl -> COMMITER dans Git (reproductibilité)


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

```bash
terraform plan
```

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

1. **Lit votre configuration** (main.tf)
2. **Lit le state actuel** (terraform.tfstate - vide pour l'instant)
3. **Compare** "ce qui existe" vs "ce que vous voulez"
4. **Génère un plan** d'exécution
5. **Affiche** ce qui va se passer

OUTPUT ATTENDU:
───────────────

```
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              = <<-EOT
            ╔════════════════════════════════════════╗
            ║   HELLO WORLD FROM TERRAFORM!          ║
            ║                                        ║
            ║   Ceci est mon premier fichier créé    ║
            ║   avec Infrastructure as Code.         ║
            ║                                        ║
            ║   Date: 2024-01-15T14:30:25Z           ║
            ╚════════════════════════════════════════╝
        EOT
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0644"
      + filename             = "./hello-terraform.txt"
      + id                   = (known after apply)
    }

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

Changes to Outputs:
  + file_content = <<-EOT
        ╔════════════════════════════════════════╗
        ║   HELLO WORLD FROM TERRAFORM!          ║
        ...
    EOT
  + file_id      = (known after apply)
  + file_path    = "./hello-terraform.txt"

─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.
```

INTERPRÉTATION DU PLAN:
───────────────────────

**SYMBOLES:**
• `+` = CREATE (va créer une nouvelle ressource)
• `~` = UPDATE (va modifier une ressource existante)
• `-` = DELETE (va détruire une ressource)
• `-/+` = REPLACE (détruire puis recréer)

**"(known after apply)":**
• Valeur qui sera connue SEULEMENT après création
• Exemple: ID d'un fichier = hash de son contenu
• On ne peut pas le savoir avant de créer le fichier

**"Plan: 1 to add, 0 to change, 0 to destroy":**
• 1 ressource sera créée
• 0 sera modifiée
• 0 sera détruite

ANALOGIE:
Le plan est comme un devis de travaux:
• Vous voyez AVANT de payer
• Vous pouvez annuler si ça ne va pas
• Pas de surprise après


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

```bash
terraform apply
```

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

1. **Refait un plan** (sécurité - peut avoir changé depuis le plan)
2. **Affiche le plan**
3. **Demande confirmation**
4. **Si vous tapez "yes":**
   • Crée les ressources (via le provider)
   • Met à jour le state
   • Affiche les outputs

INTERACTION:
────────────

```
[... affichage du plan (comme terraform 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` puis ENTRÉE**

OUTPUT APRÈS APPLICATION:
──────────────────────────

```
local_file.hello: Creating...
local_file.hello: Creation complete after 0s [id=abc123def456...]

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

Outputs:

file_content = <<EOT
╔════════════════════════════════════════╗
║   HELLO WORLD FROM TERRAFORM!          ║
║                                        ║
║   Ceci est mon premier fichier créé    ║
║   avec Infrastructure as Code.         ║
║                                        ║
║   Date: 2024-01-15T14:30:25Z           ║
╚════════════════════════════════════════╝

EOT
file_id = "abc123def456789..."
file_path = "./hello-terraform.txt"
```

FICHIERS CRÉÉS:
───────────────

```
terraform-hello-world/
├── main.tf
├── hello-terraform.txt          # <- NOUVEAU! Le fichier créé
├── terraform.tfstate            # <- NOUVEAU! Le state
├── .terraform/
└── .terraform.lock.hcl
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 6: Vérifier le résultat                                            │
└───────────────────────────────────────────────────────────────────────────┘

```bash
# Lister les fichiers
ls -la

# Devrait afficher hello-terraform.txt

# Lire le contenu
cat hello-terraform.txt
```

OUTPUT:
```
╔════════════════════════════════════════╗
║   HELLO WORLD FROM TERRAFORM!          ║
║                                        ║
║   Ceci est mon premier fichier créé    ║
║   avec Infrastructure as Code.         ║
║                                        ║
║   Date: 2024-01-15T14:30:25Z          ║
╚════════════════════════════════════════╝
```

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


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 7: Inspecter le State                                               │
└───────────────────────────────────────────────────────────────────────────┘

```bash
# Voir le state en format lisible
terraform show

# Ou lire directement le fichier
cat terraform.tfstate
```

CONTENU DU STATE (simplifié):
──────────────────────────────

```json
{
  "version": 4,
  "terraform_version": "1.7.0",
  "resources": [
    {
      "mode": "managed",
      "type": "local_file",
      "name": "hello",
      "provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
      "instances": [
        {
          "attributes": {
            "content": "╔════════════════╗...",
            "filename": "./hello-terraform.txt",
            "id": "abc123def456...",
            "file_permission": "0644"
          }
        }
      ]
    }
  ],
  "outputs": {
    "file_path": {
      "value": "./hello-terraform.txt",
      "type": "string"
    },
    "file_content": {
      "value": "╔════════════════╗...",
      "type": "string"
    },
    "file_id": {
      "value": "abc123def456...",
      "type": "string"
    }
  }
}
```

CE QU'IL FAUT RETENIR:
• Le state est en JSON
• Il contient TOUS les détails des ressources
• Terraform l'utilise pour savoir ce qui existe déjà
• **NE JAMAIS l'éditer manuellement!**


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 8: Modifier et réappliquer                                          │
└───────────────────────────────────────────────────────────────────────────┘

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

**1. Ouvrir main.tf et modifier le content:**

```hcl
resource "local_file" "hello" {
  content  = <<-EOT
    ╔════════════════════════════════════════╗
    ║   HELLO WORLD FROM TERRAFORM!          ║
    ║                                        ║
    ║   J'AI MODIFIÉ CE FICHIER!             ║
    ║   Infrastructure as Code est génial!   ║
    ║                                        ║
    ║   Date: ${timestamp()}                 ║
    ╚════════════════════════════════════════╝
  EOT
  
  filename = "${path.module}/hello-terraform.txt"
  file_permission = "0644"
}
```

**2. Voir ce qui va changer:**

```bash
terraform plan
```

OUTPUT:
```
Terraform will perform the following actions:

  # local_file.hello must be replaced
-/+ resource "local_file" "hello" {
      ~ content              = <<-EOT
          - ╔════════════════════════════════════════╗
          - ║   HELLO WORLD FROM TERRAFORM!          ║
          - ║                                        ║
          - ║   Ceci est mon premier fichier créé    ║
          - ║   avec Infrastructure as Code.         ║
          - ║                                        ║
          - ║   Date: 2024-01-15T14:30:25Z          ║
          - ╚════════════════════════════════════════╝
          + ╔════════════════════════════════════════╗
          + ║   HELLO WORLD FROM TERRAFORM!          ║
          + ║                                        ║
          + ║   J'AI MODIFIÉ CE FICHIER!             ║
          + ║   Infrastructure as Code est génial!   ║
          + ║                                        ║
          + ║   Date: 2024-01-15T14:35:12Z          ║
          + ╚════════════════════════════════════════╝
        EOT
      ~ content_md5          = "abc123..." -> (known after apply)
      ~ id                   = "abc123..." -> (known after apply)
        # (5 unchanged attributes hidden)
    }

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

INTERPRÉTATION:
• `-/+` = REPLACE (détruire puis recréer)
• `-` lignes = ancien contenu
• `+` lignes = nouveau contenu
• `~` = attribut qui change

POURQUOI REPLACE et pas UPDATE?
Le provider local_file ne peut pas "modifier en place".
Solution: supprimer l'ancien, créer le nouveau.

**3. Appliquer:**

```bash
terraform apply
# Tapez "yes"
```

**4. Vérifier:**

```bash
cat hello-terraform.txt
# Devrait afficher le nouveau contenu!
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 9: Détruire les ressources                                         │
└───────────────────────────────────────────────────────────────────────────┘

Pour nettoyer et supprimer le fichier:

```bash
terraform destroy
```

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

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

OUTPUT:
```
Terraform will perform the following actions:

  # local_file.hello will be destroyed
  - resource "local_file" "hello" {
      - content              = <<-EOT
            ╔════════════════════════════════════════╗
            ║   HELLO WORLD FROM TERRAFORM!          ║
            ...
        EOT -> null
      - filename             = "./hello-terraform.txt" -> null
      - id                   = "abc123..." -> null
    }

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

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: 
```

**TAPEZ: `yes`**

APRÈS DESTRUCTION:
```bash
ls -la
# hello-terraform.txt a disparu!

cat terraform.tfstate
# Le state est vide (plus de ressources)
```


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

```
┌──────────────────────────────────────────────────────────────────────┐
│                     WORKFLOW TERRAFORM                                │
└──────────────────────────────────────────────────────────────────────┘

1. terraform init       <- Une fois au début (ou après changement config)
   │
   v
2. terraform fmt        <- Formater le code (optionnel mais recommandé)
   │
   v
3. terraform validate   <- Vérifier la syntaxe (optionnel)
   │
   v
4. terraform plan       <- TOUJOURS! Voir ce qui va changer
   │
   v
5. terraform apply      <- Appliquer les changements
   │
   v
   [Infrastructure créée/modifiée]
   │
   v
6. terraform destroy    <- Nettoyer (si besoin)
```

COMMANDES ESSENTIELLES:
```bash
terraform init       # Initialiser (télécharge providers)
terraform plan       # Prévisualiser les changements
terraform apply      # Appliquer les changements
terraform destroy    # Détruire toute l'infrastructure
terraform fmt        # Formater le code
terraform validate   # Valider la syntaxe
terraform show       # Afficher le state actuel
terraform output     # Afficher les outputs
```


═══════════════════════════════════════════════════════════════════════════════
[NOTE] FIN DE LA PARTIE 1.4 & 1.5
═══════════════════════════════════════════════════════════════════════════════

PROCHAINE PARTIE: terraform_part2_concepts.txt
• Providers en détail
• Resources avancées
• Data Sources
• Variables et types
• Outputs
• Locals
• Modules

Vous avez maintenant:
[OK] Installé Terraform
[OK] Configuré votre éditeur
[OK] Créé votre premier projet
[OK] Compris le workflow de base
[OK] Manipulé le state

Prêt pour des exemples plus avancés! [RAPIDE]

# Fichier: python_cheats/cheatsheets/terraform_part2_concepts.txt
# Terraform - PARTIE 2: CONCEPTS CORE
# Guide Ultra-Détaillé - Providers, Resources, Data Sources, Variables, etc.


═══════════════════════════════════════════════════════════════════════════════
[PACKAGE] PARTIE 2: CONCEPTS CORE DE TERRAFORM
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ 2.1 PROVIDERS - LES CONNECTEURS VERS LES SERVICES                         ║
╚═══════════════════════════════════════════════════════════════════════════╝

DÉFINITION:
───────────

Un PROVIDER est un plugin qui permet à Terraform de communiquer avec une API
externe (cloud, SaaS, base de données, etc.).

ANALOGIE:
─────────

```
PROVIDER = ADAPTATEUR ÉLECTRIQUE

Terraform (prise universelle) -> Provider (adaptateur) -> Service (appareil)
     ┌──────────┐                  ┌──────────┐              ┌──────────┐
     │Terraform │  ──────────────>  │ Provider │  ──────────> │   AWS    │
     │   Code   │                   │   AWS    │              │   API    │
     └──────────┘                   └──────────┘              └──────────┘
```

Le provider traduit votre code HCL en appels API spécifiques au service.


ARCHITECTURE DU PROVIDER:
──────────────────────────

```
┌───────────────────────────────────────────────────────────────┐
│  Terraform Core                                               │
└────────────────────────┬──────────────────────────────────────┘
                         │
                         v
┌────────────────────────────────────────────────────────────────┐
│  Provider Interface (gRPC)                                     │
└────────────────────────┬───────────────────────────────────────┘
                         │
         ┌───────────────┼───────────────┬───────────────┐
         v               v               v               v
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│  AWS SDK     │ │  GCP SDK     │ │ Azure SDK    │ │  K8s Client  │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
       │                │                │                │
       v                v                v                v
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│   AWS API    │ │   GCP API    │ │  Azure API   │ │   K8s API    │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
```


CONFIGURATION D'UN PROVIDER:
─────────────────────────────

┌───────────────────────────────────────────────────────────────────────────┐
│ SYNTAXE BASIQUE                                                           │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# Déclaration du provider requis
terraform {
  required_providers {
    PROVIDER_NAME = {
      source  = "NAMESPACE/PROVIDER_NAME"
      version = "VERSION_CONSTRAINT"
    }
  }
}

# Configuration du provider
provider "PROVIDER_NAME" {
  # Configuration spécifique au provider
  argument1 = value1
  argument2 = value2
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ EXEMPLE: PROVIDER AWS                                                     │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# versions.tf
# ═══════════════════════════════════════════════════════════════════════

terraform {
  required_version = ">= 1.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"  # Version 5.x
    }
  }
}

# Configuration du provider AWS
provider "aws" {
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ RÉGION                                                           │
  # └─────────────────────────────────────────────────────────────────┘
  region = "us-east-1"
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ PROFILE (si vous utilisez AWS CLI configuré)                    │
  # └─────────────────────────────────────────────────────────────────┘
  profile = "default"  # Lit ~/.aws/credentials
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ AUTHENTIFICATION DIRECTE (déconseillé, utilisez variables)      │
  # └─────────────────────────────────────────────────────────────────┘
  # access_key = "AKIAIOSFODNN7EXAMPLE"  # [X] NE JAMAIS HARDCODER!
  # secret_key = "wJalrXUtnFEMI/K7..."    # [X] NE JAMAIS HARDCODER!
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ ASSUME ROLE (pour multi-comptes)                                │
  # └─────────────────────────────────────────────────────────────────┘
  assume_role {
    role_arn     = "arn:aws:iam::123456789012:role/TerraformRole"
    session_name = "terraform-session"
  }
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ TAGS PAR DÉFAUT (appliqués à toutes les ressources)             │
  # └─────────────────────────────────────────────────────────────────┘
  default_tags {
    tags = {
      Environment = "Production"
      ManagedBy   = "Terraform"
      Project     = "MyApp"
    }
  }
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ CONFIGURATION RÉSEAU                                             │
  # └─────────────────────────────────────────────────────────────────┘
  # Utile si derrière un proxy
  # http_proxy = "http://proxy.example.com:8080"
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ RETRY ET TIMEOUT                                                 │
  # └─────────────────────────────────────────────────────────────────┘
  max_retries = 3  # Nombre de retry en cas d'erreur API
}
```

MÉTHODES D'AUTHENTIFICATION AWS (du plus sûr au moins sûr):
───────────────────────────────────────────────────────────

1. **IAM ROLE (Recommandé en production)**
   ```hcl
   provider "aws" {
     assume_role {
       role_arn = "arn:aws:iam::ACCOUNT:role/ROLE_NAME"
     }
   }
   ```
   [OK] Pas de credentials stockés
   [OK] Rotation automatique
   [OK] Audit trail complet

2. **VARIABLES D'ENVIRONNEMENT (Bon pour dev local)**
   ```bash
   export AWS_ACCESS_KEY_ID="..."
   export AWS_SECRET_ACCESS_KEY="..."
   export AWS_DEFAULT_REGION="us-east-1"
   terraform apply
   ```
   [OK] Pas dans le code
   [OK] Facile à changer
   [X] Doit être configuré sur chaque machine

3. **AWS CLI PROFILE (Pratique pour multi-comptes)**
   ```hcl
   provider "aws" {
     profile = "production"  # Lit ~/.aws/credentials
   }
   ```
   [OK] Gestion centralisée des credentials
   [OK] Facile de switcher entre comptes

4. **HARDCODÉ ([X] NE JAMAIS FAIRE!)**
   ```hcl
   provider "aws" {
     access_key = "AKIAIOSFODNN7EXAMPLE"  # [X] DANGER!
     secret_key = "wJalrXUtnFEMI/K7..."    # [X] DANGER!
   }
   ```
   [X] Credentials dans Git
   [X] Risque de leak
   [X] Pas de rotation


┌───────────────────────────────────────────────────────────────────────────┐
│ PROVIDER ALIAS - MULTI-RÉGIONS                                           │
└───────────────────────────────────────────────────────────────────────────┘

POURQUOI?
Pour gérer des ressources dans plusieurs régions/comptes simultanément.

EXEMPLE: Déployer en US et EU:

```hcl
# Provider par défaut (US East)
provider "aws" {
  region = "us-east-1"
}

# Provider alternatif (Europe)
provider "aws" {
  alias  = "eu"  # <- Nom personnalisé
  region = "eu-west-1"
}

# Provider alternatif (Asie)
provider "aws" {
  alias  = "asia"
  region = "ap-southeast-1"
}

# ═══════════════════════════════════════════════════════════════════════
# UTILISATION
# ═══════════════════════════════════════════════════════════════════════

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

# Ressource en Europe (provider alias "eu")
resource "aws_instance" "eu_server" {
  provider = aws.eu  # <- Spécifie l'alias
  
  ami           = "ami-eu..."
  instance_type = "t2.micro"
  
  tags = {
    Name = "EU Server"
  }
}

# Ressource en Asie (provider alias "asia")
resource "aws_instance" "asia_server" {
  provider = aws.asia
  
  ami           = "ami-asia..."
  instance_type = "t2.micro"
  
  tags = {
    Name = "Asia Server"
  }
}
```

CAS D'USAGE MULTI-PROVIDER ALIAS:
──────────────────────────────────

1. **Multi-régions** (haute disponibilité)
2. **Multi-comptes** (dev/staging/prod séparés)
3. **Disaster Recovery** (backup dans autre région)
4. **Compliance** (données dans région spécifique)


┌───────────────────────────────────────────────────────────────────────────┐
│ PROVIDERS POPULAIRES - APERÇU                                            │
└───────────────────────────────────────────────────────────────────────────┘

╔════════════════════════════════════════════════════════════════════════╗
║ CLOUD PROVIDERS                                                         ║
╚════════════════════════════════════════════════════════════════════════╝

1. **AWS (Amazon Web Services)**
   ```hcl
   provider "aws" {
     region = "us-east-1"
   }
   ```
   • 1000+ types de ressources
   • Provider le plus mature
   • Documentation exhaustive

2. **GCP (Google Cloud Platform)**
   ```hcl
   provider "google" {
     project = "my-project-id"
     region  = "us-central1"
   }
   ```
   • 500+ types de ressources
   • Excellent pour BigData/ML
   • Integration Kubernetes native

3. **Azure (Microsoft)**
   ```hcl
   provider "azurerm" {
     features {}
     subscription_id = "xxx"
   }
   ```
   • 700+ types de ressources
   • Meilleur pour entreprises Microsoft
   • Integration AD/Windows

4. **DigitalOcean** (Simple et abordable)
   ```hcl
   provider "digitalocean" {
     token = var.do_token
   }
   ```

5. **OVH** (Europe)
   ```hcl
   provider "ovh" {
     endpoint = "ovh-eu"
   }
   ```

6. **Alibaba Cloud** (Asie)
   ```hcl
   provider "alicloud" {
     region = "cn-beijing"
   }
   ```


╔════════════════════════════════════════════════════════════════════════╗
║ ORCHESTRATION                                                           ║
╚════════════════════════════════════════════════════════════════════════╝

1. **Kubernetes**
   ```hcl
   provider "kubernetes" {
     config_path = "~/.kube/config"
   }
   ```
   • Gérer deployments, services, ingress
   • Complète kubectl

2. **Docker**
   ```hcl
   provider "docker" {
     host = "unix:///var/run/docker.sock"
   }
   ```
   • Gérer containers, images, networks
   • Idéal pour dev local

3. **Nomad** (HashiCorp)
   ```hcl
   provider "nomad" {
     address = "http://nomad.service.consul:4646"
   }
   ```


╔════════════════════════════════════════════════════════════════════════╗
║ MONITORING & OBSERVABILITY                                             ║
╚════════════════════════════════════════════════════════════════════════╝

1. **Datadog**
   ```hcl
   provider "datadog" {
     api_key = var.datadog_api_key
     app_key = var.datadog_app_key
   }
   ```

2. **Grafana**
   ```hcl
   provider "grafana" {
     url  = "http://grafana.example.com"
     auth = var.grafana_token
   }
   ```

3. **PagerDuty**
   ```hcl
   provider "pagerduty" {
     token = var.pagerduty_token
   }
   ```


╔════════════════════════════════════════════════════════════════════════╗
║ DNS & CDN                                                              ║
╚════════════════════════════════════════════════════════════════════════╝

1. **Cloudflare**
   ```hcl
   provider "cloudflare" {
     api_token = var.cloudflare_token
   }
   ```

2. **Route53** (intégré AWS)

3. **DNSimple**
   ```hcl
   provider "dnsimple" {
     token = var.dnsimple_token
   }
   ```


╔════════════════════════════════════════════════════════════════════════╗
║ VCS & CI/CD                                                            ║
╚════════════════════════════════════════════════════════════════════════╝

1. **GitHub**
   ```hcl
   provider "github" {
     token = var.github_token
     owner = "my-org"
   }
   ```
   • Gérer repos, teams, webhooks
   • Automatiser GitHub

2. **GitLab**
   ```hcl
   provider "gitlab" {
     token = var.gitlab_token
   }
   ```

3. **CircleCI**
   ```hcl
   provider "circleci" {
     api_token = var.circleci_token
   }
   ```


╔════════════════════════════════════════════════════════════════════════╗
║ DATABASES                                                               ║
╚════════════════════════════════════════════════════════════════════════╝

1. **MongoDB Atlas**
   ```hcl
   provider "mongodbatlas" {
     public_key  = var.atlas_public_key
     private_key = var.atlas_private_key
   }
   ```

2. **PostgreSQL**
   ```hcl
   provider "postgresql" {
     host     = "postgres.example.com"
     password = var.db_password
   }
   ```


╔════════════════════════════════════════════════════════════════════════╗
║ SAAS & AUTRES                                                          ║
╚════════════════════════════════════════════════════════════════════════╝

1. **Stripe** (Paiements)
   ```hcl
   provider "stripe" {
     api_key = var.stripe_key
   }
   ```

2. **Twilio** (SMS/Voice)
   ```hcl
   provider "twilio" {
     account_sid = var.twilio_sid
     auth_token  = var.twilio_token
   }
   ```

3. **Auth0** (Authentification)
   ```hcl
   provider "auth0" {
     domain        = "myapp.auth0.com"
     client_id     = var.auth0_client_id
     client_secret = var.auth0_client_secret
   }
   ```

TOTAL: **3000+ providers** disponibles sur registry.terraform.io!


╔═══════════════════════════════════════════════════════════════════════════╗
║ 2.2 RESOURCES - CRÉER DES RESSOURCES                                      ║
╚═══════════════════════════════════════════════════════════════════════════╝

DÉFINITION:
───────────

Une RESOURCE est un composant de l'infrastructure que Terraform va créer,
modifier ou détruire (VM, base de données, bucket S3, DNS record, etc.).

SYNTAXE GÉNÉRALE:
─────────────────

```hcl
resource "RESOURCE_TYPE" "RESOURCE_NAME" {
  argument1 = value1
  argument2 = value2
  
  nested_block {
    nested_argument = value
  }
  
  # Meta-arguments (disponibles pour TOUTES les ressources)
  count      = NUMBER
  for_each   = MAP_OR_SET
  depends_on = [OTHER_RESOURCES]
  provider   = PROVIDER_ALIAS
  lifecycle {
    # lifecycle rules
  }
}
```

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

```
resource "aws_instance" "web_server" {
         ^              ^
         │              └─ NOM LOCAL (vous le choisissez)
         │                 Utilisation: aws_instance.web_server
         │
         └─ TYPE (défini par le provider)
            Format: PROVIDER_RESOURCETYPE
            Exemple: aws_instance, google_compute_instance
```


┌───────────────────────────────────────────────────────────────────────────┐
│ EXEMPLE COMPLET: EC2 INSTANCE                                            │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# ═══════════════════════════════════════════════════════════════════════
# Création d'une instance EC2 AWS
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web_server" {
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ ARGUMENTS OBLIGATOIRES                                          │
  # └─────────────────────────────────────────────────────────────────┘
  
  # AMI: ID de l'image (système d'exploitation)
  ami = "ami-0c55b159cbfafe1f0"  # Ubuntu 22.04 us-east-1
  
  # Type d'instance (CPU, RAM)
  instance_type = "t2.micro"  # 1 vCPU, 1GB RAM (gratuit tier)
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ ARGUMENTS OPTIONNELS (configuration avancée)                     │
  # └─────────────────────────────────────────────────────────────────┘
  
  # Clé SSH pour se connecter
  key_name = "my-ssh-key"
  
  # Security groups (pare-feu)
  vpc_security_group_ids = [aws_security_group.web.id]
  
  # Subnet (réseau)
  subnet_id = aws_subnet.public.id
  
  # IP publique automatique
  associate_public_ip_address = true
  
  # IAM role pour permissions
  iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
  
  # Monitoring détaillé (coût additionnel)
  monitoring = true
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ NESTED BLOCKS (configuration de sous-composants)                 │
  # └─────────────────────────────────────────────────────────────────┘
  
  # Configuration du disque racine
  root_block_device {
    volume_type           = "gp3"     # Type SSD
    volume_size           = 20        # 20 GB
    delete_on_termination = true      # Supprimer avec l'instance
    encrypted             = true      # Chiffré
    
    tags = {
      Name = "web-server-root-disk"
    }
  }
  
  # Disque additionnel (optionnel)
  ebs_block_device {
    device_name           = "/dev/sdf"
    volume_type           = "gp3"
    volume_size           = 100       # 100 GB
    delete_on_termination = false     # Garder après destruction
    encrypted             = true
    
    tags = {
      Name = "web-server-data-disk"
    }
  }
  
  # Configuration réseau additionnelle
  network_interface {
    network_interface_id = aws_network_interface.web.id
    device_index         = 1
  }
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ USER DATA (script d'initialisation)                              │
  # └─────────────────────────────────────────────────────────────────┘
  
  # Script bash exécuté au premier démarrage
  user_data = <<-EOF
    #!/bin/bash
    apt-get update
    apt-get install -y nginx
    echo "Hello from Terraform!" > /var/www/html/index.html
    systemctl start nginx
  EOF
  
  # Ou charger depuis un fichier
  # user_data = file("${path.module}/init-script.sh")
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ TAGS (métadonnées)                                               │
  # └─────────────────────────────────────────────────────────────────┘
  
  tags = {
    Name        = "web-server-001"
    Environment = "production"
    ManagedBy   = "Terraform"
    Project     = "MyWebApp"
    Owner       = "team-backend"
  }
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ META-ARGUMENTS                                                    │
  # └─────────────────────────────────────────────────────────────────┘
  
  # Dépendances explicites
  depends_on = [
    aws_security_group.web,
    aws_subnet.public
  ]
  
  # Cycle de vie
  lifecycle {
    # Créer la nouvelle avant de détruire l'ancienne (zero downtime)
    create_before_destroy = true
    
    # Ignorer les changements sur ces attributs
    ignore_changes = [
      user_data,  # Ne pas recréer si user_data change
      tags["LastModified"]
    ]
    
    # Empêcher la destruction
    # prevent_destroy = true  # Décommenter pour protection
  }
}
```

EXPLICATION DES SECTIONS:
──────────────────────────

1. **ARGUMENTS**: Configuration directe de la ressource
   • ami, instance_type = obligatoires pour EC2
   • key_name, monitoring = optionnels

2. **NESTED BLOCKS**: Sous-composants configurables
   • root_block_device = disque principal
   • ebs_block_device = disques additionnels
   • Peuvent avoir leurs propres arguments

3. **USER DATA**: Script d'initialisation
   • Exécuté au premier boot
   • Utile pour installer des packages
   • Format: bash script ou cloud-init YAML

4. **TAGS**: Métadonnées
   • Organisent et catégorisent les ressources
   • Utilisés pour filtrage, coûts, automatisation
   • Best practice: toujours tagger!

5. **META-ARGUMENTS**: Options universelles
   • depends_on: Force l'ordre d'exécution
   • lifecycle: Contrôle le cycle de vie
   • count/for_each: Créer plusieurs ressources
   • (Détaillés dans PARTIE 5)


┌───────────────────────────────────────────────────────────────────────────┐
│ RÉFÉRENCES ENTRE RESSOURCES                                              │
└───────────────────────────────────────────────────────────────────────────┘

SYNTAXE:
────────

```
RESOURCE_TYPE.RESOURCE_NAME.ATTRIBUTE
```

EXEMPLE:
────────

```hcl
# Créer un security group
resource "aws_security_group" "web" {
  name = "web-sg"
  
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# Créer une instance qui UTILISE ce security group
resource "aws_instance" "web" {
  ami           = "ami-123"
  instance_type = "t2.micro"
  
  # Référence à l'attribut "id" du security group
  vpc_security_group_ids = [aws_security_group.web.id]
  #                         └──────────────┬─────────────┘
  #                                        │
  #                              Référence implicite
  #                       Terraform crée le SG en premier!
}

# Créer un Elastic IP
resource "aws_eip" "web" {
  instance = aws_instance.web.id  # <- Référence à l'instance
  domain   = "vpc"
}

# Output pour afficher l'IP
output "server_ip" {
  value = aws_eip.web.public_ip  # <- Référence à l'attribut de l'EIP
}
```

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

```
┌──────────────────────────────────────────────────────────────────┐
│ GRAPHE DE DÉPENDANCES (généré automatiquement)                   │
└──────────────────────────────────────────────────────────────────┘

                ┌───────────────────┐
                │aws_security_group │
                │       .web        │
                └─────────┬─────────┘
                          │
                          v (id)
                ┌───────────────────┐
                │  aws_instance     │
                │      .web         │
                └─────────┬─────────┘
                          │
                          v (id)
                ┌───────────────────┐
                │   aws_eip.web     │
                └───────────────────┘

ORDRE D'EXÉCUTION:
1. Créer aws_security_group.web
2. Créer aws_instance.web (attend le SG)
3. Créer aws_eip.web (attend l'instance)
```

TERRAFORM DÉTECTE AUTOMATIQUEMENT LES DÉPENDANCES!


┌───────────────────────────────────────────────────────────────────────────┐
│ ATTRIBUTS DISPONIBLES                                                     │
└───────────────────────────────────────────────────────────────────────────┘

Chaque ressource expose des attributs après création.

COMMENT SAVOIR QUELS ATTRIBUTS?
────────────────────────────────

1. **Documentation officielle**:
   https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance

2. **Terraform show**:
   ```bash
   terraform apply
   terraform show
   # Affiche tous les attributs de toutes les ressources
   ```

3. **Terraform console** (interactif):
   ```bash
   terraform console
   > aws_instance.web.public_ip
   "54.123.45.67"
   > aws_instance.web.id
   "i-1234567890abcdef0"
   ```

ATTRIBUTS COURANTS (aws_instance):
──────────────────────────────────

```hcl
aws_instance.web.id                    # "i-1234567890abcdef0"
aws_instance.web.arn                   # ARN complet
aws_instance.web.public_ip             # "54.123.45.67"
aws_instance.web.private_ip            # "10.0.1.10"
aws_instance.web.public_dns            # "ec2-54-123-45-67..."
aws_instance.web.availability_zone     # "us-east-1a"
aws_instance.web.instance_state        # "running"
aws_instance.web.primary_network_interface_id
aws_instance.web.root_block_device[0].volume_id
```


(Suite dans le prochain message - fichier trop long...)

# Fichier: python_cheats/cheatsheets/terraform_part2_suite.txt
# Terraform - PARTIE 2 (Suite): Data Sources, Variables, Outputs, Locals, Modules
# Guide Ultra-Détaillé


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

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

DÉFINITION:
───────────

Un DATA SOURCE permet de LIRE des informations depuis une source externe
SANS créer de nouvelles ressources.

DIFFÉRENCE FONDAMENTALE:
────────────────────────

```
┌─────────────────────────────────────────────────────────────────────┐
│ RESOURCE                          │ DATA SOURCE                      │
├───────────────────────────────────┼──────────────────────────────────┤
│ CRÉE une ressource                │ LIT une ressource existante      │
│ Géré par Terraform                │ Géré ailleurs (manuel, autre tf) │
│ Peut être modifié/détruit         │ Lecture seule                    │
│ Modifie l'infrastructure          │ N'affecte rien                   │
│                                   │                                  │
│ Exemple:                          │ Exemple:                         │
│ resource "aws_instance" "web" {   │ data "aws_ami" "ubuntu" {        │
│   ami = "ami-123"                 │   most_recent = true             │
│ }                                 │ }                                │
│ -> Crée une VM                     │ -> Trouve l'AMI Ubuntu récente    │
└─────────────────────────────────────────────────────────────────────┘
```

SYNTAXE:
────────

```hcl
data "DATA_SOURCE_TYPE" "DATA_SOURCE_NAME" {
  # Critères de recherche / filtres
  argument1 = value1
  argument2 = value2
  
  filter {
    name   = "filter_name"
    values = ["filter_value"]
  }
}

# Utilisation
resource "some_resource" "example" {
  value = data.DATA_SOURCE_TYPE.DATA_SOURCE_NAME.ATTRIBUTE
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ EXEMPLE 1: RÉCUPÉRER UNE AMI (IMAGE)                                     │
└───────────────────────────────────────────────────────────────────────────┘

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

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

MAUVAISE APPROCHE (hardcodé):
──────────────────────────────

```hcl
resource "aws_instance" "web" {
  ami = "ami-0c55b159cbfafe1f0"  # [X] Valide UNIQUEMENT en us-east-1
  # [X] Obsolète dans 6 mois (nouvelle version Ubuntu)
  # [X] Ne marche pas en eu-west-1
}
```

BONNE APPROCHE (data source):
──────────────────────────────

```hcl
# ═══════════════════════════════════════════════════════════════════════
# Data source: Trouver l'AMI Ubuntu la plus récente
# ═══════════════════════════════════════════════════════════════════════

data "aws_ami" "ubuntu" {
  # Prendre la plus récente
  most_recent = true
  
  # Filtre par propriétaire
  # 099720109477 = Canonical (éditeur officiel d'Ubuntu)
  owners = ["099720109477"]
  
  # Filtres pour trouver la bonne image
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
    #         └─────┬──────┘ └┬─┘ └─┬──┘ └────────┬─────────┘ └┬──┘ └─┬────┘
    #               │         │     │             │             │      │
    #          Distro    Type SSD  Ubuntu     Version      Arch     Type
  }
  
  filter {
    name   = "virtualization-type"
    values = ["hvm"]  # Type de virtualisation moderne
  }
  
  filter {
    name   = "root-device-type"
    values = ["ebs"]  # Stockage EBS (disque réseau)
  }
  
  filter {
    name   = "architecture"
    values = ["x86_64"]  # Architecture 64-bit
  }
}

# ═══════════════════════════════════════════════════════════════════════
# Utilisation dans une ressource
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" {
  # Utiliser l'ID de l'AMI trouvée par le data source
  ami           = data.aws_ami.ubuntu.id
  #               └────────┬────────┘ └─┬─┘ └┬┘
  #                        │           │    └─ Attribut (id)
  #                        │           └────── Nom du data source
  #                        └──────────────── Type du data source
  
  instance_type = "t2.micro"
  
  tags = {
    Name    = "Web Server"
    OS      = "Ubuntu ${data.aws_ami.ubuntu.name}"
    Version = data.aws_ami.ubuntu.image_location
  }
}

# ═══════════════════════════════════════════════════════════════════════
# Output pour voir les informations de l'AMI
# ═══════════════════════════════════════════════════════════════════════

output "ami_info" {
  value = {
    id               = data.aws_ami.ubuntu.id
    name             = data.aws_ami.ubuntu.name
    description      = data.aws_ami.ubuntu.description
    creation_date    = data.aws_ami.ubuntu.creation_date
    image_location   = data.aws_ami.ubuntu.image_location
    architecture     = data.aws_ami.ubuntu.architecture
    root_device_type = data.aws_ami.ubuntu.root_device_type
  }
}
```

AVANTAGES:
──────────

[OK] Fonctionne dans N'IMPORTE QUELLE région
[OK] Toujours la dernière version d'Ubuntu
[OK] Pas besoin de maintenir une liste d'AMI IDs
[OK] Auto-update quand nouvelle version sort


┌───────────────────────────────────────────────────────────────────────────┐
│ EXEMPLE 2: LISTER LES ZONES DE DISPONIBILITÉ                             │
└───────────────────────────────────────────────────────────────────────────┘

POURQUOI?
─────────

Pour créer des ressources dans TOUTES les AZs disponibles sans hardcoder.

```hcl
# ═══════════════════════════════════════════════════════════════════════
# Data source: Récupérer toutes les AZs disponibles
# ═══════════════════════════════════════════════════════════════════════

data "aws_availability_zones" "available" {
  # Seulement les AZs actives (pas en maintenance)
  state = "available"
  
  # Filtrer les AZs (optionnel)
  filter {
    name   = "opt-in-status"
    values = ["opt-in-not-required"]
  }
  
  # Exclure certaines AZs (optionnel)
  # Utile si certaines AZs ne supportent pas tous les services
  exclude_names = ["us-east-1e"]
}

# ═══════════════════════════════════════════════════════════════════════
# Utilisation: Créer des subnets dans chaque AZ
# ═══════════════════════════════════════════════════════════════════════

resource "aws_subnet" "public" {
  # Créer un subnet par AZ disponible
  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-subnet-${data.aws_availability_zones.available.names[count.index]}"
    AZ   = data.aws_availability_zones.available.names[count.index]
  }
}

# ═══════════════════════════════════════════════════════════════════════
# Output
# ═══════════════════════════════════════════════════════════════════════

output "availability_zones" {
  description = "Liste des AZs disponibles"
  value       = data.aws_availability_zones.available.names
  # Output: ["us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d", "us-east-1f"]
}
```

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

```hcl
data.aws_availability_zones.available.names
# ["us-east-1a", "us-east-1b", "us-east-1c", ...]

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

data.aws_availability_zones.available.group_names
# Noms des groupes de régions
```


┌───────────────────────────────────────────────────────────────────────────┐
│ EXEMPLE 3: RÉCUPÉRER UN VPC EXISTANT                                     │
└───────────────────────────────────────────────────────────────────────────┘

SCÉNARIO:
─────────

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

```hcl
# ═══════════════════════════════════════════════════════════════════════
# Option 1: Récupérer le VPC par défaut
# ═══════════════════════════════════════════════════════════════════════

data "aws_vpc" "default" {
  default = true  # Le VPC par défaut de la région
}

output "default_vpc_id" {
  value = data.aws_vpc.default.id
}


# ═══════════════════════════════════════════════════════════════════════
# Option 2: Récupérer un VPC par tag
# ═══════════════════════════════════════════════════════════════════════

data "aws_vpc" "prod" {
  # Filtrer par tag
  tags = {
    Name        = "production-vpc"
    Environment = "prod"
  }
}

# Utilisation
resource "aws_security_group" "web" {
  vpc_id = data.aws_vpc.prod.id
  name   = "web-sg"
  
  # ... configuration ...
}


# ═══════════════════════════════════════════════════════════════════════
# Option 3: Récupérer un VPC par ID (si connu)
# ═══════════════════════════════════════════════════════════════════════

data "aws_vpc" "existing" {
  id = "vpc-1234567890abcdef0"
}


# ═══════════════════════════════════════════════════════════════════════
# Option 4: Récupérer un VPC par CIDR
# ═══════════════════════════════════════════════════════════════════════

data "aws_vpc" "by_cidr" {
  cidr_block = "10.0.0.0/16"
}
```

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

```hcl
data.aws_vpc.prod.id                    # "vpc-123..."
data.aws_vpc.prod.arn                   # ARN complet
data.aws_vpc.prod.cidr_block            # "10.0.0.0/16"
data.aws_vpc.prod.enable_dns_hostnames  # true/false
data.aws_vpc.prod.enable_dns_support    # true/false
data.aws_vpc.prod.tags                  # Map de tags
```


┌───────────────────────────────────────────────────────────────────────────┐
│ EXEMPLE 4: RÉCUPÉRER DES SECRETS (AWS Secrets Manager)                   │
└───────────────────────────────────────────────────────────────────────────┘

SÉCURITÉ: Ne jamais hardcoder des mots de passe dans le code!

```hcl
# ═══════════════════════════════════════════════════════════════════════
# Data source: Récupérer un secret depuis AWS Secrets Manager
# ═══════════════════════════════════════════════════════════════════════

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

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

# ═══════════════════════════════════════════════════════════════════════
# Parser le JSON (si le secret est au format JSON)
# ═══════════════════════════════════════════════════════════════════════

locals {
  # Le secret est stocké au format JSON:
  # {"username": "admin", "password": "secret123", "host": "db.example.com"}
  db_credentials = jsondecode(
    data.aws_secretsmanager_secret_version.db_password.secret_string
  )
}

# ═══════════════════════════════════════════════════════════════════════
# Utilisation dans une ressource RDS
# ═══════════════════════════════════════════════════════════════════════

resource "aws_db_instance" "main" {
  identifier = "prod-database"
  engine     = "postgres"
  
  # Utiliser les credentials depuis Secrets Manager
  username = local.db_credentials.username  # "admin"
  password = local.db_credentials.password  # "secret123"
  
  # Autres paramètres
  instance_class    = "db.t3.micro"
  allocated_storage = 20
  
  tags = {
    Name = "Production Database"
  }
}
```

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] Chiffrement au repos
[OK] Contrôle d'accès IAM


┌───────────────────────────────────────────────────────────────────────────┐
│ EXEMPLE 5: INFORMATIONS SUR LE COMPTE AWS ACTUEL                         │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# ═══════════════════════════════════════════════════════════════════════
# Data source: Informations sur le compte AWS utilisé
# ═══════════════════════════════════════════════════════════════════════

data "aws_caller_identity" "current" {}

# ═══════════════════════════════════════════════════════════════════════
# Data source: Région AWS actuelle
# ═══════════════════════════════════════════════════════════════════════

data "aws_region" "current" {}

# ═══════════════════════════════════════════════════════════════════════
# Utilisation: Construire des ARNs dynamiquement
# ═══════════════════════════════════════════════════════════════════════

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

# ═══════════════════════════════════════════════════════════════════════
# Outputs
# ═══════════════════════════════════════════════════════════════════════

output "account_info" {
  value = {
    account_id = data.aws_caller_identity.current.account_id
    user_id    = data.aws_caller_identity.current.user_id
    arn        = data.aws_caller_identity.current.arn
    region     = data.aws_region.current.name
  }
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ DATA SOURCES POPULAIRES PAR PROVIDER                                     │
└───────────────────────────────────────────────────────────────────────────┘

AWS:
────
• aws_ami                  - Trouver une AMI
• aws_availability_zones   - Lister les AZs
• aws_vpc                  - Récupérer un VPC
• aws_subnet              - Récupérer un subnet
• aws_security_group      - Récupérer un SG
• aws_iam_policy_document - Générer une policy IAM
• aws_route53_zone        - Récupérer une zone DNS
• aws_s3_bucket           - Infos sur un bucket S3
• aws_secretsmanager_*    - Récupérer des secrets
• aws_caller_identity     - Infos sur le compte
• aws_region              - Région actuelle

GCP:
────
• google_compute_image    - Trouver une image
• google_compute_zones    - Lister les zones
• google_client_config    - Config du client
• google_project          - Infos sur le projet

Azure:
──────
• azurerm_subscription    - Infos sur la subscription
• azurerm_client_config   - Config du client
• azurerm_resource_group  - Récupérer un resource group


═══════════════════════════════════════════════════════════════════════════════
[CONTROL_KNOBS] PARTIE 2.4: VARIABLES - PARAMÉTRAGE FLEXIBLE
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ POURQUOI DES VARIABLES?                                                   ║
╚═══════════════════════════════════════════════════════════════════════════╝

PROBLÈME SANS VARIABLES:
────────────────────────

```hcl
# [X] Code rigide et difficile à réutiliser
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"  # Hardcodé
  instance_type = "t2.micro"                # Hardcodé
  
  tags = {
    Name        = "web-server"              # Hardcodé
    Environment = "production"              # Hardcodé
  }
}

# Pour changer l'instance_type, il faut modifier le code!
# Pour chaque environnement (dev/staging/prod), copier tout le code!
```

SOLUTION AVEC VARIABLES:
────────────────────────

```hcl
# [OK] Code flexible et réutilisable
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = var.instance_type
  
  tags = {
    Name        = "${var.project_name}-web-server"
    Environment = var.environment
  }
}

# Changer les valeurs = changer les variables!
# Même code pour dev/staging/prod avec des valeurs différentes!
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ SYNTAXE COMPLÈTE DES VARIABLES                                            ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# variables.tf
# ═══════════════════════════════════════════════════════════════════════

variable "VARIABLE_NAME" {
  # Description (optionnel mais FORTEMENT recommandé)
  description = "Description claire de la variable"
  
  # Type de données (optionnel mais recommandé)
  type = TYPE
  
  # Valeur par défaut (optionnel)
  default = DEFAULT_VALUE
  
  # Variable sensible (masquée dans les logs)
  sensitive = true/false
  
  # Validation (optionnel)
  validation {
    condition     = BOOLEAN_EXPRESSION
    error_message = "Message d'erreur si condition fausse"
  }
  
  # Nullable (peut être null)
  nullable = true/false
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ TYPES DE VARIABLES - COMPLET                                             │
└───────────────────────────────────────────────────────────────────────────┘

1. TYPES PRIMITIFS
   ───────────────

```hcl
# ═══════════════════════════════════════════════════════════════════════
# STRING (chaîne de caractères)
# ═══════════════════════════════════════════════════════════════════════

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

# Utilisation
resource "aws_instance" "web" {
  tags = {
    Name = var.project_name  # "my-app"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# NUMBER (nombre entier ou décimal)
# ═══════════════════════════════════════════════════════════════════════

variable "instance_count" {
  description = "Nombre d'instances à créer"
  type        = number
  default     = 2
}

variable "cpu_credits" {
  description = "Crédits CPU par heure"
  type        = number
  default     = 0.5  # Décimal OK
}

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


# ═══════════════════════════════════════════════════════════════════════
# BOOL (booléen: true ou false)
# ═══════════════════════════════════════════════════════════════════════

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

variable "is_production" {
  description = "Est-ce l'environnement de production?"
  type        = bool
  default     = false
}

# Utilisation
resource "aws_instance" "web" {
  monitoring = var.enable_monitoring  # true
  
  instance_type = var.is_production ? "t3.large" : "t2.micro"
  # Si prod = true -> t3.large, sinon -> t2.micro
}
```

2. TYPES COLLECTION
   ────────────────

```hcl
# ═══════════════════════════════════════════════════════════════════════
# LIST (liste ordonnée de valeurs du même type)
# ═══════════════════════════════════════════════════════════════════════

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

# Accès aux éléments (index commence à 0)
locals {
  first_az  = var.availability_zones[0]  # "us-east-1a"
  second_az = var.availability_zones[1]  # "us-east-1b"
  count_az  = length(var.availability_zones)  # 3
}

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


# ═══════════════════════════════════════════════════════════════════════
# SET (ensemble de valeurs uniques, pas d'ordre garanti)
# ═══════════════════════════════════════════════════════════════════════

variable "allowed_ports" {
  description = "Ports autorisés (uniques)"
  type        = set(number)
  default     = [80, 443, 8080]
}

# Utilisation avec for_each
resource "aws_security_group_rule" "ingress" {
  for_each = var.allowed_ports
  
  type        = "ingress"
  from_port   = each.value  # 80, puis 443, puis 8080
  to_port     = each.value
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"]
}


# ═══════════════════════════════════════════════════════════════════════
# MAP (dictionnaire clé-valeur)
# ═══════════════════════════════════════════════════════════════════════

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

variable "tags" {
  description = "Tags à appliquer"
  type        = map(string)
  default = {
    Project     = "MyApp"
    Environment = "dev"
    ManagedBy   = "Terraform"
  }
}

# Accès aux valeurs
locals {
  prod_type = var.instance_types["prod"]  # "t3.large"
  dev_type  = var.instance_types["dev"]   # "t2.micro"
}

# Utilisation
resource "aws_instance" "web" {
  instance_type = var.instance_types[var.environment]
  tags          = var.tags
}
```

3. TYPES STRUCTURÉS
   ────────────────

```hcl
# ═══════════════════════════════════════════════════════════════════════
# OBJECT (structure avec champs typés)
# ═══════════════════════════════════════════════════════════════════════

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
  })
  
  default = {
    engine            = "postgres"
    version           = "15.4"
    instance_class    = "db.t3.micro"
    allocated_storage = 20
    multi_az          = false
    backup_retention  = 7
  }
}

# Accès aux champs
locals {
  db_engine  = var.database.engine            # "postgres"
  db_storage = var.database.allocated_storage # 20
}

# Utilisation
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
}


# ═══════════════════════════════════════════════════════════════════════
# TUPLE (liste avec types différents par position)
# ═══════════════════════════════════════════════════════════════════════

variable "server_config" {
  description = "Configuration serveur [name, port, enable_ssl]"
  type        = tuple([string, number, bool])
  default     = ["web-server", 8080, true]
}

locals {
  server_name = var.server_config[0]  # "web-server"
  server_port = var.server_config[1]  # 8080
  use_ssl     = var.server_config[2]  # true
}


# ═══════════════════════════════════════════════════════════════════════
# LIST OF OBJECTS (liste de structures - TRÈS UTILE!)
# ═══════════════════════════════════════════════════════════════════════

variable "security_rules" {
  description = "Règles de sécurité"
  type = list(object({
    description = string
    port        = number
    protocol    = string
    cidr_blocks = list(string)
  }))
  
  default = [
    {
      description = "HTTP"
      port        = 80
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      description = "HTTPS"
      port        = 443
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      description = "SSH"
      port        = 22
      protocol    = "tcp"
      cidr_blocks = ["10.0.0.0/8"]
    }
  ]
}

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

4. TYPE ANY (accepte n'importe quel type)
   ──────────────────────────────────────

```hcl
variable "custom_config" {
  description = "Configuration personnalisée"
  type        = any  # Accepte tout: string, number, list, map, object
  default     = {}
}

# [ATTENTION] À utiliser avec précaution!
# Perte de validation de type
```


┌───────────────────────────────────────────────────────────────────────────┐
│ FOURNIR DES VALEURS AUX VARIABLES                                        │
└───────────────────────────────────────────────────────────────────────────┘

ORDRE DE PRIORITÉ (du plus faible au plus fort):
─────────────────────────────────────────────────

```
1. default dans variables.tf           [Priorité la plus faible]
2. TF_VAR_* variables d'environnement
3. terraform.tfvars (fichier)
4. *.auto.tfvars (fichiers automatiques)
5. -var-file="custom.tfvars" (CLI)
6. -var="key=value" (CLI)               [Priorité la plus forte]
```

MÉTHODE 1: VALEUR PAR DÉFAUT
─────────────────────────────

```hcl
# variables.tf
variable "instance_type" {
  type    = string
  default = "t2.micro"  # Utilisé si rien d'autre fourni
}
```

MÉTHODE 2: VARIABLE D'ENVIRONNEMENT
────────────────────────────────────

```bash
# Format: TF_VAR_nom_variable
export TF_VAR_instance_type="t2.small"
export TF_VAR_db_password="secret123"

terraform apply
# instance_type = "t2.small" (écrase le default)
```

MÉTHODE 3: FICHIER terraform.tfvars
────────────────────────────────────

```hcl
# terraform.tfvars (chargé automatiquement)
instance_type = "t2.medium"
project_name  = "my-app"
environment   = "production"

tags = {
  Owner = "DevOps Team"
  Cost  = "Engineering"
}
```

MÉTHODE 4: FICHIER *.auto.tfvars
─────────────────────────────────

```hcl
# dev.auto.tfvars (chargé automatiquement)
environment   = "dev"
instance_type = "t2.micro"
```

MÉTHODE 5: FICHIER CUSTOM
──────────────────────────

```bash
# prod.tfvars
environment   = "prod"
instance_type = "t3.large"
multi_az      = true

# Charger avec -var-file
terraform apply -var-file="prod.tfvars"
```

MÉTHODE 6: LIGNE DE COMMANDE
─────────────────────────────

```bash
# -var a la priorité MAXIMALE
terraform apply \
  -var="instance_type=t2.large" \
  -var="project_name=override"
```

MÉTHODE 7: INTERACTION (si aucune valeur fournie)
──────────────────────────────────────────────────

```bash
terraform apply

# Si variable n'a pas de default, Terraform demande:
var.db_password
  Mot de passe de la base de données
  
  Enter a value:
```


(Suite dans le prochain message...)

# Fichier: python_cheats/cheatsheets/terraform_part2_variables_outputs.txt
# Terraform - PARTIE 2 (Fin): Variables, Validation, Outputs, Locals
# Guide Ultra-Détaillé


═══════════════════════════════════════════════════════════════════════════════
[OUTIL] SUITE PARTIE 2.4: VARIABLES - VALIDATION ET BEST PRACTICES
═══════════════════════════════════════════════════════════════════════════════

┌───────────────────────────────────────────────────────────────────────────┐
│ VALIDATION DES VARIABLES                                                  │
└───────────────────────────────────────────────────────────────────────────┘

POURQUOI VALIDER?
─────────────────

[OK] Détecter les erreurs AVANT terraform apply
[OK] Documenter les contraintes
[OK] Éviter les configurations invalides
[OK] Messages d'erreur clairs

SYNTAXE:
────────

```hcl
variable "example" {
  type = string
  
  validation {
    condition     = BOOLEAN_EXPRESSION
    error_message = "Message si condition = false"
  }
}
```


EXEMPLE 1: VALIDER UNE RÉGION AWS
──────────────────────────────────

```hcl
# variables.tf
variable "aws_region" {
  description = "Région AWS où déployer"
  type        = string
  
  validation {
    condition = contains([
      "us-east-1",
      "us-east-2",
      "us-west-1",
      "us-west-2",
      "eu-west-1",
      "eu-central-1"
    ], var.aws_region)
    
    error_message = <<-EOT
      Région invalide: ${var.aws_region}
      Régions autorisées: us-east-1, us-east-2, us-west-1, us-west-2, 
                         eu-west-1, eu-central-1
    EOT
  }
}

# Test avec une région invalide:
# terraform apply -var="aws_region=ap-south-1"
# 
# Error: Invalid value for variable
#   on variables.tf line 5:
#    5: variable "aws_region" {
# 
# Région invalide: ap-south-1
# Régions autorisées: us-east-1, us-east-2, us-west-1, us-west-2,
#                    eu-west-1, eu-central-1
```


EXEMPLE 2: VALIDER UN ENVIRONNEMENT
────────────────────────────────────

```hcl
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"
  }
}
```


EXEMPLE 3: VALIDER UN PORT RÉSEAU
──────────────────────────────────

```hcl
variable "application_port" {
  description = "Port de l'application"
  type        = number
  
  validation {
    # Port entre 1024 et 65535 (non-privilégiés)
    condition = (
      var.application_port >= 1024 &&
      var.application_port <= 65535
    )
    error_message = "Le port doit être entre 1024 et 65535"
  }
  
  validation {
    # Éviter les ports communs (sécurité)
    condition = !contains(
      [3306, 5432, 6379, 27017],  # MySQL, PostgreSQL, Redis, MongoDB
      var.application_port
    )
    error_message = "Ne pas utiliser les ports standards de base de données"
  }
}
```


EXEMPLE 4: VALIDER UN NOM (REGEX)
──────────────────────────────────

```hcl
variable "project_name" {
  description = "Nom du projet (alphanumeric + hyphens uniquement)"
  type        = string
  
  validation {
    # Regex: commence par lettre, puis lettres/chiffres/hyphens
    # Longueur: 3-20 caractères
    condition = can(regex("^[a-z][a-z0-9-]{2,19}$", var.project_name))
    
    error_message = <<-EOT
      Le nom du projet doit:
      - Commencer par une lettre minuscule
      - Contenir uniquement: lettres minuscules, chiffres, hyphens (-)
      - Avoir entre 3 et 20 caractères
      
      Exemples valides: my-app, project-123, web-app-v2
      Exemples invalides: MyApp, 123project, project_name
    EOT
  }
}
```


EXEMPLE 5: VALIDER UN CIDR
──────────────────────────

```hcl
variable "vpc_cidr" {
  description = "CIDR block du VPC"
  type        = string
  
  validation {
    # Vérifier format CIDR valide
    condition = can(cidrhost(var.vpc_cidr, 0))
    error_message = "CIDR invalide. Format attendu: X.X.X.X/Y (ex: 10.0.0.0/16)"
  }
  
  validation {
    # Vérifier range privé (RFC 1918)
    condition = can(regex(
      "^(10\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.|192\\.168\\.)",
      var.vpc_cidr
    ))
    error_message = "CIDR doit être dans les ranges privés (10.x, 172.16-31.x, 192.168.x)"
  }
}
```


EXEMPLE 6: VALIDATIONS MULTIPLES ET COMPLEXES
──────────────────────────────────────────────

```hcl
variable "instance_config" {
  description = "Configuration de l'instance"
  type = object({
    instance_type = string
    volume_size   = number
    enable_backup = bool
  })
  
  # Validation 1: Instance type valide
  validation {
    condition = contains(
      ["t2.micro", "t2.small", "t2.medium", "t3.micro", "t3.small"],
      var.instance_config.instance_type
    )
    error_message = "Instance type doit être un type t2 ou t3 éligible"
  }
  
  # Validation 2: Volume size raisonnable
  validation {
    condition = (
      var.instance_config.volume_size >= 8 &&
      var.instance_config.volume_size <= 100
    )
    error_message = "Volume size doit être entre 8 et 100 GB"
  }
  
  # Validation 3: Logique métier
  validation {
    condition = (
      !var.instance_config.enable_backup ||
      var.instance_config.volume_size >= 20
    )
    error_message = "Si backup activé, le volume doit être >= 20 GB"
  }
}
```


EXEMPLE 7: VALIDER UNE LISTE
─────────────────────────────

```hcl
variable "allowed_ips" {
  description = "Liste des IPs autorisées"
  type        = list(string)
  
  validation {
    # Au moins une IP
    condition     = length(var.allowed_ips) > 0
    error_message = "Au moins une IP doit être spécifiée"
  }
  
  validation {
    # Toutes les IPs valides
    condition = alltrue([
      for ip in var.allowed_ips :
      can(regex("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(/\\d{1,2})?$", ip))
    ])
    error_message = "Toutes les entrées doivent être des IPs valides (X.X.X.X ou X.X.X.X/Y)"
  }
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ VARIABLES SENSIBLES                                                       │
└───────────────────────────────────────────────────────────────────────────┘

POURQUOI?
─────────

Pour masquer les valeurs sensibles dans:
• Les logs Terraform
• Le plan Terraform
• Les outputs en console

[ATTENTION]  ATTENTION: La valeur est quand même dans le state!

```hcl
# variables.tf
variable "db_password" {
  description = "Mot de passe de la base de données"
  type        = string
  sensitive   = true  # <- MASQUE dans les logs
}

variable "api_key" {
  description = "Clé API"
  type        = string
  sensitive   = true
}

# Utilisation
resource "aws_db_instance" "main" {
  password = var.db_password
  # ... autres paramètres
}
```

COMPORTEMENT:
─────────────

```bash
# Sans sensitive = true:
terraform plan
# + password = "SuperSecret123!"  <- [ATTENTION] VISIBLE!

# Avec sensitive = true:
terraform plan
# + password = (sensitive value)  <- [OK] MASQUÉ
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ORGANISATION DES FICHIERS VARIABLES                                      │
└───────────────────────────────────────────────────────────────────────────┘

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

```
projet/
├── variables.tf              # Déclarations des variables
├── terraform.tfvars          # Valeurs par défaut (committable)
├── dev.tfvars               # Valeurs dev
├── staging.tfvars           # Valeurs staging
├── prod.tfvars              # Valeurs prod
├── secrets.auto.tfvars      # Secrets (NE PAS COMMITER!)
└── .gitignore               # Ignorer les secrets
```

EXEMPLE:
────────

```hcl
# ═══════════════════════════════════════════════════════════════════════
# variables.tf - DÉCLARATIONS
# ═══════════════════════════════════════════════════════════════════════

variable "environment" {
  description = "Environnement de déploiement"
  type        = string
}

variable "instance_type" {
  description = "Type d'instance EC2"
  type        = string
}

variable "db_password" {
  description = "Mot de passe base de données"
  type        = string
  sensitive   = true
}

variable "tags" {
  description = "Tags communs"
  type        = map(string)
  default     = {}
}


# ═══════════════════════════════════════════════════════════════════════
# terraform.tfvars - VALEURS PAR DÉFAUT
# ═══════════════════════════════════════════════════════════════════════

tags = {
  ManagedBy = "Terraform"
  Project   = "MyApp"
}


# ═══════════════════════════════════════════════════════════════════════
# dev.tfvars - ENVIRONNEMENT DEV
# ═══════════════════════════════════════════════════════════════════════

environment   = "dev"
instance_type = "t2.micro"


# ═══════════════════════════════════════════════════════════════════════
# prod.tfvars - ENVIRONNEMENT PROD
# ═══════════════════════════════════════════════════════════════════════

environment   = "prod"
instance_type = "t3.large"


# ═══════════════════════════════════════════════════════════════════════
# secrets.auto.tfvars - SECRETS (NE PAS COMMITER!)
# ═══════════════════════════════════════════════════════════════════════

db_password = "SuperSecret123!"
```

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

```bash
# Dev
terraform apply -var-file="dev.tfvars"

# Prod
terraform apply -var-file="prod.tfvars"

# secrets.auto.tfvars est chargé automatiquement (*.auto.tfvars)
```

.gitignore:
───────────

```gitignore
# Terraform
*.tfstate
*.tfstate.*
.terraform/
.terraform.lock.hcl

# Secrets
secrets.auto.tfvars
*.secret.tfvars
override.tf
override.tf.json
*_override.tf
*_override.tf.json
```


═══════════════════════════════════════════════════════════════════════════════
[SORTIE] PARTIE 2.5: OUTPUTS - AFFICHER DES INFORMATIONS
═══════════════════════════════════════════════════════════════════════════════

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

DÉFINITION:
───────────

Un OUTPUT permet d'AFFICHER des valeurs après l'application:
• Informations importantes (IPs, URLs, IDs)
• Transmettre des valeurs à d'autres modules
• Afficher dans la console

ANALOGIE:
─────────

```
OUTPUT = PANNEAU D'AFFICHAGE À LA FIN

Recette de cuisine:
┌────────────────────────────────────────┐
│ 1. Préparer les ingrédients            │  <- Ressources
│ 2. Mélanger                            │
│ 3. Cuire 30 min                        │
│                                        │
│ RÉSULTAT:                              │  <- Outputs
│ • Temps de cuisson: 30 min            │
│ • Portions: 4                          │
│ • Température: 180°C                   │
└────────────────────────────────────────┘
```

SYNTAXE:
────────

```hcl
output "OUTPUT_NAME" {
  description = "Description de l'output"
  value       = EXPRESSION
  sensitive   = true/false
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ EXEMPLES PRATIQUES D'OUTPUTS                                             │
└───────────────────────────────────────────────────────────────────────────┘

EXEMPLE 1: OUTPUTS SIMPLES
───────────────────────────

```hcl
# outputs.tf

# ═══════════════════════════════════════════════════════════════════════
# ID d'une instance
# ═══════════════════════════════════════════════════════════════════════

output "instance_id" {
  description = "ID de l'instance EC2"
  value       = aws_instance.web.id
}
# Output: instance_id = "i-1234567890abcdef0"


# ═══════════════════════════════════════════════════════════════════════
# IP publique
# ═══════════════════════════════════════════════════════════════════════

output "instance_public_ip" {
  description = "IP publique de l'instance"
  value       = aws_instance.web.public_ip
}
# Output: instance_public_ip = "54.123.45.67"


# ═══════════════════════════════════════════════════════════════════════
# URL d'accès
# ═══════════════════════════════════════════════════════════════════════

output "application_url" {
  description = "URL de l'application"
  value       = "http://${aws_instance.web.public_ip}:8080"
}
# Output: application_url = "http://54.123.45.67:8080"


# ═══════════════════════════════════════════════════════════════════════
# Multiple IDs avec count
# ═══════════════════════════════════════════════════════════════════════

output "instance_ids" {
  description = "Liste des IDs des instances"
  value       = aws_instance.web[*].id
  # Splat expression: récupère les IDs de toutes les instances
}
# Output: instance_ids = [
#   "i-111",
#   "i-222",
#   "i-333"
# ]
```


EXEMPLE 2: OUTPUT D'OBJET COMPLEXE
───────────────────────────────────

```hcl
# Regrouper plusieurs informations
output "instance_details" {
  description = "Détails complets de l'instance"
  value = {
    id               = aws_instance.web.id
    public_ip        = aws_instance.web.public_ip
    private_ip       = aws_instance.web.private_ip
    availability_zone = aws_instance.web.availability_zone
    instance_type    = aws_instance.web.instance_type
    ami              = aws_instance.web.ami
    state            = aws_instance.web.instance_state
  }
}

# Output:
# instance_details = {
#   id                = "i-1234567890abcdef0"
#   public_ip         = "54.123.45.67"
#   private_ip        = "10.0.1.10"
#   availability_zone = "us-east-1a"
#   instance_type     = "t2.micro"
#   ami               = "ami-0c55b159cbfafe1f0"
#   state             = "running"
# }
```


EXEMPLE 3: OUTPUT CONDITIONNEL
───────────────────────────────

```hcl
# Afficher URL HTTPS seulement si certificat présent
output "secure_url" {
  description = "URL sécurisée (si SSL activé)"
  value = var.enable_ssl ? (
    "https://${aws_instance.web.public_ip}"
  ) : (
    "SSL non activé"
  )
}
```


EXEMPLE 4: OUTPUT SENSIBLE
───────────────────────────

```hcl
# Masquer les valeurs sensibles dans les logs
output "db_password" {
  description = "Mot de passe de la base de données"
  value       = aws_db_instance.main.password
  sensitive   = true  # <- Masqué dans les logs
}

# Dans terraform apply:
# db_password = <sensitive>

# Pour voir la valeur:
# terraform output db_password
# ou
# terraform output -json | jq -r '.db_password.value'
```


EXEMPLE 5: OUTPUT AVEC FOR
───────────────────────────

```hcl
# Créer un map instance_name -> IP
output "instance_ips" {
  description = "Map des IPs par nom d'instance"
  value = {
    for instance in aws_instance.web :
    instance.tags["Name"] => instance.public_ip
  }
}

# Output:
# instance_ips = {
#   "web-server-1" = "54.123.45.67"
#   "web-server-2" = "54.123.45.68"
#   "web-server-3" = "54.123.45.69"
# }
```


EXEMPLE 6: OUTPUT FORMATÉ
──────────────────────────

```hcl
# Connection string pour base de données
output "database_connection_string" {
  description = "Connection string PostgreSQL"
  value = format(
    "postgresql://%s:%s@%s:%d/%s",
    aws_db_instance.main.username,
    "****",  # Masquer le password
    aws_db_instance.main.endpoint,
    aws_db_instance.main.port,
    aws_db_instance.main.db_name
  )
  sensitive = true
}

# Output:
# database_connection_string = "postgresql://admin:****@mydb.123.us-east-1.rds.amazonaws.com:5432/myapp"
```


┌───────────────────────────────────────────────────────────────────────────┐
│ UTILISER LES OUTPUTS                                                      │
└───────────────────────────────────────────────────────────────────────────┘

COMMANDES:
──────────

```bash
# 1. Voir tous les outputs
terraform output

# Output:
# instance_id = "i-1234567890abcdef0"
# instance_public_ip = "54.123.45.67"
# application_url = "http://54.123.45.67:8080"


# 2. Voir un output spécifique
terraform output instance_id
# i-1234567890abcdef0


# 3. Format JSON (pour scripts)
terraform output -json

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


# 4. Extraire une valeur avec jq
terraform output -json | jq -r '.instance_public_ip.value'
# 54.123.45.67


# 5. Utiliser dans un script bash
IP=$(terraform output -raw instance_public_ip)
echo "Connexion à l'instance: ssh ubuntu@$IP"
# Connexion à l'instance: ssh ubuntu@54.123.45.67
```


CAS D'USAGE: CHAÎNER AVEC D'AUTRES OUTILS
──────────────────────────────────────────

```bash
#!/bin/bash
# deploy.sh

# 1. Déployer l'infrastructure
terraform apply -auto-approve

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

# 3. Attendre que le serveur soit prêt
echo "Attente du serveur..."
until ssh -o ConnectTimeout=2 ubuntu@$SERVER_IP "echo 'Ready'" 2>/dev/null; do
  sleep 5
done

# 4. Déployer l'application
echo "Déploiement de l'application..."
scp app.zip ubuntu@$SERVER_IP:/tmp/
ssh ubuntu@$SERVER_IP "cd /tmp && unzip -o app.zip && sudo systemctl restart myapp"

# 5. Afficher l'URL
APP_URL=$(terraform output -raw application_url)
echo "[OK] Application déployée: $APP_URL"
```


═══════════════════════════════════════════════════════════════════════════════
[OUTIL] PARTIE 2.6: LOCALS - VALEURS CALCULÉES
═══════════════════════════════════════════════════════════════════════════════

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

DÉFINITION:
───────────

Un LOCAL est une valeur calculée et réutilisable DANS le module.
Contrairement aux variables (inputs) et outputs, les locals sont internes.

ANALOGIE:
─────────

```
PROGRAMME DE CUISINE:

Variables (inputs):     Locals:              Outputs:
┌──────────────┐       ┌──────────────┐     ┌──────────────┐
│ • Farine     │  ───-> │ Pâte =       │ ──-> │ • Gâteau     │
│ • Oeufs      │       │   farine +   │     │ • Portions:4 │
│ • Sucre      │       │   oeufs +    │     └──────────────┘
└──────────────┘       │   sucre      │
                       │              │
                       │ Temps =      │
                       │   30 min +   │
                       │   préparation│
                       └──────────────┘

Locals = calculs intermédiaires, pas exposés à l'extérieur
```

DIFFÉRENCE VARIABLES vs LOCALS:
────────────────────────────────

```
VARIABLES                      LOCALS
────────────────────────────────────────────────────────
• Définies de l'EXTÉRIEUR     • Calculées en INTERNE
• Peuvent changer             • Valeur fixe (dans run)
• Inputs du module            • Helpers internes
• Visibles de l'extérieur     • Privées au module

Exemple:
var.environment = "prod"      local.instance_name = "${var.environment}-web"
(fourni par user)             (calculé automatiquement)
```

SYNTAXE:
────────

```hcl
locals {
  # Définir plusieurs valeurs locales
  local_name_1 = value_or_expression
  local_name_2 = value_or_expression
  local_name_3 = value_or_expression
}

# Utilisation
resource "..." "..." {
  argument = local.local_name_1
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ EXEMPLES PRATIQUES DE LOCALS                                             │
└───────────────────────────────────────────────────────────────────────────┘

EXEMPLE 1: CONSTRUIRE DES NOMS
───────────────────────────────

```hcl
# variables.tf
variable "environment" {
  type = string
}

variable "project_name" {
  type = string
}

# main.tf
locals {
  # Construire un préfixe commun
  name_prefix = "${var.project_name}-${var.environment}"
  
  # Noms de ressources
  instance_name = "${local.name_prefix}-web"
  db_name       = "${local.name_prefix}-db"
  bucket_name   = "${local.name_prefix}-assets-${data.aws_caller_identity.current.account_id}"
}

resource "aws_instance" "web" {
  # ...
  tags = {
    Name = local.instance_name  # "myapp-prod-web"
  }
}

resource "aws_db_instance" "main" {
  identifier = local.db_name  # "myapp-prod-db"
  # ...
}

resource "aws_s3_bucket" "assets" {
  bucket = local.bucket_name  # "myapp-prod-assets-123456789012"
  # ...
}
```


EXEMPLE 2: TAGS COMMUNS (DRY)
──────────────────────────────

```hcl
# Ne pas répéter les tags partout!

locals {
  # Tags communs à TOUTES les ressources
  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "Terraform"
    Owner       = var.team_name
    CostCenter  = var.cost_center
    CreatedAt   = timestamp()
  }
}

# Utilisation: merge avec tags spécifiques
resource "aws_instance" "web" {
  # ...
  tags = merge(
    local.common_tags,
    {
      Name = "web-server"
      Role = "webserver"
    }
  )
}

resource "aws_db_instance" "main" {
  # ...
  tags = merge(
    local.common_tags,
    {
      Name = "database"
      Role = "database"
    }
  )
}

# Résultat pour l'instance:
# {
#   Project     = "myapp"
#   Environment = "prod"
#   ManagedBy   = "Terraform"
#   Owner       = "backend-team"
#   CostCenter  = "engineering"
#   CreatedAt   = "2024-01-15T14:30:00Z"
#   Name        = "web-server"
#   Role        = "webserver"
# }
```


EXEMPLE 3: CONFIGURATION SELON ENVIRONNEMENT
─────────────────────────────────────────────

```hcl
variable "environment" {
  type = string
}

locals {
  # Configuration qui change selon l'environnement
  is_production = var.environment == "prod"
  
  instance_type = local.is_production ? "t3.large" : "t2.micro"
  instance_count = local.is_production ? 3 : 1
  enable_backup  = local.is_production ? true : false
  enable_monitoring = local.is_production ? true : false
  
  # Configuration DB
  db_config = {
    instance_class = local.is_production ? "db.m5.large" : "db.t3.micro"
    multi_az       = local.is_production ? true : false
    backup_retention = local.is_production ? 30 : 7
    storage_type   = local.is_production ? "io1" : "gp2"
  }
}

resource "aws_instance" "web" {
  count         = local.instance_count      # 3 en prod, 1 en dev
  instance_type = local.instance_type       # t3.large en prod, t2.micro en dev
  monitoring    = local.enable_monitoring   # true en prod, false en dev
  # ...
}

resource "aws_db_instance" "main" {
  instance_class = local.db_config.instance_class
  multi_az       = local.db_config.multi_az
  # ...
}
```


EXEMPLE 4: CALCULS COMPLEXES
─────────────────────────────

```hcl
locals {
  # Calculer le CIDR pour chaque subnet
  # VPC = 10.0.0.0/16, on veut 3 subnets /24
  vpc_cidr = "10.0.0.0/16"
  
  subnet_cidrs = [
    cidrsubnet(local.vpc_cidr, 8, 0),  # 10.0.0.0/24
    cidrsubnet(local.vpc_cidr, 8, 1),  # 10.0.1.0/24
    cidrsubnet(local.vpc_cidr, 8, 2),  # 10.0.2.0/24
  ]
  
  # Calculer nombre total d'IPs disponibles
  total_ips = sum([
    for cidr in local.subnet_cidrs :
    pow(2, 32 - tonumber(split("/", cidr)[1])) - 5  # -5 pour IPs réservées AWS
  ])
}
```


EXEMPLE 5: FLATTEN DE STRUCTURES
─────────────────────────────────

```hcl
variable "security_rules" {
  type = list(object({
    description = string
    ports       = list(number)
    cidr_blocks = list(string)
  }))
  
  default = [
    {
      description = "Web"
      ports       = [80, 443]
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      description = "SSH"
      ports       = [22]
      cidr_blocks = ["10.0.0.0/8", "172.16.0.0/12"]
    }
  ]
}

locals {
  # Flatten: transformer en liste de règles individuelles
  # (une par combinaison port + cidr)
  flattened_rules = flatten([
    for rule in var.security_rules : [
      for port in rule.ports : [
        for cidr in rule.cidr_blocks : {
          description = rule.description
          port        = port
          cidr_block  = cidr
        }
      ]
    ]
  ])
}

# Résultat:
# [
#   { description = "Web", port = 80,  cidr_block = "0.0.0.0/0" },
#   { description = "Web", port = 443, cidr_block = "0.0.0.0/0" },
#   { description = "SSH", port = 22,  cidr_block = "10.0.0.0/8" },
#   { description = "SSH", port = 22,  cidr_block = "172.16.0.0/12" },
# ]

# Utilisation
resource "aws_security_group_rule" "ingress" {
  for_each = { for idx, rule in local.flattened_rules : idx => rule }
  
  type        = "ingress"
  description = each.value.description
  from_port   = each.value.port
  to_port     = each.value.port
  protocol    = "tcp"
  cidr_blocks = [each.value.cidr_block]
  # ...
}
```


EXEMPLE 6: RÉFÉRENCER D'AUTRES LOCALS
──────────────────────────────────────

```hcl
locals {
  # Base
  environment   = var.environment
  project_name  = var.project_name
  
  # Construits à partir des précédents
  name_prefix   = "${local.project_name}-${local.environment}"
  
  # Encore plus complexe
  full_instance_name = "${local.name_prefix}-web-${random_id.suffix.hex}"
  
  # Configuration complète
  instance_config = {
    name          = local.full_instance_name
    tags          = local.common_tags
    instance_type = local.instance_type_mapping[local.environment]
  }
  
  instance_type_mapping = {
    dev     = "t2.micro"
    staging = "t2.small"
    prod    = "t3.large"
  }
  
  common_tags = {
    Project     = local.project_name
    Environment = local.environment
    Name        = local.name_prefix
  }
}
```


EXEMPLE 7: LOCALS POUR LISIBILITÉ
──────────────────────────────────

```hcl
# [X] SANS LOCALS (illisible)
resource "aws_instance" "web" {
  ami = var.environment == "prod" ? (
    var.region == "us-east-1" ? "ami-prod-us-east-1" : (
      var.region == "eu-west-1" ? "ami-prod-eu-west-1" : "ami-prod-default"
    )
  ) : (
    var.region == "us-east-1" ? "ami-dev-us-east-1" : "ami-dev-default"
  )
}

# [OK] AVEC LOCALS (clair)
locals {
  ami_mapping = {
    "prod-us-east-1" = "ami-prod-us-east-1"
    "prod-eu-west-1" = "ami-prod-eu-west-1"
    "prod-default"   = "ami-prod-default"
    "dev-us-east-1"  = "ami-dev-us-east-1"
    "dev-default"    = "ami-dev-default"
  }
  
  ami_key = "${var.environment}-${var.region}"
  selected_ami = lookup(
    local.ami_mapping,
    local.ami_key,
    local.ami_mapping["${var.environment}-default"]
  )
}

resource "aws_instance" "web" {
  ami = local.selected_ami  # Clair et simple!
}
```


BEST PRACTICES LOCALS:
──────────────────────

[OK] **Utiliser pour DRY (Don't Repeat Yourself)**
[OK] **Simplifier les expressions complexes**
[OK] **Calculer des valeurs dérivées**
[OK] **Améliorer la lisibilité**
[OK] **Centraliser la logique métier**

[X] **Ne pas abuser** (trop de locals = confusion)
[X] **Ne pas réimplémenter des variables** (utiliser variables si c'est un input)


(Suite avec la Partie 2.7 Modules dans le prochain fichier...)

# Fichier: python_cheats/cheatsheets/terraform_part2_modules.txt
# Terraform - PARTIE 2.7: MODULES
# Guide Ultra-Détaillé - Réutilisabilité et Organisation


═══════════════════════════════════════════════════════════════════════════════
[PACKAGE] PARTIE 2.7: MODULES - COMPOSANTS RÉUTILISABLES
═══════════════════════════════════════════════════════════════════════════════

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

DÉFINITION:
───────────

Un MODULE est un ensemble de fichiers .tf regroupés dans un dossier,
qui peut être réutilisé comme un composant.

TOUT code Terraform est un module:
• Votre dossier de travail = "root module"
• Dossiers appelés = "child modules"

ANALOGIE:
─────────

```
MODULE = FONCTION EN PROGRAMMATION

Sans modules (répétition):              Avec modules (réutilisation):
┌────────────────────────────┐         ┌────────────────────────────┐
│ Créer EC2 + SG + EIP       │         │ module "web_server" {      │
│   (100 lignes de code)     │         │   source = "./modules/web" │
│                            │         │   name   = "web-1"         │
│ Créer EC2 + SG + EIP       │         │ }                          │
│   (100 lignes RÉPÉTÉES)    │         │                            │
│                            │         │ module "web_server_2" {    │
│ Créer EC2 + SG + EIP       │         │   source = "./modules/web" │
│   (100 lignes ENCORE!)     │         │   name   = "web-2"         │
│                            │         │ }                          │
│ = 300 lignes               │         │ = 20 lignes                │
└────────────────────────────┘         └────────────────────────────┘

DRY: Don't Repeat Yourself
```


POURQUOI DES MODULES?
─────────────────────

1. **RÉUTILISABILITÉ**
   ```
   Créer un module VPC une fois
   -> Réutiliser dans 10 projets
   -> Économie de temps énorme
   ```

2. **MAINTENABILITÉ**
   ```
   Bug trouvé dans la config VPC?
   -> Corriger UNE fois dans le module
   -> TOUS les projets en bénéficient
   ```

3. **ABSTRACTION**
   ```
   Cache la complexité
   Module "database":
   • Input: type, size, name
   • Output: connection_string
   • Cache: 50 lignes de config complexe
   ```

4. **STANDARDISATION**
   ```
   Équipe de 10 devs
   -> Tous utilisent les MÊMES modules
   -> Infrastructure homogène
   -> Best practices appliquées partout
   ```

5. **ORGANISATION**
   ```
   Projet complexe (200 ressources)
   -> modules/
       ├── networking/
       ├── compute/
       ├── database/
       └── monitoring/
   -> Clair et structuré
   ```


╔═══════════════════════════════════════════════════════════════════════════╗
║ STRUCTURE D'UN MODULE                                                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

STRUCTURE MINIMALE:
───────────────────

```
mon-module/
├── main.tf           # Ressources principales
├── variables.tf      # Inputs du module
├── outputs.tf        # Outputs du module
└── README.md         # Documentation
```

STRUCTURE COMPLÈTE:
───────────────────

```
mon-module/
├── main.tf           # Ressources principales
├── variables.tf      # Variables d'entrée
├── outputs.tf        # Valeurs de sortie
├── versions.tf       # Versions Terraform et providers
├── README.md         # Documentation complète
├── examples/         # Exemples d'utilisation
│   ├── basic/
│   │   ├── main.tf
│   │   └── variables.tf
│   └── advanced/
│       ├── main.tf
│       └── variables.tf
├── tests/            # Tests automatisés
│   └── module_test.go
└── CHANGELOG.md      # Historique des versions
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ CRÉER UN MODULE - EXEMPLE COMPLET                                         ║
╚═══════════════════════════════════════════════════════════════════════════╝

SCÉNARIO: Module pour créer un serveur web EC2

┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 1: CRÉER LA STRUCTURE                                              │
└───────────────────────────────────────────────────────────────────────────┘

```bash
mkdir -p modules/web-server
cd modules/web-server
touch main.tf variables.tf outputs.tf versions.tf README.md
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 2: DÉFINIR LES VARIABLES (INPUTS)                                  │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# modules/web-server/variables.tf
# ═══════════════════════════════════════════════════════════════════════

variable "name" {
  description = "Nom du serveur web"
  type        = string
}

variable "instance_type" {
  description = "Type d'instance EC2"
  type        = string
  default     = "t2.micro"
}

variable "vpc_id" {
  description = "ID du VPC où créer le serveur"
  type        = string
}

variable "subnet_id" {
  description = "ID du subnet où créer le serveur"
  type        = string
}

variable "allowed_cidr_blocks" {
  description = "CIDRs autorisés à accéder au serveur web"
  type        = list(string)
  default     = ["0.0.0.0/0"]
}

variable "ssh_key_name" {
  description = "Nom de la clé SSH pour connexion"
  type        = string
  default     = null
}

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

variable "tags" {
  description = "Tags à appliquer aux ressources"
  type        = map(string)
  default     = {}
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 3: CRÉER LES RESSOURCES                                            │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# modules/web-server/main.tf
# ═══════════════════════════════════════════════════════════════════════

# ┌─────────────────────────────────────────────────────────────────────┐
# │ DATA SOURCES                                                         │
# └─────────────────────────────────────────────────────────────────────┘

# Trouver la dernière AMI Ubuntu
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"]
  }
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ LOCALS                                                               │
# └─────────────────────────────────────────────────────────────────────┘

locals {
  # Tags communs
  common_tags = merge(
    var.tags,
    {
      Name      = var.name
      ManagedBy = "Terraform"
      Module    = "web-server"
    }
  )
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ SECURITY GROUP                                                       │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_security_group" "web" {
  name        = "${var.name}-sg"
  description = "Security group for ${var.name}"
  vpc_id      = var.vpc_id

  # HTTP
  ingress {
    description = "HTTP"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = var.allowed_cidr_blocks
  }

  # HTTPS
  ingress {
    description = "HTTPS"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = var.allowed_cidr_blocks
  }

  # SSH (si clé SSH fournie)
  dynamic "ingress" {
    for_each = var.ssh_key_name != null ? [1] : []
    content {
      description = "SSH"
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
      cidr_blocks = var.allowed_cidr_blocks
    }
  }

  # Tout le trafic sortant
  egress {
    description = "All outbound"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = merge(
    local.common_tags,
    {
      Name = "${var.name}-sg"
    }
  )
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ EC2 INSTANCE                                                         │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_instance" "web" {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = var.instance_type
  subnet_id              = var.subnet_id
  vpc_security_group_ids = [aws_security_group.web.id]
  key_name               = var.ssh_key_name
  monitoring             = var.enable_monitoring

  # User data: Installer Nginx au démarrage
  user_data = <<-EOF
    #!/bin/bash
    apt-get update
    apt-get install -y nginx
    
    # Page HTML personnalisée
    cat > /var/www/html/index.html <<HTML
    <!DOCTYPE html>
    <html>
    <head>
      <title>${var.name}</title>
      <style>
        body {
          font-family: Arial, sans-serif;
          text-align: center;
          padding: 50px;
          background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
          color: white;
        }
        h1 { font-size: 3em; }
      </style>
    </head>
    <body>
      <h1>[RAPIDE] ${var.name}</h1>
      <p>Server is running!</p>
      <p>Managed by Terraform Module</p>
    </body>
    </html>
    HTML
    
    systemctl start nginx
    systemctl enable nginx
  EOF

  # Disk configuration
  root_block_device {
    volume_type           = "gp3"
    volume_size           = 20
    delete_on_termination = true
    encrypted             = true
  }

  tags = local.common_tags

  lifecycle {
    create_before_destroy = true
  }
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ ELASTIC IP (pour IP publique fixe)                                  │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_eip" "web" {
  instance = aws_instance.web.id
  domain   = "vpc"

  tags = merge(
    local.common_tags,
    {
      Name = "${var.name}-eip"
    }
  )

  depends_on = [aws_instance.web]
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 4: DÉFINIR LES OUTPUTS                                             │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# modules/web-server/outputs.tf
# ═══════════════════════════════════════════════════════════════════════

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

output "public_ip" {
  description = "IP publique du serveur"
  value       = aws_eip.web.public_ip
}

output "private_ip" {
  description = "IP privée du serveur"
  value       = aws_instance.web.private_ip
}

output "security_group_id" {
  description = "ID du security group"
  value       = aws_security_group.web.id
}

output "url" {
  description = "URL pour accéder au serveur"
  value       = "http://${aws_eip.web.public_ip}"
}

output "ssh_command" {
  description = "Commande SSH pour se connecter"
  value       = var.ssh_key_name != null ? "ssh ubuntu@${aws_eip.web.public_ip}" : "SSH key not configured"
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 5: VERSIONS                                                         │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# modules/web-server/versions.tf
# ═══════════════════════════════════════════════════════════════════════

terraform {
  required_version = ">= 1.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.0"
    }
  }
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 6: DOCUMENTATION                                                    │
└───────────────────────────────────────────────────────────────────────────┘

```markdown
# modules/web-server/README.md

# Web Server Module

Module Terraform pour créer un serveur web EC2 avec Nginx pré-installé.

## Features

- EC2 instance avec Ubuntu 22.04 LTS
- Nginx pré-installé et configuré
- Security Group avec ports HTTP/HTTPS ouverts
- Elastic IP pour IP publique fixe
- Support SSH optionnel
- Monitoring optionnel

## Usage

```hcl
module "web_server" {
  source = "./modules/web-server"

  name       = "my-web-server"
  vpc_id     = "vpc-123456"
  subnet_id  = "subnet-789012"
  
  instance_type = "t2.micro"
  ssh_key_name  = "my-ssh-key"
  
  allowed_cidr_blocks = ["0.0.0.0/0"]
  
  tags = {
    Environment = "production"
    Project     = "myapp"
  }
}
```

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|----------|
| name | Nom du serveur | string | - | yes |
| vpc_id | ID du VPC | string | - | yes |
| subnet_id | ID du subnet | string | - | yes |
| instance_type | Type d'instance | string | "t2.micro" | no |
| ssh_key_name | Clé SSH | string | null | no |
| allowed_cidr_blocks | CIDRs autorisés | list(string) | ["0.0.0.0/0"] | no |
| enable_monitoring | Monitoring CloudWatch | bool | false | no |
| tags | Tags additionnels | map(string) | {} | no |

## Outputs

| Name | Description |
|------|-------------|
| instance_id | ID de l'instance EC2 |
| public_ip | IP publique |
| private_ip | IP privée |
| security_group_id | ID du security group |
| url | URL du serveur |
| ssh_command | Commande SSH |

## Examples

Voir le dossier `examples/` pour des exemples complets.
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ UTILISER LE MODULE                                                        ║
╚═══════════════════════════════════════════════════════════════════════════╝

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

```
mon-projet/
├── modules/
│   └── web-server/         # Notre module
│       ├── main.tf
│       ├── variables.tf
│       ├── outputs.tf
│       ├── versions.tf
│       └── README.md
├── main.tf                 # Root module
├── variables.tf
├── outputs.tf
└── terraform.tfvars
```


┌───────────────────────────────────────────────────────────────────────────┐
│ UTILISATION SIMPLE                                                        │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# main.tf (root module)
# ═══════════════════════════════════════════════════════════════════════

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

provider "aws" {
  region = "us-east-1"
}

# ┌─────────────────────────────────────────────────────────────────────┐
# │ Créer un VPC et subnet (simplifié)                                  │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  
  tags = {
    Name = "main-vpc"
  }
}

resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  map_public_ip_on_launch = true
  
  tags = {
    Name = "public-subnet"
  }
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }
}

resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ UTILISER LE MODULE WEB-SERVER                                       │
# └─────────────────────────────────────────────────────────────────────┘

module "web_server_1" {
  source = "./modules/web-server"
  
  # Inputs obligatoires
  name      = "web-server-1"
  vpc_id    = aws_vpc.main.id
  subnet_id = aws_subnet.public.id
  
  # Inputs optionnels
  instance_type       = "t2.micro"
  ssh_key_name        = "my-ssh-key"
  allowed_cidr_blocks = ["0.0.0.0/0"]
  enable_monitoring   = false
  
  tags = {
    Environment = "production"
    Project     = "myapp"
  }
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ CRÉER UN DEUXIÈME SERVEUR (réutilisation facile!)                   │
# └─────────────────────────────────────────────────────────────────────┘

module "web_server_2" {
  source = "./modules/web-server"
  
  name      = "web-server-2"
  vpc_id    = aws_vpc.main.id
  subnet_id = aws_subnet.public.id
  
  instance_type = "t2.small"  # Plus gros
  
  tags = {
    Environment = "production"
    Project     = "myapp"
  }
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ACCÉDER AUX OUTPUTS DU MODULE                                            │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# outputs.tf (root module)
# ═══════════════════════════════════════════════════════════════════════

output "server_1_url" {
  description = "URL du serveur 1"
  value       = module.web_server_1.url
  #             └────┬──────────┘ └─┬─┘
  #                  │             └─ Output du module
  #                  └─ Nom du module appelé
}

output "server_1_ip" {
  description = "IP publique du serveur 1"
  value       = module.web_server_1.public_ip
}

output "server_2_url" {
  description = "URL du serveur 2"
  value       = module.web_server_2.url
}

output "all_servers" {
  description = "Informations de tous les serveurs"
  value = {
    server_1 = {
      id         = module.web_server_1.instance_id
      ip         = module.web_server_1.public_ip
      url        = module.web_server_1.url
      ssh        = module.web_server_1.ssh_command
    }
    server_2 = {
      id         = module.web_server_2.instance_id
      ip         = module.web_server_2.public_ip
      url        = module.web_server_2.url
      ssh        = module.web_server_2.ssh_command
    }
  }
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ DÉPLOYER                                                                  │
└───────────────────────────────────────────────────────────────────────────┘

```bash
# 1. Initialiser (télécharge providers + modules)
terraform init

# Output:
# Initializing modules...
# - web_server_1 in modules/web-server
# - web_server_2 in modules/web-server
# 
# Initializing provider plugins...
# - Finding hashicorp/aws versions matching "~> 5.0"...
# ...

# 2. Planifier
terraform plan

# 3. Appliquer
terraform apply

# Outputs affichés:
# server_1_url = "http://54.123.45.67"
# server_1_ip = "54.123.45.67"
# server_2_url = "http://54.123.45.68"
# all_servers = {
#   server_1 = {
#     id  = "i-123..."
#     ip  = "54.123.45.67"
#     url = "http://54.123.45.67"
#     ssh = "ssh ubuntu@54.123.45.67"
#   }
#   server_2 = {
#     ...
#   }
# }

# 4. Tester
curl $(terraform output -raw server_1_url)
# Affiche la page HTML du serveur!
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ SOURCES DE MODULES                                                        ║
╚═══════════════════════════════════════════════════════════════════════════╝

Un module peut venir de différentes sources:

1. **LOCAL (chemin relatif)**
   ```hcl
   module "example" {
     source = "./modules/my-module"
   }
   ```

2. **GIT (repository)**
   ```hcl
   module "example" {
     source = "git::https://github.com/user/repo.git"
   }
   
   # Avec branche spécifique
   module "example" {
     source = "git::https://github.com/user/repo.git?ref=v1.0.0"
   }
   
   # Avec sous-dossier
   module "example" {
     source = "git::https://github.com/user/repo.git//modules/vpc"
   }
   ```

3. **TERRAFORM REGISTRY (officiel)**
   ```hcl
   module "vpc" {
     source  = "terraform-aws-modules/vpc/aws"
     version = "5.1.0"
   }
   ```

4. **HTTP (URL directe)**
   ```hcl
   module "example" {
     source = "https://example.com/modules/my-module.zip"
   }
   ```

5. **S3 (bucket AWS)**
   ```hcl
   module "example" {
     source = "s3::https://s3-eu-west-1.amazonaws.com/my-bucket/modules/vpc.zip"
   }
   ```

# Fichier: python_cheats/cheatsheets/terraform_part3_state.txt
# Terraform - PARTIE 3: STATE MANAGEMENT
# Guide Ultra-Détaillé - Le Cerveau de Terraform


═══════════════════════════════════════════════════════════════════════════════
[ARCHIVE] PARTIE 3: STATE MANAGEMENT - COMPRENDRE ET MAÎTRISER
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ 3.1 LE STATE - CONCEPT FONDAMENTAL                                        ║
╚═══════════════════════════════════════════════════════════════════════════╝

DÉFINITION:
───────────

Le STATE (terraform.tfstate) est un fichier JSON qui contient:
• L'inventaire complet de votre infrastructure
• Les attributs de chaque ressource créée
• Les dépendances entre ressources
• Les métadonnées de configuration

ANALOGIE:
─────────

```
LE STATE = REGISTRE/INVENTAIRE D'UN MAGASIN

Sans inventaire:
┌─────────────────────────────────────────────────────────────────┐
│ "Combien de produits avons-nous?"                               │
│ -> Aucune idée, il faut tout compter manuellement                │
│                                                                  │
│ "Qui a acheté quoi?"                                            │
│ -> Impossible à savoir                                           │
│                                                                  │
│ "Qu'est-ce qui a changé depuis hier?"                           │
│ -> Faut tout re-vérifier                                         │
└─────────────────────────────────────────────────────────────────┘

Avec inventaire (State):
┌─────────────────────────────────────────────────────────────────┐
│ "Combien de produits?"                                          │
│ -> Consulte l'inventaire: 1,234 produits                         │
│                                                                  │
│ "Qui a acheté quoi?"                                            │
│ -> Historique complet dans la base                               │
│                                                                  │
│ "Changements depuis hier?"                                      │
│ -> Compare inventaire d'hier vs aujourd'hui                      │
└─────────────────────────────────────────────────────────────────┘
```


POURQUOI LE STATE EST CRITIQUE?
────────────────────────────────

1. **MAPPING CODE <-> INFRASTRUCTURE RÉELLE**
   
   ```
   Code Terraform          State              Cloud réel
   ┌──────────────┐       ┌──────────────┐   ┌──────────────┐
   │ resource     │       │ "instances": │   │  Instance    │
   │ "aws_instance│  <-──-> │   [{         │<-─->│  EC2 running │
   │  .web" {     │       │    "id":     │   │  i-123abc    │
   │  ...         │       │    "i-123abc"│   │              │
   │ }            │       │   }]         │   │              │
   └──────────────┘       └──────────────┘   └──────────────┘
   
   Le state CONNECTE le code à la réalité!
   ```

2. **PERFORMANCE**
   
   Sans state:
   ```
   terraform plan
   -> Appeler AWS API pour CHAQUE ressource
   -> Liste des instances (API call)
   -> Détails de l'instance 1 (API call)
   -> Détails de l'instance 2 (API call)
   -> ... × 100 ressources
   -> Très lent! (minutes)
   ```
   
   Avec state:
   ```
   terraform plan
   -> Lire terraform.tfstate (local, instantané)
   -> Comparer code vs state
   -> Appeler API SEULEMENT pour vérifier les changements
   -> Rapide! (secondes)
   ```

3. **DÉPENDANCES**
   
   ```hcl
   resource "aws_vpc" "main" { ... }
   resource "aws_subnet" "public" {
     vpc_id = aws_vpc.main.id  # Dépendance!
   }
   ```
   
   Le state stocke l'ordre de création:
   ```json
   {
     "resources": [
       {
         "type": "aws_vpc",
         "name": "main",
         "created_at": "2024-01-15T10:00:00Z"
       },
       {
         "type": "aws_subnet",
         "name": "public",
         "depends_on": ["aws_vpc.main"],
         "created_at": "2024-01-15T10:00:05Z"
       }
     ]
   }
   ```

4. **TRACKING DES CHANGEMENTS**
   
   Terraform compare:
   ```
   Desired State     vs     Current State     =     Actions
   (Code .tf)               (State file)            (Plan)
   
   instance_type           instance_type           -> Update
   = "t2.small"            = "t2.micro"               instance_type
   
   ami = "ami-new"         ami = "ami-old"         -> Replace
                                                       instance
   ```


STRUCTURE DU STATE:
───────────────────

```json
{
  "version": 4,
  "terraform_version": "1.7.0",
  "serial": 3,
  "lineage": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  
  "resources": [
    {
      "mode": "managed",
      "type": "aws_instance",
      "name": "web",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            "id": "i-1234567890abcdef0",
            "ami": "ami-0c55b159cbfafe1f0",
            "instance_type": "t2.micro",
            "public_ip": "54.123.45.67",
            "private_ip": "10.0.1.10",
            "availability_zone": "us-east-1a",
            "tags": {
              "Name": "web-server"
            }
          },
          "dependencies": [
            "aws_security_group.web",
            "aws_subnet.public"
          ]
        }
      ]
    }
  ],
  
  "outputs": {
    "instance_id": {
      "value": "i-1234567890abcdef0",
      "type": "string"
    }
  },
  
  "check_results": null
}
```

CHAMPS IMPORTANTS:
──────────────────

• **version**: Version du format du state (actuellement 4)
• **terraform_version**: Version de Terraform ayant créé le state
• **serial**: Numéro incrémental (détecte les modifications concurrentes)
• **lineage**: UUID unique (détecte les divergences de state)
• **resources**: Liste de toutes les ressources gérées
• **attributes**: Tous les détails de chaque ressource
• **dependencies**: Graphe des dépendances


[ATTENTION]  RÈGLES D'OR DU STATE:
─────────────────────────

```
╔═══════════════════════════════════════════════════════════════════╗
║ 1. NE JAMAIS ÉDITER LE STATE À LA MAIN!                          ║
║    -> Risque de corruption                                         ║
║    -> Utiliser: terraform state mv/rm/import                       ║
╚═══════════════════════════════════════════════════════════════════╝

╔═══════════════════════════════════════════════════════════════════╗
║ 2. NE JAMAIS COMMITER LE STATE DANS GIT!                         ║
║    -> Contient des secrets en clair                                ║
║    -> Conflits git = corruption du state                           ║
║    -> Ajouter à .gitignore                                         ║
╚═══════════════════════════════════════════════════════════════════╝

╔═══════════════════════════════════════════════════════════════════╗
║ 3. TOUJOURS UTILISER UN BACKEND DISTANT EN ÉQUIPE/PROD!          ║
║    -> S3 + DynamoDB (AWS)                                          ║
║    -> Terraform Cloud                                              ║
║    -> Google Cloud Storage                                         ║
╚═══════════════════════════════════════════════════════════════════╝

╔═══════════════════════════════════════════════════════════════════╗
║ 4. ACTIVER LE VERSIONING DU BACKEND!                             ║
║    -> Backup automatique en cas d'erreur                           ║
║    -> Rollback possible                                            ║
╚═══════════════════════════════════════════════════════════════════╝

╔═══════════════════════════════════════════════════════════════════╗
║ 5. ACTIVER LE LOCKING!                                           ║
║    -> Évite les modifications concurrentes                         ║
║    -> Prévient la corruption                                       ║
╚═══════════════════════════════════════════════════════════════════╝
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ 3.2 BACKENDS - OÙ STOCKER LE STATE                                        ║
╚═══════════════════════════════════════════════════════════════════════════╝

DÉFINITION:
───────────

Un BACKEND définit où et comment le state est stocké et accédé.

TYPES DE BACKENDS:
──────────────────

```
┌─────────────────────────────────────────────────────────────────┐
│ 1. LOCAL (par défaut)                                           │
│    -> Fichier terraform.tfstate dans le dossier                  │
│    -> [ATTENTION]  NE PAS UTILISER EN PRODUCTION!                         │
│                                                                  │
│ 2. REMOTE (distants)                                            │
│    -> S3 (AWS)                                                   │
│    -> Google Cloud Storage (GCP)                                 │
│    -> Azure Blob Storage                                         │
│    -> Terraform Cloud                                            │
│    -> Consul                                                     │
│    -> etcd                                                       │
└─────────────────────────────────────────────────────────────────┘
```


┌───────────────────────────────────────────────────────────────────────────┐
│ BACKEND LOCAL (Par défaut)                                               │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# Configuration par défaut (optionnel de le spécifier)
terraform {
  backend "local" {
    path = "terraform.tfstate"  # Chemin du fichier (défaut)
  }
}
```

AVANTAGES:
──────────
[OK] Simple (aucune config)
[OK] Rapide (accès local)
[OK] Gratuit

INCONVÉNIENTS:
──────────────
[X] Pas partageable en équipe
[X] Pas de locking (risque de corruption)
[X] Pas de versioning (backup)
[X] Contient des secrets en clair
[X] Perdu si disque crash

QUAND L'UTILISER:
─────────────────
• Projets personnels
• Prototypage rapide
• Tests locaux
• Apprentissage

[ATTENTION]  JAMAIS en production ou en équipe!


┌───────────────────────────────────────────────────────────────────────────┐
│ BACKEND S3 + DYNAMODB (AWS) - RECOMMANDÉ                                 │
└───────────────────────────────────────────────────────────────────────────┘

ARCHITECTURE:
─────────────

```
┌──────────────────────────────────────────────────────────────────┐
│                    S3 + DYNAMODB BACKEND                          │
└──────────────────────────────────────────────────────────────────┘

Terraform                        AWS
┌──────────┐                    ┌──────────────────┐
│          │────read/write────-> │  S3 Bucket       │
│ terraform│                    │  (State storage) │
│  apply   │                    └──────────────────┘
│          │                           │
│          │                           v (versioning)
│          │                    ┌──────────────────┐
│          │                    │  S3 Versions     │
│          │                    │  (Backup auto)   │
│          │                    └──────────────────┘
│          │
│          │────acquire lock──-> ┌──────────────────┐
│          │                    │  DynamoDB Table  │
│          │<-───lock granted─── │  (State locking) │
└──────────┘                    └──────────────────┘
```

ÉTAPE 1: CRÉER L'INFRASTRUCTURE BACKEND
────────────────────────────────────────

```hcl
# backend-setup/main.tf
# ═══════════════════════════════════════════════════════════════════════
# [ATTENTION]  À EXÉCUTER UNE SEULE FOIS POUR CRÉER LE BACKEND
# ═══════════════════════════════════════════════════════════════════════

terraform {
  required_version = ">= 1.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  
  # Pour cette première étape, utiliser local backend
  # (chicken and egg problem: on crée le backend distant)
}

provider "aws" {
  region = "us-east-1"
}

# ┌─────────────────────────────────────────────────────────────────────┐
# │ BUCKET S3 POUR LE STATE                                             │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_s3_bucket" "terraform_state" {
  # Nom UNIQUE GLOBALEMENT
  bucket = "my-terraform-state-${data.aws_caller_identity.current.account_id}"
  
  # IMPORTANT: Empêche suppression accidentelle
  lifecycle {
    prevent_destroy = true
  }
  
  tags = {
    Name        = "Terraform State Bucket"
    Environment = "Management"
    Purpose     = "Infrastructure State Storage"
  }
}

# Data source pour récupérer l'account ID
data "aws_caller_identity" "current" {}

# ┌─────────────────────────────────────────────────────────────────────┐
# │ VERSIONING (Backup automatique!)                                    │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_s3_bucket_versioning" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  
  versioning_configuration {
    status = "Enabled"  # Activer versioning
  }
}

# POURQUOI? 
# -> Chaque modification du state crée une nouvelle version
# -> Rollback possible en cas d'erreur
# -> Historique complet des changements


# ┌─────────────────────────────────────────────────────────────────────┐
# │ CHIFFREMENT (Sécurité!)                                             │
# └─────────────────────────────────────────────────────────────────────┘

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"  # Chiffrement AES-256
    }
  }
}

# POURQUOI?
# -> Le state contient des secrets (passwords, keys)
# -> Chiffrement au repos obligatoire


# ┌─────────────────────────────────────────────────────────────────────┐
# │ BLOQUER 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
}

# POURQUOI?
# -> Empêche l'accès public accidentel
# -> State ne doit JAMAIS être public


# ┌─────────────────────────────────────────────────────────────────────┐
# │ LIFECYCLE POLICY (Nettoyage des anciennes versions)                 │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_s3_bucket_lifecycle_configuration" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  
  rule {
    id     = "delete-old-versions"
    status = "Enabled"
    
    # Supprimer les versions non-current après 90 jours
    noncurrent_version_expiration {
      noncurrent_days = 90
    }
    
    # Transition vers Glacier après 30 jours (économie)
    noncurrent_version_transition {
      noncurrent_days = 30
      storage_class   = "GLACIER"
    }
  }
}


# ┌─────────────────────────────────────────────────────────────────────┐
# │ TABLE DYNAMODB POUR LE LOCKING                                      │
# └─────────────────────────────────────────────────────────────────────┘

resource "aws_dynamodb_table" "terraform_lock" {
  name         = "terraform-state-lock"
  billing_mode = "PAY_PER_REQUEST"  # On-demand (pas de capacity planning)
  hash_key     = "LockID"
  
  attribute {
    name = "LockID"
    type = "S"  # String
  }
  
  # Protection contre suppression
  lifecycle {
    prevent_destroy = true
  }
  
  # Point-in-time recovery (backup)
  point_in_time_recovery {
    enabled = true
  }
  
  tags = {
    Name        = "Terraform State Lock Table"
    Environment = "Management"
    Purpose     = "State Locking"
  }
}

# POURQUOI DynamoDB?
# -> Atomic operations (pas de race conditions)
# -> Haute disponibilité
# -> Lock/Unlock en millisecondes
# -> Pay-per-request (très peu cher)


# ┌─────────────────────────────────────────────────────────────────────┐
# │ OUTPUTS (Important!)                                                 │
# └─────────────────────────────────────────────────────────────────────┘

output "s3_bucket_name" {
  description = "Nom du bucket S3 pour le state"
  value       = aws_s3_bucket.terraform_state.bucket
}

output "dynamodb_table_name" {
  description = "Nom de la table DynamoDB pour le locking"
  value       = aws_dynamodb_table.terraform_lock.name
}

output "backend_config" {
  description = "Configuration à copier dans votre backend"
  value = <<-EOT
    terraform {
      backend "s3" {
        bucket         = "${aws_s3_bucket.terraform_state.bucket}"
        key            = "path/to/terraform.tfstate"
        region         = "us-east-1"
        encrypt        = true
        dynamodb_table = "${aws_dynamodb_table.terraform_lock.name}"
      }
    }
  EOT
}
```

DÉPLOYER LE BACKEND:
────────────────────

```bash
cd backend-setup

# 1. Initialiser
terraform init

# 2. Planifier
terraform plan

# 3. Créer le backend
terraform apply

# Output affichera la config à copier!
# NOTER les noms du bucket et de la table
```

[ATTENTION]  IMPORTANT:
• Créer LE BACKEND UNE SEULE FOIS
• NE JAMAIS le détruire (sauf migration)
• Le state de cette stack peut rester local


ÉTAPE 2: CONFIGURER VOS PROJETS POUR UTILISER CE BACKEND
──────────────────────────────────────────────────────────

```hcl
# Dans vos projets Terraform
# versions.tf

terraform {
  required_version = ">= 1.0"
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ BACKEND S3 CONFIGURATION                                         │
  # └─────────────────────────────────────────────────────────────────┘
  
  backend "s3" {
    # Nom du bucket créé précédemment
    bucket = "my-terraform-state-123456789012"
    
    # Chemin du state dans le bucket (organisation)
    key = "prod/myapp/terraform.tfstate"
    #      └┬─┘ └─┬──┘ └────────┬─────────┘
    #       │    │              └─ Nom du fichier
    #       │    └─ Nom du projet/app
    #       └─ Environnement
    
    # Région du bucket
    region = "us-east-1"
    
    # Chiffrement (OBLIGATOIRE)
    encrypt = true
    
    # Table DynamoDB pour le locking (OBLIGATOIRE)
    dynamodb_table = "terraform-state-lock"
    
    # ┌─────────────────────────────────────────────────────────────────┐
    # │ CONFIGURATION AVANCÉE (Optionnel)                                │
    # └─────────────────────────────────────────────────────────────────┘
    
    # Profil AWS (si multi-comptes)
    # profile = "production"
    
    # Role ARN (si assume role)
    # role_arn = "arn:aws:iam::ACCOUNT:role/TerraformRole"
    
    # KMS key pour chiffrement (au lieu de AES256)
    # kms_key_id = "arn:aws:kms:us-east-1:ACCOUNT:key/KEY_ID"
  }
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}
```

ORGANISATION DES KEYS DANS S3:
──────────────────────────────

```
Bucket: my-terraform-state-123456789012
│
├── dev/
│   ├── frontend/
│   │   └── terraform.tfstate
│   ├── backend/
│   │   └── terraform.tfstate
│   └── database/
│       └── terraform.tfstate
│
├── staging/
│   ├── frontend/
│   │   └── terraform.tfstate
│   ├── backend/
│   │   └── terraform.tfstate
│   └── database/
│       └── terraform.tfstate
│
└── prod/
    ├── frontend/
    │   └── terraform.tfstate
    ├── backend/
    │   └── terraform.tfstate
    └── database/
        └── terraform.tfstate

STRUCTURE CLAIRE:
• Séparation par environnement (dev/staging/prod)
• Séparation par projet/composant (frontend/backend/database)
• Évite les conflits
• Isolation des changements
```

MIGRER VERS LE BACKEND S3:
──────────────────────────

```bash
# Si vous avez déjà un projet avec state local:

# 1. Ajouter la config backend dans versions.tf (ci-dessus)

# 2. Réinitialiser Terraform
terraform init

# Terraform détecte le nouveau backend et demande:
# Do you want to copy existing state to the new backend?
#   Pre-existing state was found while migrating the previous "local" backend
#   to the newly configured "s3" backend. No existing state was found in the
#   newly configured "s3" backend. Do you want to copy this state to the new
#   "s3" backend? Enter "yes" to copy and "no" to start with an empty state.
#   
#   Enter a value: 

# 3. Taper "yes"
yes

# 4. Vérifier que le state est bien dans S3
aws s3 ls s3://my-terraform-state-123456789012/prod/myapp/

# 5. Supprimer le state local (APRÈS vérification!)
rm terraform.tfstate terraform.tfstate.backup

# 6. Ajouter .gitignore (si pas déjà fait)
echo "*.tfstate" >> .gitignore
echo "*.tfstate.*" >> .gitignore
```


(Suite dans les fichiers suivants - Token limit approché...)

# Fichier: python_cheats/cheatsheets/terraform_part3_workspaces.txt
# Terraform - PARTIE 3.3: WORKSPACES ET STATE COMMANDS
# Guide Ultra-Détaillé


═══════════════════════════════════════════════════════════════════════════════
[SYNC] PARTIE 3.3: WORKSPACES - GÉRER PLUSIEURS ENVIRONNEMENTS
═══════════════════════════════════════════════════════════════════════════════

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

DÉFINITION:
───────────

Un WORKSPACE est un STATE SÉPARÉ utilisant la même configuration de code.
-> Permet de gérer plusieurs environnements (dev/staging/prod) avec le même code.

ANALOGIE:
─────────

```
WORKSPACE = BRANCHES GIT POUR L'INFRASTRUCTURE

Code unique            Workspaces (states séparés)
┌──────────────┐      ┌──────────────┐
│              │ ───-> │ dev          │ (state dev)
│  main.tf     │      ├──────────────┤
│  variables.tf│ ───-> │ staging      │ (state staging)
│  ...         │      ├──────────────┤
│              │ ───-> │ prod         │ (state prod)
└──────────────┘      └──────────────┘

MÊME CODE, DIFFÉRENTS ÉTATS!
```


WORKSPACE PAR DÉFAUT:
─────────────────────

Quand vous utilisez Terraform, vous êtes TOUJOURS dans un workspace.
Par défaut: workspace "default"

```bash
terraform workspace list
# * default  <- Vous êtes ici
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ COMMANDES WORKSPACE                                                       ║
╚═══════════════════════════════════════════════════════════════════════════╝

```bash
# ═══════════════════════════════════════════════════════════════════════
# LISTER LES WORKSPACES
# ═══════════════════════════════════════════════════════════════════════

terraform workspace list
# * default
#   dev
#   staging
#   prod
# (* = workspace actuel)


# ═══════════════════════════════════════════════════════════════════════
# VOIR LE WORKSPACE ACTUEL
# ═══════════════════════════════════════════════════════════════════════

terraform workspace show
# default


# ═══════════════════════════════════════════════════════════════════════
# CRÉER UN NOUVEAU WORKSPACE
# ═══════════════════════════════════════════════════════════════════════

terraform workspace new dev
# Created and switched to workspace "dev"!
# 
# You're now on a new, empty workspace. Workspaces isolate their state,
# so if you run "terraform plan" Terraform will not see any existing state
# for this configuration.

terraform workspace new staging
terraform workspace new prod


# ═══════════════════════════════════════════════════════════════════════
# CHANGER DE WORKSPACE
# ═══════════════════════════════════════════════════════════════════════

terraform workspace select prod
# Switched to workspace "prod"

terraform workspace select dev
# Switched to workspace "dev"


# ═══════════════════════════════════════════════════════════════════════
# SUPPRIMER UN WORKSPACE
# ═══════════════════════════════════════════════════════════════════════

# D'abord, changer vers un autre workspace
terraform workspace select default

# Puis supprimer
terraform workspace delete dev
# Deleted workspace "dev"!

# [ATTENTION] Ne peut pas supprimer le workspace actuel!
# [ATTENTION] Ne peut pas supprimer un workspace avec des ressources (sauf -force)
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ UTILISER LES WORKSPACES DANS LE CODE                                      ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ RÉFÉRENCER LE WORKSPACE ACTUEL                                           │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# terraform.workspace = nom du workspace actuel

locals {
  environment = terraform.workspace
  # Si workspace = "dev"   -> environment = "dev"
  # Si workspace = "prod"  -> environment = "prod"
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = terraform.workspace == "prod" ? "t3.large" : "t2.micro"
  
  tags = {
    Name        = "web-${terraform.workspace}"
    Environment = terraform.workspace
  }
}

output "environment" {
  value = "Déployé dans l'environnement: ${terraform.workspace}"
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ EXEMPLE COMPLET: CONFIGURATION PAR WORKSPACE                             │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# main.tf
# ═══════════════════════════════════════════════════════════════════════

terraform {
  required_version = ">= 1.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  
  # Backend S3 avec workspaces
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "app/terraform.tfstate"  # <- Même clé pour tous
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
    
    # Les workspaces ajoutent automatiquement un préfixe:
    # default  -> s3://bucket/app/terraform.tfstate
    # dev      -> s3://bucket/env:/dev/app/terraform.tfstate
    # prod     -> s3://bucket/env:/prod/app/terraform.tfstate
  }
}

provider "aws" {
  region = "us-east-1"
}

# ═══════════════════════════════════════════════════════════════════════
# CONFIGURATION PAR ENVIRONNEMENT
# ═══════════════════════════════════════════════════════════════════════

locals {
  # Map de configurations par environnement
  environment_config = {
    default = {
      instance_type  = "t2.micro"
      instance_count = 1
      db_instance    = "db.t3.micro"
      enable_backup  = false
    }
    dev = {
      instance_type  = "t2.micro"
      instance_count = 1
      db_instance    = "db.t3.micro"
      enable_backup  = false
    }
    staging = {
      instance_type  = "t2.small"
      instance_count = 2
      db_instance    = "db.t3.small"
      enable_backup  = true
    }
    prod = {
      instance_type  = "t3.large"
      instance_count = 3
      db_instance    = "db.m5.large"
      enable_backup  = true
    }
  }
  
  # Récupérer config du workspace actuel
  config = local.environment_config[terraform.workspace]
}

# ═══════════════════════════════════════════════════════════════════════
# VPC (unique par workspace)
# ═══════════════════════════════════════════════════════════════════════

resource "aws_vpc" "main" {
  cidr_block = "10.${terraform.workspace == "prod" ? 0 : terraform.workspace == "staging" ? 1 : 2}.0.0/16"
  
  tags = {
    Name        = "${terraform.workspace}-vpc"
    Environment = terraform.workspace
  }
}

# ═══════════════════════════════════════════════════════════════════════
# EC2 INSTANCES (configuration selon workspace)
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "app" {
  count = local.config.instance_count
  
  ami           = data.aws_ami.ubuntu.id
  instance_type = local.config.instance_type
  
  tags = {
    Name        = "${terraform.workspace}-app-${count.index + 1}"
    Environment = terraform.workspace
    Index       = count.index + 1
  }
}

# ═══════════════════════════════════════════════════════════════════════
# RDS (configuration selon workspace)
# ═══════════════════════════════════════════════════════════════════════

resource "aws_db_instance" "main" {
  identifier     = "${terraform.workspace}-database"
  engine         = "postgres"
  instance_class = local.config.db_instance
  
  allocated_storage       = terraform.workspace == "prod" ? 100 : 20
  backup_retention_period = local.config.enable_backup ? 30 : 7
  multi_az                = terraform.workspace == "prod"
  
  skip_final_snapshot = terraform.workspace != "prod"
  
  tags = {
    Name        = "${terraform.workspace}-db"
    Environment = terraform.workspace
  }
}

# ═══════════════════════════════════════════════════════════════════════
# OUTPUTS
# ═══════════════════════════════════════════════════════════════════════

output "workspace_info" {
  value = {
    workspace      = terraform.workspace
    instance_count = local.config.instance_count
    instance_type  = local.config.instance_type
    db_instance    = local.config.db_instance
  }
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ WORKFLOW COMPLET AVEC WORKSPACES                                         │
└───────────────────────────────────────────────────────────────────────────┘

```bash
# ═══════════════════════════════════════════════════════════════════════
# 1. INITIALISATION
# ═══════════════════════════════════════════════════════════════════════

terraform init
# Crée le workspace "default" automatiquement


# ═══════════════════════════════════════════════════════════════════════
# 2. CRÉER LES WORKSPACES
# ═══════════════════════════════════════════════════════════════════════

terraform workspace new dev
terraform workspace new staging
terraform workspace new prod


# ═══════════════════════════════════════════════════════════════════════
# 3. DÉPLOYER EN DEV
# ═══════════════════════════════════════════════════════════════════════

terraform workspace select dev

terraform plan
# -> Créera 1 instance t2.micro, 1 db.t3.micro

terraform apply -auto-approve


# ═══════════════════════════════════════════════════════════════════════
# 4. DÉPLOYER EN STAGING
# ═══════════════════════════════════════════════════════════════════════

terraform workspace select staging

terraform plan
# -> Créera 2 instances t2.small, 1 db.t3.small

terraform apply -auto-approve


# ═══════════════════════════════════════════════════════════════════════
# 5. DÉPLOYER EN PROD
# ═══════════════════════════════════════════════════════════════════════

terraform workspace select prod

terraform plan
# -> Créera 3 instances t3.large, 1 db.m5.large

terraform apply -auto-approve


# ═══════════════════════════════════════════════════════════════════════
# 6. VOIR L'ÉTAT DES WORKSPACES
# ═══════════════════════════════════════════════════════════════════════

terraform workspace list
#   default
#   dev
#   staging
# * prod  <- Workspace actuel


# ═══════════════════════════════════════════════════════════════════════
# 7. COMPARER LES ENVIRONNEMENTS
# ═══════════════════════════════════════════════════════════════════════

# Dev
terraform workspace select dev
terraform output workspace_info

# Staging
terraform workspace select staging
terraform output workspace_info

# Prod
terraform workspace select prod
terraform output workspace_info
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ AVANTAGES ET LIMITATIONS DES WORKSPACES                                   ║
╚═══════════════════════════════════════════════════════════════════════════╝

AVANTAGES:
──────────

[OK] **Un seul code** pour tous les environnements
[OK] **Isolation complète** des states
[OK] **Facile à utiliser** (commandes simples)
[OK] **Backend unique** (S3 partagé avec préfixes)
[OK] **Pas de duplication** de code

LIMITATIONS:
────────────

[X] **Même backend** pour tous les workspaces
   -> Si backend S3 compromis, tous les envs affectés

[X] **Difficile de gérer des différences majeures**
   -> Si dev et prod très différents, code devient complexe

[X] **Pas de séparation IAM native**
   -> Même credentials pour tous les workspaces

[X] **Risk de confusion**
   -> Oublier de changer de workspace = modifier mauvais env!

[X] **Pas recommandé pour production critique**
   -> Alternative: Dossiers séparés ou Terraform Cloud


QUAND UTILISER WORKSPACES?
───────────────────────────

[OK] Environnements similaires (dev/staging/prod identiques)
[OK] Petits projets / prototypes
[OK] Tests / expérimentations
[OK] Même équipe gérant tous les envs

[X] Environnements très différents
[X] Équipes séparées (dev team vs ops team)
[X] Production critique avec compliance stricte
[X] Multi-région ou multi-compte AWS


ALTERNATIVE RECOMMANDÉE POUR PRODUCTION:
─────────────────────────────────────────

```
Structure de dossiers séparés:

project/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── backend.tf       # Backend S3 dev
│   │   └── terraform.tfvars
│   ├── staging/
│   │   ├── main.tf
│   │   ├── backend.tf       # Backend S3 staging
│   │   └── terraform.tfvars
│   └── prod/
│       ├── main.tf
│       ├── backend.tf       # Backend S3 prod (séparé!)
│       └── terraform.tfvars
└── modules/
    └── app/
        ├── main.tf
        └── variables.tf

AVANTAGES:
[OK] Backends séparés (sécurité)
[OK] Permissions IAM différentes
[OK] Moins de risque de confusion
[OK] Peut avoir des configurations radicalement différentes
```


═══════════════════════════════════════════════════════════════════════════════
[OUTILS] PARTIE 3.4: STATE COMMANDS - MANIPULATION AVANCÉE
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ TERRAFORM STATE - COMMANDES AVANCÉES                                      ║
╚═══════════════════════════════════════════════════════════════════════════╝

[ATTENTION]  ATTENTION: Ces commandes modifient le state!
    Toujours faire un backup avant: terraform state pull > backup.tfstate

```bash
# ═══════════════════════════════════════════════════════════════════════
# STATE LIST - LISTER LES RESSOURCES
# ═══════════════════════════════════════════════════════════════════════

terraform state list
# aws_instance.web[0]
# aws_instance.web[1]
# aws_vpc.main
# aws_subnet.public[0]
# aws_subnet.public[1]
# module.database.aws_db_instance.main


# ═══════════════════════════════════════════════════════════════════════
# STATE SHOW - DÉTAILS D'UNE RESSOURCE
# ═══════════════════════════════════════════════════════════════════════

terraform state show aws_instance.web[0]
# # aws_instance.web[0]:
# resource "aws_instance" "web" {
#     ami                          = "ami-0c55b159cbfafe1f0"
#     instance_type                = "t2.micro"
#     id                           = "i-1234567890abcdef0"
#     public_ip                    = "54.123.45.67"
#     ...
# }


# ═══════════════════════════════════════════════════════════════════════
# STATE MV - RENOMMER UNE RESSOURCE
# ═══════════════════════════════════════════════════════════════════════

# Scénario: Vous avez renommé une ressource dans le code
# Avant: resource "aws_instance" "server"
# Après:  resource "aws_instance" "web_server"

# Sans state mv -> Terraform détruit l'ancienne et crée la nouvelle!
# Avec state mv -> Simple renommage, pas de destruction

terraform state mv aws_instance.server aws_instance.web_server
# Moved aws_instance.server to aws_instance.web_server


# ═══════════════════════════════════════════════════════════════════════
# STATE RM - RETIRER UNE RESSOURCE DU STATE
# ═══════════════════════════════════════════════════════════════════════

# Retire du state SANS détruire la ressource réelle
# Utile pour: arrêter de gérer une ressource avec Terraform

terraform state rm aws_instance.old_server
# Removed aws_instance.old_server
# 
# [ATTENTION] La ressource existe toujours dans AWS!
# [ATTENTION] Terraform ne la gère plus


# ═══════════════════════════════════════════════════════════════════════
# STATE PULL - TÉLÉCHARGER LE STATE
# ═══════════════════════════════════════════════════════════════════════

terraform state pull > current_state.json
# Sauvegarde le state actuel


# ═══════════════════════════════════════════════════════════════════════
# STATE PUSH - UPLOADER UN STATE
# ═══════════════════════════════════════════════════════════════════════

# [ATTENTION] DANGEREUX! Écrase le state distant
terraform state push backup_state.json


# ═══════════════════════════════════════════════════════════════════════
# STATE REPLACE-PROVIDER - CHANGER LE PROVIDER
# ═══════════════════════════════════════════════════════════════════════

# Utile après migration de provider
terraform state replace-provider \
  registry.terraform.io/hashicorp/aws \
  registry.terraform.io/hashicorp/aws


# ═══════════════════════════════════════════════════════════════════════
# IMPORT - IMPORTER RESSOURCE EXISTANTE
# ═══════════════════════════════════════════════════════════════════════

# Scénario: Instance EC2 créée manuellement, on veut la gérer avec Terraform

# 1. Créer le bloc resource (vide)
resource "aws_instance" "imported" {
  # Configuration sera remplie après import
}

# 2. Importer
terraform import aws_instance.imported i-1234567890abcdef0

# 3. Terraform génère maintenant le state pour cette ressource
terraform state show aws_instance.imported

# 4. Copier les attributs dans le code pour qu'ils matchent
```


┌───────────────────────────────────────────────────────────────────────────┐
│ CAS D'USAGE RÉELS                                                         │
└───────────────────────────────────────────────────────────────────────────┘

**CAS 1: REFACTORING - Déplacer ressource vers un module**

```bash
# Avant:
# resource "aws_instance" "web" { ... }

# Après:
# module "web_server" {
#   source = "./modules/web"
# }

# Déplacer dans le state
terraform state mv \
  aws_instance.web \
  module.web_server.aws_instance.main
```

**CAS 2: SPLIT - Séparer count en for_each**

```bash
# Avant: count
# resource "aws_instance" "web" {
#   count = 3
# }

# Après: for_each
# resource "aws_instance" "web" {
#   for_each = toset(["web-1", "web-2", "web-3"])
# }

# Migrer
terraform state mv 'aws_instance.web[0]' 'aws_instance.web["web-1"]'
terraform state mv 'aws_instance.web[1]' 'aws_instance.web["web-2"]'
terraform state mv 'aws_instance.web[2]' 'aws_instance.web["web-3"]'
```

**CAS 3: ADOPTION - Importer infrastructure existante**

```bash
# Infrastructure créée manuellement, maintenant gérée par Terraform

# 1. Écrire le code Terraform
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

# 2. Importer
terraform import aws_vpc.main vpc-1234567890abcdef0

# 3. Plan pour voir les diffs
terraform plan
# Ajuster le code jusqu'à ce que plan = "No changes"
```

FIN PARTIE 3 - STATE MANAGEMENT COMPLET! [BRAVO]

# Fichier: python_cheats/cheatsheets/terraform_part4_hcl_avance.txt
# Terraform - PARTIE 4: LANGAGE HCL AVANCÉ
# Guide Ultra-Détaillé - Maîtriser HCL de A à Z


═══════════════════════════════════════════════════════════════════════════════
[NOTE] PARTIE 4: LANGAGE HCL AVANCÉ
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ 4.1 SYNTAXE HCL COMPLÈTE - FONDAMENTAUX                                   ║
╚═══════════════════════════════════════════════════════════════════════════╝

DÉFINITION HCL:
───────────────

HCL = HashiCorp Configuration Language
• Langage déclaratif (pas impératif)
• Syntaxe lisible par humains
• Structure en blocs
• Fortement typé

ANALOGIE:
─────────

```
HCL ≈ JSON + YAML + UN PEU DE LOGIQUE

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

YAML:                   HCL:
resource:               resource "aws_instance" "web" {
  aws_instance:           ami = "ami-123"
    web:                }
      ami: ami-123

HCL = Plus structuré que YAML, plus lisible que JSON
```


┌───────────────────────────────────────────────────────────────────────────┐
│ STRUCTURE DE BASE                                                         │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# ═══════════════════════════════════════════════════════════════════════
# COMMENTAIRES
# ═══════════════════════════════════════════════════════════════════════

# Ceci est un commentaire sur une ligne

/*
  Ceci est un commentaire
  sur plusieurs lignes
*/

// Commentaire style C++ (aussi valide)


# ═══════════════════════════════════════════════════════════════════════
# BLOCS (BLOCKS)
# ═══════════════════════════════════════════════════════════════════════

# Syntaxe générale:
BLOCK_TYPE "LABEL_1" "LABEL_2" {
  ARGUMENT_NAME = ARGUMENT_VALUE
  
  NESTED_BLOCK {
    ARGUMENT_NAME = ARGUMENT_VALUE
  }
}

# Exemples concrets:

# Bloc avec 2 labels
resource "aws_instance" "web" {
  ami = "ami-123"
}

# Bloc avec 1 label
variable "instance_type" {
  type = string
}

# Bloc sans label
terraform {
  required_version = ">= 1.0"
}


# ═══════════════════════════════════════════════════════════════════════
# ARGUMENTS (ATTRIBUTS)
# ═══════════════════════════════════════════════════════════════════════

# Format: key = value
ami           = "ami-123"
instance_type = "t2.micro"
count         = 3
enabled       = true


# ═══════════════════════════════════════════════════════════════════════
# TYPES PRIMITIFS
# ═══════════════════════════════════════════════════════════════════════

# String (chaîne)
name = "web-server"
description = "My server"

# Number (nombre)
port = 8080
count = 3
timeout = 30.5

# Bool (booléen)
enabled = true
monitoring = false


# ═══════════════════════════════════════════════════════════════════════
# COLLECTIONS
# ═══════════════════════════════════════════════════════════════════════

# List (liste)
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
ports = [80, 443, 8080]

# Map (dictionnaire)
tags = {
  Name        = "web-server"
  Environment = "production"
  Owner       = "ops-team"
}

# Set (ensemble)
# Syntaxe identique à list, mais ordre non garanti
cidr_blocks = ["10.0.1.0/24", "10.0.2.0/24"]


# ═══════════════════════════════════════════════════════════════════════
# TYPES COMPLEXES
# ═══════════════════════════════════════════════════════════════════════

# Object (structure)
database = {
  engine   = "postgres"
  version  = "15.4"
  port     = 5432
  multi_az = true
}

# Tuple (liste typée)
config = ["web-server", 8080, true]
#         └─string─┘  └int┘  └bool┘


# ═══════════════════════════════════════════════════════════════════════
# BLOCS IMBRIQUÉS
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" {
  ami           = "ami-123"
  instance_type = "t2.micro"
  
  # Bloc imbriqué
  root_block_device {
    volume_size = 20
    volume_type = "gp3"
    encrypted   = true
  }
  
  # Autre bloc imbriqué
  tags = {
    Name = "web-server"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# RÉFÉRENCES
# ═══════════════════════════════════════════════════════════════════════

# Référence à une ressource
vpc_id = aws_vpc.main.id
#        └─type─┘ └name┘ └attribut┘

# Référence à une variable
instance_type = var.instance_type

# Référence à un local
name = local.server_name

# Référence à un data source
ami = data.aws_ami.ubuntu.id

# Référence à un module
connection_string = module.database.connection_string


# ═══════════════════════════════════════════════════════════════════════
# INTERPOLATION (EXPRESSIONS DANS STRINGS)
# ═══════════════════════════════════════════════════════════════════════

# Syntaxe: ${expression}
name = "server-${var.environment}"
# Résultat si environment = "prod": "server-prod"

url = "http://${aws_instance.web.public_ip}:8080"
# Résultat: "http://54.123.45.67:8080"

# Interpolation complexe
message = "Instance ${aws_instance.web.id} running at ${aws_instance.web.public_ip}"


# ═══════════════════════════════════════════════════════════════════════
# HEREDOC (MULTI-LIGNES)
# ═══════════════════════════════════════════════════════════════════════

# Syntaxe: <<-EOT ... EOT
user_data = <<-EOT
  #!/bin/bash
  apt-get update
  apt-get install -y nginx
  echo "Hello World" > /var/www/html/index.html
EOT

# Avec interpolation
user_data = <<-EOT
  #!/bin/bash
  echo "Environment: ${var.environment}" > /etc/environment
  echo "Server: ${var.server_name}" >> /etc/environment
EOT


# ═══════════════════════════════════════════════════════════════════════
# DIRECTIVES (INSTRUCTIONS SPÉCIALES)
# ═══════════════════════════════════════════════════════════════════════

# %{ if } ... %{ endif } - Conditionnel dans string
user_data = <<-EOT
  #!/bin/bash
  %{ if var.install_nginx }
  apt-get install -y nginx
  %{ endif }
  echo "Setup complete"
EOT

# %{ for } ... %{ endfor } - Boucle dans string
user_data = <<-EOT
  #!/bin/bash
  %{ for port in var.open_ports ~}
  ufw allow ${port}
  %{ endfor ~}
EOT
```


┌───────────────────────────────────────────────────────────────────────────┐
│ RÈGLES DE SYNTAXE IMPORTANTES                                            │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# ═══════════════════════════════════════════════════════════════════════
# 1. SENSIBILITÉ À LA CASSE
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" { }     # [OK] OK
Resource "aws_instance" "web" { }     # [X] Erreur (Resource avec majuscule)
RESOURCE "aws_instance" "web" { }     # [X] Erreur


# ═══════════════════════════════════════════════════════════════════════
# 2. GUILLEMETS
# ═══════════════════════════════════════════════════════════════════════

name = "web-server"                   # [OK] Double quotes
name = 'web-server'                   # [X] Single quotes non supportées


# ═══════════════════════════════════════════════════════════════════════
# 3. VIRGULES OPTIONNELLES
# ═══════════════════════════════════════════════════════════════════════

# Dans les listes (les deux valides)
ports = [80, 443, 8080]               # [OK] Avec virgules
ports = [80 443 8080]                 # [OK] Sans virgules (newline suffit)

ports = [
  80,
  443,
  8080,                               # [OK] Trailing comma OK
]


# ═══════════════════════════════════════════════════════════════════════
# 4. ÉGALITÉ vs DEUX-POINTS
# ═══════════════════════════════════════════════════════════════════════

# Arguments: = (égal)
ami = "ami-123"                       # [OK]

# JAMAIS deux-points pour arguments
ami: "ami-123"                        # [X] Erreur


# ═══════════════════════════════════════════════════════════════════════
# 5. INDENTATION
# ═══════════════════════════════════════════════════════════════════════

# Convention: 2 espaces
resource "aws_instance" "web" {
  ami           = "ami-123"           # 2 espaces
  instance_type = "t2.micro"
  
  tags = {
    Name = "server"                   # 4 espaces (nested)
  }
}

# terraform fmt formatte automatiquement


# ═══════════════════════════════════════════════════════════════════════
# 6. ALIGNEMENT
# ═══════════════════════════════════════════════════════════════════════

# terraform fmt aligne automatiquement les =
ami           = "ami-123"
instance_type = "t2.micro"
monitoring    = true


# ═══════════════════════════════════════════════════════════════════════
# 7. NOMS VALIDES (IDENTIFIERS)
# ═══════════════════════════════════════════════════════════════════════

# Règles:
# - Commence par lettre ou underscore
# - Contient lettres, chiffres, underscores, hyphens
# - Pas de mots réservés (if, for, etc.)

# [OK] Valides
resource "aws_instance" "web_server" { }
resource "aws_instance" "web-server" { }
resource "aws_instance" "web123" { }
resource "aws_instance" "_server" { }

# [X] Invalides
resource "aws_instance" "123web" { }        # Commence par chiffre
resource "aws_instance" "web server" { }    # Espace
resource "aws_instance" "web.server" { }    # Point
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ 4.2 EXPRESSIONS ET OPÉRATEURS - CALCULS ET LOGIQUE                        ║
╚═══════════════════════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────────────────────┐
│ OPÉRATEURS ARITHMÉTIQUES                                                  │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # Addition
  total = 10 + 5                      # 15
  
  # Soustraction
  difference = 20 - 8                 # 12
  
  # Multiplication
  product = 4 * 7                     # 28
  
  # Division
  quotient = 15 / 3                   # 5
  result   = 10 / 3                   # 3.333...
  
  # Modulo (reste)
  remainder = 17 % 5                  # 2
  
  # Priorité des opérations (PEMDAS)
  calc1 = 2 + 3 * 4                   # 14 (pas 20!)
  calc2 = (2 + 3) * 4                 # 20 (parenthèses forcent l'ordre)
  
  # Avec variables
  instance_count = var.base_count * 2
  storage_size   = var.volume_size + 10
}

# ═══════════════════════════════════════════════════════════════════════
# CAS D'USAGE RÉELS
# ═══════════════════════════════════════════════════════════════════════

locals {
  # Calculer taille totale de stockage
  gb_per_instance = 20
  total_storage_gb = var.instance_count * local.gb_per_instance
  # Si instance_count = 5 -> 100 GB total
  
  # Calculer coût mensuel estimé
  cost_per_instance_per_hour = 0.0116  # t2.micro
  hours_per_month = 730
  monthly_cost = var.instance_count * local.cost_per_instance_per_hour * local.hours_per_month
  
  # Round robin: distribuer dans plusieurs subnets
  subnet_index = count.index % length(var.subnet_ids)
  # count.index = 0,1,2,3,4,5... -> subnet_index = 0,1,2,0,1,2... (si 3 subnets)
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ OPÉRATEURS DE COMPARAISON                                                │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # Égalité
  is_prod = var.environment == "prod"         # true/false
  
  # Inégalité
  not_dev = var.environment != "dev"          # true/false
  
  # Plus grand
  is_large = var.instance_count > 5           # true/false
  
  # Plus grand ou égal
  is_medium_plus = var.instance_count >= 3    # true/false
  
  # Plus petit
  is_small = var.instance_count < 3           # true/false
  
  # Plus petit ou égal
  is_small_or_medium = var.instance_count <= 5 # true/false
}

# ═══════════════════════════════════════════════════════════════════════
# UTILISATION DANS CONDITIONS
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" {
  count = var.environment == "prod" ? 3 : 1
  # Si prod -> 3 instances, sinon -> 1
  
  ami           = "ami-123"
  instance_type = var.instance_count > 10 ? "t3.large" : "t2.micro"
  # Si beaucoup d'instances -> type plus gros
  
  monitoring = var.environment != "dev"
  # Monitoring activé partout sauf dev
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ OPÉRATEURS LOGIQUES                                                       │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # AND (&&)
  enable_backup = var.environment == "prod" && var.backup_enabled
  # true SEULEMENT si prod ET backup_enabled
  
  # OR (||)
  enable_monitoring = var.environment == "prod" || var.environment == "staging"
  # true si prod OU staging
  
  # NOT (!)
  is_development = !var.is_production
  # Inverse le booléen
  
  # Combinaisons complexes
  should_scale = (
    var.environment == "prod" && 
    var.load_average > 0.8
  ) || var.force_scale
  # Scale si (prod ET charge haute) OU force_scale activé
}

# ═══════════════════════════════════════════════════════════════════════
# CAS D'USAGE: CONTRÔLES FINS
# ═══════════════════════════════════════════════════════════════════════

resource "aws_db_instance" "main" {
  identifier = "mydb"
  
  # Multi-AZ seulement si prod ET haute dispo requise
  multi_az = var.environment == "prod" && var.high_availability
  
  # Backup si pas dev ET (prod OU staging)
  backup_retention_period = (
    var.environment != "dev" && 
    (var.environment == "prod" || var.environment == "staging")
  ) ? 30 : 0
  
  # Chiffrement si (prod OU staging) ET (conformité OU sensible)
  storage_encrypted = (
    (var.environment == "prod" || var.environment == "staging") &&
    (var.compliance_required || var.data_sensitive)
  )
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ OPÉRATEUR TERNAIRE (CONDITIONNEL)                                        │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# Syntaxe: CONDITION ? VALEUR_SI_VRAI : VALEUR_SI_FAUX

locals {
  # Simple
  instance_type = var.environment == "prod" ? "t3.large" : "t2.micro"
  
  # Avec calcul
  instance_count = var.environment == "prod" ? var.base_count * 3 : 1
  
  # Nested (imbriqué)
  instance_type = (
    var.environment == "prod" ? "t3.large" :
    var.environment == "staging" ? "t2.small" :
    "t2.micro"  # default
  )
}

# ═══════════════════════════════════════════════════════════════════════
# EXEMPLES PRATIQUES
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" {
  ami = var.environment == "prod" ? var.prod_ami : var.dev_ami
  
  instance_type = var.instance_count > 10 ? "t3.large" : "t2.small"
  
  # Monitoring détaillé seulement en prod
  monitoring = var.environment == "prod" ? true : false
  
  # EBS optimized seulement si type large
  ebs_optimized = var.instance_type == "t3.large" ? true : false
  
  # Tags conditionnels
  tags = merge(
    var.common_tags,
    {
      Name = var.environment == "prod" ? "prod-web" : "dev-web"
      Tier = var.is_public ? "public" : "private"
    }
  )
}

# ═══════════════════════════════════════════════════════════════════════
# PATTERN: CASCADE DE CONDITIONS
# ═══════════════════════════════════════════════════════════════════════

locals {
  # Déterminer taille selon environnement
  instance_size = (
    var.environment == "prod" ? "large" :
    var.environment == "staging" ? "medium" :
    var.environment == "test" ? "small" :
    "micro"  # default (dev, etc.)
  )
  
  # Mapper taille -> type EC2
  instance_type_map = {
    micro  = "t2.micro"
    small  = "t2.small"
    medium = "t2.medium"
    large  = "t3.large"
  }
  
  instance_type = local.instance_type_map[local.instance_size]
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ ACCÈS AUX COLLECTIONS                                                     │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # Liste
  availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
  
  # Accès par index (commence à 0)
  first_az  = local.availability_zones[0]      # "us-east-1a"
  second_az = local.availability_zones[1]      # "us-east-1b"
  last_az   = local.availability_zones[2]      # "us-east-1c"
  
  # Longueur
  az_count = length(local.availability_zones)  # 3
  
  # Map
  instance_types = {
    dev     = "t2.micro"
    staging = "t2.small"
    prod    = "t3.large"
  }
  
  # Accès par clé
  dev_type  = local.instance_types["dev"]      # "t2.micro"
  prod_type = local.instance_types["prod"]     # "t3.large"
  
  # Accès sécurisé (avec default si clé inexistante)
  current_type = lookup(
    local.instance_types,
    var.environment,
    "t2.micro"  # default
  )
}

# ═══════════════════════════════════════════════════════════════════════
# SLICING (EXTRACTION DE SOUS-ENSEMBLES)
# ═══════════════════════════════════════════════════════════════════════

locals {
  all_zones = ["us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d"]
  
  # Slice: liste[start:end]
  first_two = slice(local.all_zones, 0, 2)    # ["us-east-1a", "us-east-1b"]
  last_two  = slice(local.all_zones, 2, 4)    # ["us-east-1c", "us-east-1d"]
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ SPLAT EXPRESSIONS (*)                                                     │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# Splat = Extraire un attribut de TOUS les éléments d'une liste

# ═══════════════════════════════════════════════════════════════════════
# AVEC count
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" {
  count = 3
  
  ami           = "ami-123"
  instance_type = "t2.micro"
  
  tags = {
    Name = "web-${count.index}"
  }
}

# Récupérer tous les IDs
output "all_instance_ids" {
  value = aws_instance.web[*].id
  # Équivalent à:
  # [
  #   aws_instance.web[0].id,
  #   aws_instance.web[1].id,
  #   aws_instance.web[2].id
  # ]
}

# Récupérer toutes les IPs
output "all_public_ips" {
  value = aws_instance.web[*].public_ip
}

# ═══════════════════════════════════════════════════════════════════════
# AVEC for_each
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "app" {
  for_each = toset(["web", "api", "worker"])
  
  ami           = "ami-123"
  instance_type = "t2.micro"
  
  tags = {
    Name = each.key
  }
}

# Récupérer tous les IDs (avec values)
output "app_instance_ids" {
  value = values(aws_instance.app)[*].id
  # OU
  value = [for instance in aws_instance.app : instance.id]
}
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ 4.3 FONCTIONS BUILT-IN - 50+ FONCTIONS DÉTAILLÉES                         ║
╚═══════════════════════════════════════════════════════════════════════════╝

Terraform inclut 80+ fonctions intégrées. Voici les plus importantes:

┌───────────────────────────────────────────────────────────────────────────┐
│ FONCTIONS STRING (CHAÎNES DE CARACTÈRES)                                 │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # ═══════════════════════════════════════════════════════════════════════
  # upper() - MAJUSCULES
  # ═══════════════════════════════════════════════════════════════════════
  
  name = "hello world"
  uppercase = upper(local.name)               # "HELLO WORLD"
  
  # Cas d'usage: tags normalisés
  resource_name = upper(var.project_name)
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # lower() - minuscules
  # ═══════════════════════════════════════════════════════════════════════
  
  email = "User@EXAMPLE.COM"
  lowercase = lower(local.email)              # "user@example.com"
  
  # Cas d'usage: noms de ressources AWS (souvent lowercase requis)
  bucket_name = lower("${var.company}-${var.project}")
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # title() - Title Case
  # ═══════════════════════════════════════════════════════════════════════
  
  text = "hello world"
  title_case = title(local.text)              # "Hello World"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # trim() - Supprimer espaces début/fin
  # ═══════════════════════════════════════════════════════════════════════
  
  padded = "  hello world  "
  trimmed = trim(local.padded, " ")           # "hello world"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # trimprefix() / trimsuffix() - Supprimer préfixe/suffixe
  # ═══════════════════════════════════════════════════════════════════════
  
  name_with_prefix = "prod-myapp"
  without_prefix = trimprefix(local.name_with_prefix, "prod-")  # "myapp"
  
  name_with_suffix = "myapp-v1"
  without_suffix = trimsuffix(local.name_with_suffix, "-v1")    # "myapp"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # replace() - Remplacer substring
  # ═══════════════════════════════════════════════════════════════════════
  
  text = "Hello_World"
  replaced = replace(local.text, "_", "-")    # "Hello-World"
  
  # Cas d'usage: normaliser noms
  safe_name = replace(var.user_input, " ", "-")
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # split() - Diviser string en liste
  # ═══════════════════════════════════════════════════════════════════════
  
  csv = "web,api,worker"
  services = split(",", local.csv)            # ["web", "api", "worker"]
  
  # Cas d'usage: parser input utilisateur
  availability_zones = split(",", var.az_input)
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # join() - Joindre liste en string
  # ═══════════════════════════════════════════════════════════════════════
  
  items = ["apple", "banana", "cherry"]
  joined = join(", ", local.items)            # "apple, banana, cherry"
  
  # Cas d'usage: construire connection string
  hosts = ["host1", "host2", "host3"]
  connection_string = "postgresql://${join(",", local.hosts)}/db"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # substr() - Extraire sous-chaîne
  # ═══════════════════════════════════════════════════════════════════════
  
  text = "HelloWorld"
  sub = substr(local.text, 0, 5)              # "Hello"
  
  # substr(string, offset, length)
  # offset = position de départ (0 = début)
  # length = nombre de caractères
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # format() - Formater string (style printf)
  # ═══════════════════════════════════════════════════════════════════════
  
  formatted = format("Server %s has IP %s", "web-1", "10.0.1.10")
  # "Server web-1 has IP 10.0.1.10"
  
  # Avec padding
  padded_number = format("%03d", 42)          # "042"
  
  # Cas d'usage: noms avec numéros
  instance_name = format("web-%03d", count.index + 1)  # "web-001", "web-002"...
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # formatlist() - Format sur liste
  # ═══════════════════════════════════════════════════════════════════════
  
  names = ["web", "api", "worker"]
  formatted_names = formatlist("server-%s", local.names)
  # ["server-web", "server-api", "server-worker"]
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # regex() - Expression régulière
  # ═══════════════════════════════════════════════════════════════════════
  
  email = "user@example.com"
  username = regex("^([^@]+)@", local.email)[0]  # "user"
  
  # Validation
  is_valid_email = can(regex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", var.email))
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # startswith() / endswith() - Vérifier début/fin
  # ═══════════════════════════════════════════════════════════════════════
  
  filename = "config.json"
  is_json = endswith(local.filename, ".json")  # true
  is_config = startswith(local.filename, "config")  # true
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ FONCTIONS NUMÉRIQUES                                                      │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # ═══════════════════════════════════════════════════════════════════════
  # abs() - Valeur absolue
  # ═══════════════════════════════════════════════════════════════════════
  
  negative = -42
  absolute = abs(local.negative)              # 42
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # ceil() - Arrondir vers le haut
  # ═══════════════════════════════════════════════════════════════════════
  
  value = 4.3
  rounded_up = ceil(local.value)              # 5
  
  # Cas d'usage: calculer nombre d'instances nécessaires
  users = 123
  users_per_instance = 50
  instance_count = ceil(local.users / local.users_per_instance)  # 3
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # floor() - Arrondir vers le bas
  # ═══════════════════════════════════════════════════════════════════════
  
  value = 4.7
  rounded_down = floor(local.value)           # 4
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # max() - Maximum
  # ═══════════════════════════════════════════════════════════════════════
  
  maximum = max(5, 12, 3, 9)                  # 12
  
  # Cas d'usage: garantir minimum
  instance_count = max(var.desired_count, 2)  # Au moins 2
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # min() - Minimum
  # ═══════════════════════════════════════════════════════════════════════
  
  minimum = min(5, 12, 3, 9)                  # 3
  
  # Cas d'usage: limiter maximum
  instance_count = min(var.desired_count, 10)  # Max 10
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # pow() - Puissance
  # ═══════════════════════════════════════════════════════════════════════
  
  power = pow(2, 8)                           # 256 (2^8)
  
  # Cas d'usage: calculer taille CIDR
  cidr_size = pow(2, 32 - 24)                 # 256 IPs (/24)
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # log() - Logarithme
  # ═══════════════════════════════════════════════════════════════════════
  
  logarithm = log(100, 10)                    # 2 (log10(100))
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ FONCTIONS DE COLLECTION                                                   │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # ═══════════════════════════════════════════════════════════════════════
  # length() - Longueur
  # ═══════════════════════════════════════════════════════════════════════
  
  list = ["a", "b", "c"]
  list_length = length(local.list)            # 3
  
  map = { a = 1, b = 2, c = 3 }
  map_length = length(local.map)              # 3
  
  string = "hello"
  string_length = length(local.string)        # 5
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # concat() - Concaténer listes
  # ═══════════════════════════════════════════════════════════════════════
  
  list1 = ["a", "b"]
  list2 = ["c", "d"]
  combined = concat(local.list1, local.list2)  # ["a", "b", "c", "d"]
  
  # Cas d'usage: combiner CIDRs
  private_cidrs = ["10.0.1.0/24", "10.0.2.0/24"]
  public_cidrs  = ["10.0.10.0/24", "10.0.11.0/24"]
  all_cidrs = concat(local.private_cidrs, local.public_cidrs)
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # contains() - Vérifier présence
  # ═══════════════════════════════════════════════════════════════════════
  
  list = ["prod", "staging", "dev"]
  is_prod = contains(local.list, "prod")      # true
  is_test = contains(local.list, "test")      # false
  
  # Cas d'usage: validation
  valid_environment = contains(["dev", "staging", "prod"], var.environment)
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # distinct() - Supprimer doublons
  # ═══════════════════════════════════════════════════════════════════════
  
  list = ["a", "b", "a", "c", "b"]
  unique = distinct(local.list)               # ["a", "b", "c"]
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # flatten() - Aplatir listes imbriquées
  # ═══════════════════════════════════════════════════════════════════════
  
  nested = [["a", "b"], ["c", "d"], ["e"]]
  flat = flatten(local.nested)                # ["a", "b", "c", "d", "e"]
  
  # Cas d'usage: combiner multiple for expressions
  all_ports = flatten([
    for service in var.services : service.ports
  ])
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # keys() - Extraire clés d'un map
  # ═══════════════════════════════════════════════════════════════════════
  
  map = {
    web    = "t2.micro"
    api    = "t2.small"
    worker = "t2.medium"
  }
  service_names = keys(local.map)             # ["web", "api", "worker"]
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # values() - Extraire valeurs d'un map
  # ═══════════════════════════════════════════════════════════════════════
  
  instance_types = values(local.map)          # ["t2.micro", "t2.small", "t2.medium"]
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # lookup() - Chercher dans map avec default
  # ═══════════════════════════════════════════════════════════════════════
  
  map = { dev = "t2.micro", prod = "t3.large" }
  instance_type = lookup(local.map, var.environment, "t2.micro")
  # Si environment existe dans map -> retourne valeur
  # Sinon -> retourne "t2.micro"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # merge() - Fusionner maps
  # ═══════════════════════════════════════════════════════════════════════
  
  map1 = { a = 1, b = 2 }
  map2 = { c = 3, d = 4 }
  merged = merge(local.map1, local.map2)      # { a=1, b=2, c=3, d=4 }
  
  # Si conflit, dernière valeur gagne
  map3 = { a = 1, b = 2 }
  map4 = { b = 99, c = 3 }
  merged = merge(local.map3, local.map4)      # { a=1, b=99, c=3 }
  
  # Cas d'usage: combiner tags
  all_tags = merge(
    var.common_tags,
    var.environment_tags,
    { Name = "web-server" }
  )
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # reverse() - Inverser liste
  # ═══════════════════════════════════════════════════════════════════════
  
  list = ["a", "b", "c"]
  reversed = reverse(local.list)              # ["c", "b", "a"]
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # sort() - Trier liste
  # ═══════════════════════════════════════════════════════════════════════
  
  list = ["zebra", "apple", "banana"]
  sorted = sort(local.list)                   # ["apple", "banana", "zebra"]
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # slice() - Extraire sous-ensemble
  # ═══════════════════════════════════════════════════════════════════════
  
  list = ["a", "b", "c", "d", "e"]
  subset = slice(local.list, 1, 4)            # ["b", "c", "d"]
  # slice(list, start_index, end_index)
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # zipmap() - Créer map depuis 2 listes
  # ═══════════════════════════════════════════════════════════════════════
  
  keys = ["name", "age", "city"]
  values = ["Alice", "30", "Paris"]
  map = zipmap(local.keys, local.values)
  # { name = "Alice", age = "30", city = "Paris" }
  
  # Cas d'usage: mapper AZs -> subnet IDs
  az_to_subnet = zipmap(
    data.aws_availability_zones.available.names,
    aws_subnet.public[*].id
  )
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ FONCTIONS DE DATE/TEMPS                                                   │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # ═══════════════════════════════════════════════════════════════════════
  # timestamp() - Timestamp actuel
  # ═══════════════════════════════════════════════════════════════════════
  
  current_time = timestamp()
  # "2024-01-15T14:30:00Z"
  
  # Cas d'usage: tags de création
  tags = {
    CreatedAt = timestamp()
  }
  
  # [ATTENTION] timestamp() change à CHAQUE apply!
  # Utiliser avec lifecycle.ignore_changes si besoin
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # formatdate() - Formater date
  # ═══════════════════════════════════════════════════════════════════════
  
  formatted = formatdate("DD MMM YYYY hh:mm", timestamp())
  # "15 Jan 2024 14:30"
  
  # Formats:
  # YYYY = année (2024)
  # MM = mois (01-12)
  # DD = jour (01-31)
  # hh = heure (00-23)
  # mm = minute (00-59)
  # ss = seconde (00-59)
  
  date_only = formatdate("YYYY-MM-DD", timestamp())  # "2024-01-15"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # timeadd() - Ajouter durée
  # ═══════════════════════════════════════════════════════════════════════
  
  now = timestamp()
  in_one_hour = timeadd(local.now, "1h")
  in_one_day = timeadd(local.now, "24h")
  in_one_week = timeadd(local.now, "168h")
  
  # Cas d'usage: expiration
  expiration_date = timeadd(timestamp(), "720h")  # +30 jours
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ FONCTIONS RÉSEAU/IP                                                       │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # ═══════════════════════════════════════════════════════════════════════
  # cidrhost() - Extraire IP d'un CIDR
  # ═══════════════════════════════════════════════════════════════════════
  
  vpc_cidr = "10.0.0.0/16"
  
  first_ip = cidrhost(local.vpc_cidr, 0)      # "10.0.0.0"
  second_ip = cidrhost(local.vpc_cidr, 1)     # "10.0.0.1"
  tenth_ip = cidrhost(local.vpc_cidr, 10)     # "10.0.0.10"
  
  # Cas d'usage: IPs fixes pour ressources
  bastion_ip = cidrhost(var.public_subnet_cidr, 10)
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # cidrnetmask() - Extraire netmask
  # ═══════════════════════════════════════════════════════════════════════
  
  cidr = "10.0.0.0/16"
  netmask = cidrnetmask(local.cidr)           # "255.255.0.0"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # cidrsubnet() - Diviser CIDR en sous-réseaux
  # ═══════════════════════════════════════════════════════════════════════
  
  vpc_cidr = "10.0.0.0/16"
  
  # cidrsubnet(prefix, newbits, netnum)
  # newbits = combien de bits ajouter au masque
  # netnum = quel sous-réseau (0, 1, 2...)
  
  subnet_0 = cidrsubnet(local.vpc_cidr, 8, 0)  # "10.0.0.0/24"
  subnet_1 = cidrsubnet(local.vpc_cidr, 8, 1)  # "10.0.1.0/24"
  subnet_2 = cidrsubnet(local.vpc_cidr, 8, 2)  # "10.0.2.0/24"
  
  # Cas d'usage: créer subnets automatiquement
  subnet_cidrs = [
    for i in range(3) :
    cidrsubnet(var.vpc_cidr, 8, i)
  ]
  # ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24"]
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # cidrsubnets() - Diviser en plusieurs sous-réseaux d'un coup
  # ═══════════════════════════════════════════════════════════════════════
  
  vpc_cidr = "10.0.0.0/16"
  
  # cidrsubnets(prefix, newbits...)
  subnets = cidrsubnets(local.vpc_cidr, 4, 4, 8, 4)
  # [
  #   "10.0.0.0/20",   # newbits=4
  #   "10.0.16.0/20",  # newbits=4
  #   "10.0.32.0/24",  # newbits=8
  #   "10.0.33.0/20"   # newbits=4
  # ]
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ FONCTIONS FILESYSTEM                                                      │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # ═══════════════════════════════════════════════════════════════════════
  # file() - Lire fichier
  # ═══════════════════════════════════════════════════════════════════════
  
  script_content = file("${path.module}/scripts/init.sh")
  config_content = file("./config.json")
  
  # Cas d'usage: user_data
  user_data = file("${path.module}/user-data.sh")
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # fileexists() - Vérifier existence fichier
  # ═══════════════════════════════════════════════════════════════════════
  
  has_config = fileexists("./config.json")    # true/false
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # filebase64() - Lire fichier en base64
  # ═══════════════════════════════════════════════════════════════════════
  
  image_data = filebase64("./logo.png")
  
  # Cas d'usage: upload fichier dans S3
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # basename() - Extraire nom de fichier
  # ═══════════════════════════════════════════════════════════════════════
  
  path = "/path/to/file.txt"
  filename = basename(local.path)             # "file.txt"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # dirname() - Extraire dossier parent
  # ═══════════════════════════════════════════════════════════════════════
  
  path = "/path/to/file.txt"
  directory = dirname(local.path)             # "/path/to"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # abspath() - Chemin absolu
  # ═══════════════════════════════════════════════════════════════════════
  
  relative = "./config.json"
  absolute = abspath(local.relative)          # "/full/path/to/config.json"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # path.module / path.root / path.cwd
  # ═══════════════════════════════════════════════════════════════════════
  
  # path.module = chemin du module actuel
  script = file("${path.module}/init.sh")
  
  # path.root = chemin du module root
  config = file("${path.root}/global-config.json")
  
  # path.cwd = current working directory
  local_file = file("${path.cwd}/local.txt")
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ FONCTIONS TYPE/CONVERSION                                                 │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # ═══════════════════════════════════════════════════════════════════════
  # type() - Obtenir le type
  # ═══════════════════════════════════════════════════════════════════════
  
  string_type = type("hello")                 # "string"
  number_type = type(42)                      # "number"
  bool_type = type(true)                      # "bool"
  list_type = type([1, 2, 3])                 # "list"
  map_type = type({a = 1})                    # "map"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # can() - Tester si expression valide
  # ═══════════════════════════════════════════════════════════════════════
  
  # Tester si string est un nombre
  is_number = can(tonumber("123"))            # true
  not_number = can(tonumber("abc"))           # false
  
  # Tester si CIDR valide
  is_valid_cidr = can(cidrhost("10.0.0.0/16", 0))  # true
  
  # Cas d'usage: validation
  valid_port = can(tonumber(var.port)) && tonumber(var.port) > 0
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # try() - Essayer expression, fallback si erreur
  # ═══════════════════════════════════════════════════════════════════════
  
  # try(expression1, expression2, ..., default)
  port = try(tonumber(var.port), 8080)
  # Si var.port convertible en nombre -> utilise conversion
  # Sinon -> utilise 8080
  
  # Cas d'usage: parsing flexible
  instance_count = try(
    tonumber(var.count),
    length(var.instance_list),
    3  # default
  )
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # tobool() / tostring() / tonumber() - Conversions
  # ═══════════════════════════════════════════════════════════════════════
  
  # String -> Number
  port_number = tonumber("8080")              # 8080
  
  # String -> Bool
  is_enabled = tobool("true")                 # true
  
  # Number -> String
  port_string = tostring(8080)                # "8080"
  
  # Bool -> String
  enabled_string = tostring(true)             # "true"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # tolist() / tomap() / toset() - Conversions collections
  # ═══════════════════════════════════════════════════════════════════════
  
  # Tuple -> List
  tuple = ["a", 1, true]
  list = tolist(local.tuple)
  
  # List -> Set
  list_with_duplicates = ["a", "b", "a", "c"]
  set = toset(local.list_with_duplicates)     # {"a", "b", "c"}
  
  # Object -> Map
  object = { a = 1, b = 2 }
  map = tomap(local.object)
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ FONCTIONS ENCODING                                                        │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # ═══════════════════════════════════════════════════════════════════════
  # base64encode() / base64decode()
  # ═══════════════════════════════════════════════════════════════════════
  
  text = "Hello World"
  encoded = base64encode(local.text)          # "SGVsbG8gV29ybGQ="
  decoded = base64decode(local.encoded)       # "Hello World"
  
  # Cas d'usage: user_data (doit être base64)
  user_data = base64encode(file("${path.module}/init.sh"))
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # jsonencode() / jsondecode()
  # ═══════════════════════════════════════════════════════════════════════
  
  # Object -> JSON string
  data = { name = "Alice", age = 30 }
  json_string = jsonencode(local.data)        # '{"name":"Alice","age":30}'
  
  # JSON string -> Object
  json = '{"name":"Bob","age":25}'
  object = jsondecode(local.json)             # { name = "Bob", age = 25 }
  
  # Cas d'usage: IAM policies
  policy_json = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Action = ["s3:GetObject"]
      Resource = ["arn:aws:s3:::my-bucket/*"]
    }]
  })
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # yamlencode() / yamldecode()
  # ═══════════════════════════════════════════════════════════════════════
  
  # Object -> YAML string
  data = { name = "Alice", items = ["a", "b"] }
  yaml_string = yamlencode(local.data)
  # name: Alice
  # items:
  # - a
  # - b
  
  # YAML string -> Object
  yaml = <<-EOT
    name: Bob
    age: 25
  EOT
  object = yamldecode(local.yaml)             # { name = "Bob", age = 25 }
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # urlencode() - Encoder pour URL
  # ═══════════════════════════════════════════════════════════════════════
  
  text = "Hello World!"
  url_safe = urlencode(local.text)            # "Hello+World%21"
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ FONCTIONS CRYPTO/HASH                                                     │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
locals {
  # ═══════════════════════════════════════════════════════════════════════
  # md5() - Hash MD5
  # ═══════════════════════════════════════════════════════════════════════
  
  text = "Hello World"
  hash = md5(local.text)
  # "b10a8db164e0754105b7a99be72e3fe5"
  
  # Cas d'usage: générer ID unique basé sur contenu
  content_hash = md5(file("${path.module}/config.json"))
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # sha1() / sha256() / sha512() - Hash SHA
  # ═══════════════════════════════════════════════════════════════════════
  
  text = "Hello World"
  sha1_hash = sha1(local.text)
  sha256_hash = sha256(local.text)
  sha512_hash = sha512(local.text)
  
  # Cas d'usage: vérifier intégrité fichier
  checksum = sha256(file("./script.sh"))
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # uuid() - Générer UUID
  # ═══════════════════════════════════════════════════════════════════════
  
  unique_id = uuid()
  # "8fa85f64-5717-4562-b3fc-2c963f66afa6"
  
  # [ATTENTION] uuid() change à CHAQUE apply!
  # Utiliser avec lifecycle.ignore_changes
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # uuidv5() - UUID v5 (déterministe)
  # ═══════════════════════════════════════════════════════════════════════
  
  namespace = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"  # DNS namespace
  name = "example.com"
  deterministic_uuid = uuidv5(local.namespace, local.name)
  # Même namespace + name = même UUID (reproductible)
}
```


(Suite dans le prochain message...)

# Fichier: python_cheats/cheatsheets/terraform_part4_suite.txt
# Terraform - PARTIE 4 (Suite): Conditions, Boucles, Dynamic Blocks
# Guide Ultra-Détaillé


═══════════════════════════════════════════════════════════════════════════════
[MELANGE] PARTIE 4.4: CONDITIONS ET TERNAIRES - LOGIQUE CONDITIONNELLE
═══════════════════════════════════════════════════════════════════════════════

┌───────────────────────────────────────────────────────────────────────────┐
│ OPÉRATEUR TERNAIRE - BASE                                                │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# Syntaxe: CONDITION ? VALEUR_SI_VRAI : VALEUR_SI_FAUX

locals {
  # ═══════════════════════════════════════════════════════════════════════
  # EXEMPLE SIMPLE
  # ═══════════════════════════════════════════════════════════════════════
  
  environment = "prod"
  instance_type = local.environment == "prod" ? "t3.large" : "t2.micro"
  # Résultat: "t3.large"
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # CONDITIONS MULTIPLES (NESTED)
  # ═══════════════════════════════════════════════════════════════════════
  
  instance_type = (
    var.environment == "prod" ? "t3.large" :
    var.environment == "staging" ? "t2.medium" :
    var.environment == "test" ? "t2.small" :
    "t2.micro"  # default
  )
  
  
  # ═══════════════════════════════════════════════════════════════════════
  # AVEC OPÉRATEURS LOGIQUES
  # ═══════════════════════════════════════════════════════════════════════
  
  # AND
  enable_backup = (var.environment == "prod" && var.backup_enabled) ? true : false
  
  # OR
  enable_monitoring = (var.environment == "prod" || var.environment == "staging") ? true : false
  
  # Combinaison complexe
  scale_up = (
    (var.environment == "prod" && var.load > 0.8) ||
    var.force_scale == true
  ) ? var.instance_count * 2 : var.instance_count
}

# ═══════════════════════════════════════════════════════════════════════
# UTILISATION DANS RESOURCES
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" {
  ami = var.environment == "prod" ? var.prod_ami : var.dev_ami
  
  instance_type = var.high_performance ? "t3.2xlarge" : "t2.micro"
  
  # Monitoring détaillé seulement si demandé
  monitoring = var.enable_detailed_monitoring ? true : false
  
  # Associate public IP seulement si public subnet
  associate_public_ip_address = var.subnet_type == "public" ? true : false
  
  # EBS optimized pour types large
  ebs_optimized = contains(["t3.large", "t3.xlarge"], var.instance_type) ? true : false
  
  tags = merge(
    var.common_tags,
    {
      Name = var.environment == "prod" ? "prod-web-server" : "dev-web-server"
      Tier = var.is_public ? "public" : "private"
    }
  )
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ CONDITIONS AVEC count                                                     │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# ═══════════════════════════════════════════════════════════════════════
# CRÉER RESSOURCE CONDITIONNELLEMENT
# ═══════════════════════════════════════════════════════════════════════

# Pattern: count = CONDITION ? 1 : 0

resource "aws_eip" "web" {
  count = var.assign_eip ? 1 : 0
  # Si assign_eip = true -> crée 1 EIP
  # Si assign_eip = false -> crée 0 EIP (ressource n'existe pas)
  
  instance = aws_instance.web.id
  domain   = "vpc"
}

resource "aws_db_instance" "replica" {
  count = var.enable_read_replica ? 1 : 0
  
  replicate_source_db = aws_db_instance.main.id
  instance_class      = "db.t3.micro"
}

# Référencer ressource conditionnelle:
output "elastic_ip" {
  value = var.assign_eip ? aws_eip.web[0].public_ip : null
  # Si EIP existe -> retourne IP
  # Sinon -> retourne null
}


# ═══════════════════════════════════════════════════════════════════════
# NOMBRE VARIABLE DE RESSOURCES
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" {
  count = var.environment == "prod" ? 3 : 1
  # Prod -> 3 instances
  # Dev -> 1 instance
  
  ami           = var.ami_id
  instance_type = "t2.micro"
  
  tags = {
    Name = "${var.environment}-web-${count.index + 1}"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# CONDITIONS COMPLEXES
# ═══════════════════════════════════════════════════════════════════════

resource "aws_cloudwatch_metric_alarm" "cpu" {
  count = (
    var.enable_monitoring &&
    (var.environment == "prod" || var.environment == "staging")
  ) ? var.instance_count : 0
  
  alarm_name = "cpu-utilization-${count.index}"
  # ... configuration alarm
}
```


┌───────────────────────────────────────────────────────────────────────────┐
│ PATTERNS CONDITIONNELS AVANCÉS                                           │
└───────────────────────────────────────────────────────────────────────────┘

```hcl
# ═══════════════════════════════════════════════════════════════════════
# PATTERN 1: LISTE CONDITIONNELLE
# ═══════════════════════════════════════════════════════════════════════

locals {
  # Ajouter élément seulement si condition
  security_group_ids = concat(
    [aws_security_group.base.id],
    var.enable_ssh ? [aws_security_group.ssh[0].id] : [],
    var.enable_https ? [aws_security_group.https[0].id] : []
  )
}

resource "aws_instance" "web" {
  vpc_security_group_ids = local.security_group_ids
}


# ═══════════════════════════════════════════════════════════════════════
# PATTERN 2: MAP CONDITIONNEL
# ═══════════════════════════════════════════════════════════════════════

locals {
  # Tags conditionnels
  all_tags = merge(
    var.common_tags,
    var.environment == "prod" ? {
      Backup     = "daily"
      Monitoring = "enabled"
    } : {},
    var.compliance_required ? {
      Compliance = "yes"
      AuditLevel = "high"
    } : {}
  )
}


# ═══════════════════════════════════════════════════════════════════════
# PATTERN 3: VALEUR PAR DÉFAUT AVEC try()
# ═══════════════════════════════════════════════════════════════════════

locals {
  # try() pour gérer valeurs potentiellement nulles
  instance_type = try(
    var.instance_type_override,
    local.environment_configs[var.environment].instance_type,
    "t2.micro"  # ultimate fallback
  )
  
  # can() pour vérifier validité
  port = can(tonumber(var.port)) ? tonumber(var.port) : 8080
}


# ═══════════════════════════════════════════════════════════════════════
# PATTERN 4: NULL COMME ABSENCE
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" {
  # key_name = null -> attribut absent (comme si non spécifié)
  key_name = var.enable_ssh ? var.ssh_key_name : null
  
  # iam_instance_profile = null -> pas de profile IAM
  iam_instance_profile = var.attach_iam_role ? aws_iam_instance_profile.app[0].name : null
}


# ═══════════════════════════════════════════════════════════════════════
# PATTERN 5: ONE_OF (choix parmi options)
# ═══════════════════════════════════════════════════════════════════════

locals {
  # Choisir AMI selon provider ou custom
  ami = (
    var.custom_ami != null ? var.custom_ami :
    var.use_latest ? data.aws_ami.latest.id :
    var.ami_id
  )
}
```


═══════════════════════════════════════════════════════════════════════════════
[SYNC] PARTIE 4.5: BOUCLES - for, for_each, count
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ count - BOUCLE SIMPLE PAR INDEX                                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# UTILISATION BASIQUE
# ═══════════════════════════════════════════════════════════════════════

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

# Référencer les instances créées
output "instance_ids" {
  value = aws_instance.web[*].id
  # Splat: récupère tous les IDs
  # ["i-123", "i-456", "i-789"]
}

output "first_instance_ip" {
  value = aws_instance.web[0].public_ip
  # Accès par index
}


# ═══════════════════════════════════════════════════════════════════════
# count AVEC LISTE
# ═══════════════════════════════════════════════════════════════════════

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-${var.availability_zones[count.index]}"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# count CONDITIONNEL
# ═══════════════════════════════════════════════════════════════════════

resource "aws_eip" "nat" {
  count = var.enable_nat ? var.az_count : 0
  # Si enable_nat = true -> crée az_count EIPs
  # Si enable_nat = false -> ne crée rien
  
  domain = "vpc"
  
  tags = {
    Name = "nat-eip-${count.index + 1}"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# LIMITES DE count
# ═══════════════════════════════════════════════════════════════════════

# [X] PROBLÈME: Si liste change, tout est recréé!

variable "servers" {
  default = ["web", "api", "worker"]
}

resource "aws_instance" "app" {
  count = length(var.servers)
  
  tags = {
    Name = var.servers[count.index]
  }
}

# Si on retire "api" de la liste:
# ["web", "api", "worker"] -> ["web", "worker"]
# 
# Terraform fait:
# - app[0] reste (web)
# - app[1] détruit et recrée (api -> worker)  <- MAUVAIS!
# - app[2] détruit (worker n'existe plus)
#
# Solution: utiliser for_each à la place
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ for_each - BOUCLE PAR CLÉ/VALEUR                                          ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# AVEC SET (toset)
# ═══════════════════════════════════════════════════════════════════════

variable "servers" {
  default = ["web", "api", "worker"]
}

resource "aws_instance" "app" {
  for_each = toset(var.servers)
  # Convertit liste en set
  
  ami           = var.ami_id
  instance_type = "t2.micro"
  
  tags = {
    Name = each.key  # "web", "api", "worker"
    # each.key = each.value (pour un set)
  }
}

# Référencer
output "web_instance_id" {
  value = aws_instance.app["web"].id
  # Accès par clé (pas index!)
}

output "all_instance_ids" {
  value = values(aws_instance.app)[*].id
  # values() extrait toutes les instances
}


# ═══════════════════════════════════════════════════════════════════════
# AVEC MAP
# ═══════════════════════════════════════════════════════════════════════

variable "instance_configs" {
  default = {
    web = {
      instance_type = "t2.small"
      port          = 80
    }
    api = {
      instance_type = "t2.medium"
      port          = 8080
    }
    worker = {
      instance_type = "t2.micro"
      port          = 0
    }
  }
}

resource "aws_instance" "app" {
  for_each = var.instance_configs
  
  ami           = var.ami_id
  instance_type = each.value.instance_type
  # each.key = "web", "api", "worker"
  # each.value = le map complet pour cette clé
  
  tags = {
    Name = each.key
    Type = each.value.instance_type
    Port = each.value.port
  }
}


# ═══════════════════════════════════════════════════════════════════════
# FILTRAGE AVEC for_each
# ═══════════════════════════════════════════════════════════════════════

variable "all_subnets" {
  default = {
    public-a  = { cidr = "10.0.1.0/24", public = true }
    public-b  = { cidr = "10.0.2.0/24", public = true }
    private-a = { cidr = "10.0.10.0/24", public = false }
    private-b = { cidr = "10.0.11.0/24", public = false }
  }
}

# Créer route seulement pour subnets publics
resource "aws_route" "public_internet" {
  for_each = {
    for name, subnet in var.all_subnets :
    name => subnet
    if subnet.public  # <- FILTRE
  }
  
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.main.id
}


# ═══════════════════════════════════════════════════════════════════════
# AVANTAGES DE for_each vs count
# ═══════════════════════════════════════════════════════════════════════

# [OK] Stable: retirer un élément ne recrée pas les autres
# [OK] Lisible: accès par nom (app["web"]) vs index (app[0])
# [OK] Flexible: filtrage facile avec if

# Exemple: retirer "api"
# ["web", "api", "worker"] -> ["web", "worker"]
# 
# Terraform fait:
# - app["web"] reste
# - app["api"] détruit  <- SEULEMENT "api"!
# - app["worker"] reste
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ FOR EXPRESSION - TRANSFORMER COLLECTIONS                                  ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# FOR AVEC LISTE -> LISTE
# ═══════════════════════════════════════════════════════════════════════

locals {
  names = ["alice", "bob", "charlie"]
  
  # Transformer chaque élément
  uppercase_names = [
    for name in local.names : upper(name)
  ]
  # ["ALICE", "BOB", "CHARLIE"]
  
  # Avec condition (filtrage)
  short_names = [
    for name in local.names : name
    if length(name) <= 5
  ]
  # ["alice", "bob"]
  
  # Expression complexe
  formatted_names = [
    for name in local.names :
    "User: ${upper(name)}"
  ]
  # ["User: ALICE", "User: BOB", "User: CHARLIE"]
}


# ═══════════════════════════════════════════════════════════════════════
# FOR AVEC LISTE -> MAP
# ═══════════════════════════════════════════════════════════════════════

locals {
  servers = ["web", "api", "worker"]
  
  # Créer map depuis liste
  server_ports = {
    for server in local.servers :
    server => 8080
  }
  # {
  #   web    = 8080
  #   api    = 8080
  #   worker = 8080
  # }
  
  # Map avec valeurs calculées
  server_names = {
    for idx, server in local.servers :
    server => "server-${idx + 1}"
  }
  # {
  #   web    = "server-1"
  #   api    = "server-2"
  #   worker = "server-3"
  # }
}


# ═══════════════════════════════════════════════════════════════════════
# FOR AVEC MAP -> LISTE
# ═══════════════════════════════════════════════════════════════════════

locals {
  instances = {
    web    = { ip = "10.0.1.10", port = 80 }
    api    = { ip = "10.0.1.20", port = 8080 }
    worker = { ip = "10.0.1.30", port = 9000 }
  }
  
  # Extraire toutes les IPs
  all_ips = [
    for name, config in local.instances : config.ip
  ]
  # ["10.0.1.10", "10.0.1.20", "10.0.1.30"]
  
  # Créer connection strings
  connection_strings = [
    for name, config in local.instances :
    "${name}: ${config.ip}:${config.port}"
  ]
  # ["web: 10.0.1.10:80", "api: 10.0.1.20:8080", ...]
}


# ═══════════════════════════════════════════════════════════════════════
# FOR AVEC MAP -> MAP
# ═══════════════════════════════════════════════════════════════════════

locals {
  raw_config = {
    web    = { size = "small", region = "us-east-1" }
    api    = { size = "medium", region = "us-west-2" }
    worker = { size = "large", region = "eu-west-1" }
  }
  
  # Transformer map
  processed_config = {
    for name, config in local.raw_config :
    name => {
      instance_type = config.size == "small" ? "t2.micro" : (
        config.size == "medium" ? "t2.small" : "t2.medium"
      )
      availability_zone = "${config.region}a"
    }
  }
}


# ═══════════════════════════════════════════════════════════════════════
# FILTRAGE AVANCÉ
# ═══════════════════════════════════════════════════════════════════════

locals {
  all_instances = {
    web-1    = { type = "web", env = "prod", enabled = true }
    web-2    = { type = "web", env = "dev", enabled = true }
    api-1    = { type = "api", env = "prod", enabled = false }
    worker-1 = { type = "worker", env = "prod", enabled = true }
  }
  
  # Filtrer instances prod ET enabled
  prod_enabled = {
    for name, config in local.all_instances :
    name => config
    if config.env == "prod" && config.enabled
  }
  # {
  #   web-1    = { type = "web", env = "prod", enabled = true }
  #   worker-1 = { type = "worker", env = "prod", enabled = true }
  # }
  
  # Filtrer par type
  web_instances = {
    for name, config in local.all_instances :
    name => config
    if config.type == "web"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# FLATTEN - APLATIR LISTES IMBRIQUÉES
# ═══════════════════════════════════════════════════════════════════════

locals {
  apps = {
    frontend = {
      servers = ["web-1", "web-2"]
      ports   = [80, 443]
    }
    backend = {
      servers = ["api-1", "api-2", "api-3"]
      ports   = [8080, 8443]
    }
  }
  
  # Créer règle pour chaque combinaison serveur + port
  security_rules = flatten([
    for app_name, app_config in local.apps : [
      for server in app_config.servers : [
        for port in app_config.ports : {
          app    = app_name
          server = server
          port   = port
        }
      ]
    ]
  ])
  # [
  #   { app = "frontend", server = "web-1", port = 80 },
  #   { app = "frontend", server = "web-1", port = 443 },
  #   { app = "frontend", server = "web-2", port = 80 },
  #   { app = "frontend", server = "web-2", port = 443 },
  #   { app = "backend", server = "api-1", port = 8080 },
  #   { app = "backend", server = "api-1", port = 8443 },
  #   ...
  # ]
}

# Utilisation avec for_each
resource "aws_security_group_rule" "app" {
  for_each = {
    for idx, rule in local.security_rules :
    "${rule.app}-${rule.server}-${rule.port}" => rule
  }
  
  type        = "ingress"
  from_port   = each.value.port
  to_port     = each.value.port
  protocol    = "tcp"
  description = "${each.value.app} - ${each.value.server}"
}
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ 4.6 DYNAMIC BLOCKS - BLOCS RÉPÉTITIFS                                     ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# PROBLÈME SANS DYNAMIC
# ═══════════════════════════════════════════════════════════════════════

# [X] RÉPÉTITION (pas DRY)
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 = ["10.0.0.0/8"]
  }
  
  # ... et si 20 ports?
}


# ═══════════════════════════════════════════════════════════════════════
# SOLUTION: DYNAMIC BLOCK
# ═══════════════════════════════════════════════════════════════════════

variable "ingress_rules" {
  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 = ["10.0.0.0/8"]
    }
  ]
}

resource "aws_security_group" "web" {
  name = "web-sg"
  
  # [OK] Dynamic block
  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
    }
  }
}


# ═══════════════════════════════════════════════════════════════════════
# SYNTAXE DYNAMIC
# ═══════════════════════════════════════════════════════════════════════

dynamic "BLOCK_NAME" {
  for_each = COLLECTION
  
  # Optionnel: renommer iterator
  iterator = CUSTOM_NAME
  
  content {
    # Utiliser BLOCK_NAME.value ou CUSTOM_NAME.value
    argument = BLOCK_NAME.value.field
  }
}


# ═══════════════════════════════════════════════════════════════════════
# AVEC ITERATOR CUSTOM
# ═══════════════════════════════════════════════════════════════════════

resource "aws_security_group" "web" {
  name = "web-sg"
  
  dynamic "ingress" {
    for_each = var.ingress_rules
    iterator = rule  # <- Nom custom
    
    content {
      description = rule.value.description  # <- Utilise "rule"
      from_port   = rule.value.from_port
      to_port     = rule.value.to_port
      protocol    = rule.value.protocol
      cidr_blocks = rule.value.cidr_blocks
    }
  }
}


# ═══════════════════════════════════════════════════════════════════════
# DYNAMIC CONDITIONNEL
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t2.micro"
  
  # Bloc ebs_block_device seulement si var.attach_volume = true
  dynamic "ebs_block_device" {
    for_each = var.attach_volume ? [1] : []
    # Si true -> for_each = [1] -> crée le bloc
    # Si false -> for_each = [] -> ne crée pas le bloc
    
    content {
      device_name = "/dev/sdf"
      volume_size = var.volume_size
      volume_type = "gp3"
    }
  }
}


# ═══════════════════════════════════════════════════════════════════════
# DYNAMIC IMBRIQUÉS
# ═══════════════════════════════════════════════════════════════════════

variable "load_balancer_config" {
  default = {
    listeners = [
      {
        port     = 80
        protocol = "HTTP"
        
        default_actions = [
          {
            type             = "forward"
            target_group_arn = "arn:aws:elasticloadbalancing:..."
          }
        ]
      },
      {
        port     = 443
        protocol = "HTTPS"
        
        default_actions = [
          {
            type             = "forward"
            target_group_arn = "arn:aws:elasticloadbalancing:..."
          },
          {
            type = "authenticate-cognito"
            user_pool_arn = "arn:aws:cognito:..."
          }
        ]
      }
    ]
  }
}

resource "aws_lb" "main" {
  name = "main-lb"
  
  # Dynamic niveau 1: listeners
  dynamic "listener" {
    for_each = var.load_balancer_config.listeners
    
    content {
      port     = listener.value.port
      protocol = listener.value.protocol
      
      # Dynamic niveau 2: actions par listener
      dynamic "default_action" {
        for_each = listener.value.default_actions
        
        content {
          type             = default_action.value.type
          target_group_arn = lookup(default_action.value, "target_group_arn", null)
          
          # Encore un niveau si authentification
          dynamic "authenticate_cognito" {
            for_each = default_action.value.type == "authenticate-cognito" ? [1] : []
            
            content {
              user_pool_arn = default_action.value.user_pool_arn
            }
          }
        }
      }
    }
  }
}


# ═══════════════════════════════════════════════════════════════════════
# EXEMPLE COMPLET: ALB AVEC DYNAMIC
# ═══════════════════════════════════════════════════════════════════════

variable "alb_listeners" {
  default = [
    {
      port            = 80
      protocol        = "HTTP"
      ssl_policy      = null
      certificate_arn = null
    },
    {
      port            = 443
      protocol        = "HTTPS"
      ssl_policy      = "ELBSecurityPolicy-TLS-1-2-2017-01"
      certificate_arn = "arn:aws:acm:us-east-1:..."
    }
  ]
}

resource "aws_lb_listener" "main" {
  for_each = {
    for idx, listener in var.alb_listeners :
    listener.port => listener
  }
  
  load_balancer_arn = aws_lb.main.arn
  port              = each.value.port
  protocol          = each.value.protocol
  ssl_policy        = each.value.ssl_policy
  certificate_arn   = each.value.certificate_arn
  
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.main.arn
  }
}
```


(Suite dans le message final...)

# Fichier: python_cheats/cheatsheets/terraform_part4_final.txt
# Terraform - PARTIE 4 (Final): Templates et Heredoc
# Guide Ultra-Détaillé


═══════════════════════════════════════════════════════════════════════════════
[FICHIER] PARTIE 4.7: TEMPLATES ET HEREDOC - GÉNÉRATION DE CONTENU
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ HEREDOC - MULTI-LIGNES BASIQUES                                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# SYNTAXE HEREDOC
# ═══════════════════════════════════════════════════════════════════════

locals {
  # <<-EOT ... EOT
  # Le "<<-" permet l'indentation
  
  simple_text = <<-EOT
    Ceci est un texte
    sur plusieurs lignes.
    Les espaces initiaux sont préservés.
  EOT
  
  # Résultat:
  # "  Ceci est un texte\n  sur plusieurs lignes.\n  Les espaces initiaux sont préservés.\n"
  
  
  # << (sans tiret) = stricte (pas d'indentation)
  strict_text = <<EOT
Texte sans indentation.
Doit commencer à la colonne 0.
EOT
  
  
  # Nom du delimiter (EOT) peut être n'importe quoi
  custom_delimiter = <<-END_OF_SCRIPT
    Script content here
  END_OF_SCRIPT
}


# ═══════════════════════════════════════════════════════════════════════
# INTERPOLATION DANS HEREDOC
# ═══════════════════════════════════════════════════════════════════════

locals {
  environment = "production"
  server_name = "web-01"
  
  script = <<-EOT
    #!/bin/bash
    # Configuration for ${local.environment}
    
    HOSTNAME="${local.server_name}"
    ENV="${local.environment}"
    
    echo "Configuring $HOSTNAME in $ENV environment"
    
    # Install packages
    apt-get update
    apt-get install -y nginx
  EOT
}


# ═══════════════════════════════════════════════════════════════════════
# USER DATA EC2
# ═══════════════════════════════════════════════════════════════════════

resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t2.micro"
  
  user_data = <<-EOT
    #!/bin/bash
    set -e
    
    # Variables
    HOSTNAME="${var.server_name}"
    DOMAIN="${var.domain_name}"
    
    # Update system
    apt-get update && apt-get upgrade -y
    
    # Install nginx
    apt-get install -y nginx
    
    # Configure nginx
    cat > /etc/nginx/sites-available/default <<'EOF'
    server {
        listen 80;
        server_name $HOSTNAME.$DOMAIN;
        
        location / {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
        }
    }
    EOF
    
    # Start nginx
    systemctl enable nginx
    systemctl start nginx
    
    echo "Setup complete for $HOSTNAME.$DOMAIN" > /var/log/setup.log
  EOT
}
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ DIRECTIVES DANS HEREDOC - LOGIQUE AVANCÉE                                 ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# %{ IF } ... %{ ENDIF } - CONDITIONNEL
# ═══════════════════════════════════════════════════════════════════════

locals {
  enable_ssl = true
  enable_monitoring = false
  
  config = <<-EOT
    # Configuration file
    
    %{ if local.enable_ssl }
    ssl_certificate /etc/ssl/cert.pem;
    ssl_certificate_key /etc/ssl/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    %{ endif }
    
    %{ if local.enable_monitoring }
    monitoring_enabled = true;
    metrics_port = 9090;
    %{ endif }
    
    %{ if !local.enable_monitoring }
    # Monitoring is disabled
    %{ endif }
  EOT
}

# Résultat:
# # Configuration file
# 
# ssl_certificate /etc/ssl/cert.pem;
# ssl_certificate_key /etc/ssl/key.pem;
# ssl_protocols TLSv1.2 TLSv1.3;
# 
# # Monitoring is disabled


# ═══════════════════════════════════════════════════════════════════════
# %{ IF } ... %{ ELSE } ... %{ ENDIF }
# ═══════════════════════════════════════════════════════════════════════

locals {
  environment = "prod"
  
  script = <<-EOT
    #!/bin/bash
    
    %{ if local.environment == "prod" }
    # Production configuration
    LOG_LEVEL=warning
    WORKERS=10
    %{ else }
    # Development configuration
    LOG_LEVEL=debug
    WORKERS=2
    %{ endif }
  EOT
}


# ═══════════════════════════════════════════════════════════════════════
# %{ FOR } ... %{ ENDFOR } - BOUCLE
# ═══════════════════════════════════════════════════════════════════════

locals {
  servers = ["web-1", "web-2", "web-3"]
  
  hosts_file = <<-EOT
    # /etc/hosts
    
    127.0.0.1 localhost
    
    # Application servers
    %{ for server in local.servers ~}
    10.0.1.${index(local.servers, server) + 10} ${server}.example.com ${server}
    %{ endfor ~}
  EOT
}

# Résultat:
# # /etc/hosts
# 
# 127.0.0.1 localhost
# 
# # Application servers
# 10.0.1.10 web-1.example.com web-1
# 10.0.1.11 web-2.example.com web-2
# 10.0.1.12 web-3.example.com web-3


# ═══════════════════════════════════════════════════════════════════════
# STRIP MARKERS (~) - SUPPRIMER ESPACES/NEWLINES
# ═══════════════════════════════════════════════════════════════════════

locals {
  # Sans ~
  without_strip = <<-EOT
    Lines:
    %{ for i in [1, 2, 3] }
    - Item ${i}
    %{ endfor }
    Done
  EOT
  # Résultat:
  # Lines:
  # 
  # - Item 1
  # 
  # - Item 2
  # 
  # - Item 3
  # 
  # Done
  
  
  # Avec ~
  with_strip = <<-EOT
    Lines:
    %{ for i in [1, 2, 3] ~}
    - Item ${i}
    %{ endfor ~}
    Done
  EOT
  # Résultat:
  # Lines:
  # - Item 1
  # - Item 2
  # - Item 3
  # Done
}

# ~ à gauche  (%{~ ) = supprime espaces avant
# ~ à droite ( ~%}) = supprime espaces après


# ═══════════════════════════════════════════════════════════════════════
# COMBINAISONS COMPLEXES
# ═══════════════════════════════════════════════════════════════════════

variable "services" {
  default = [
    { name = "web", port = 80, ssl = true },
    { name = "api", port = 8080, ssl = true },
    { name = "admin", port = 3000, ssl = false }
  ]
}

locals {
  nginx_config = <<-EOT
    # Nginx configuration
    
    %{ for service in var.services ~}
    # ${service.name} service
    server {
        listen ${service.port}%{ if service.ssl } ssl%{ endif };
        server_name ${service.name}.example.com;
        
        %{ if service.ssl ~}
        ssl_certificate /etc/ssl/${service.name}/cert.pem;
        ssl_certificate_key /etc/ssl/${service.name}/key.pem;
        %{ endif ~}
        
        location / {
            proxy_pass http://localhost:${service.port + 1000};
            proxy_set_header Host $host;
        }
    }
    
    %{ endfor ~}
  EOT
}

# Résultat:
# # Nginx configuration
# 
# # web service
# server {
#     listen 80 ssl;
#     server_name web.example.com;
#     
#     ssl_certificate /etc/ssl/web/cert.pem;
#     ssl_certificate_key /etc/ssl/web/key.pem;
#     
#     location / {
#         proxy_pass http://localhost:1080;
#         proxy_set_header Host $host;
#     }
# }
# 
# # api service
# ...
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ TEMPLATEFILE() - FICHIERS TEMPLATES EXTERNES                              ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# SYNTAXE templatefile()
# ═══════════════════════════════════════════════════════════════════════

# templatefile(path, vars)
# - path: chemin vers le fichier template
# - vars: map de variables à injecter

locals {
  rendered = templatefile("${path.module}/template.tpl", {
    environment = var.environment
    hostname    = var.hostname
    ports       = var.ports
  })
}


# ═══════════════════════════════════════════════════════════════════════
# EXEMPLE: USER DATA DEPUIS FICHIER
# ═══════════════════════════════════════════════════════════════════════

# templates/user-data.sh.tpl
# ──────────────────────────
#!/bin/bash
set -e

# Configuration for ${environment}
HOSTNAME="${hostname}"
DOMAIN="${domain}"

# Install packages
apt-get update
%{ for package in packages ~}
apt-get install -y ${package}
%{ endfor ~}

# Configure application
cat > /etc/app/config.json <<'EOF'
{
  "environment": "${environment}",
  "hostname": "${hostname}",
  "database": {
    "host": "${db_host}",
    "port": ${db_port}
  }
}
EOF

echo "Setup complete" > /var/log/setup.log


# main.tf
# ───────
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t2.micro"
  
  user_data = templatefile("${path.module}/templates/user-data.sh.tpl", {
    environment = var.environment
    hostname    = var.hostname
    domain      = var.domain
    packages    = ["nginx", "nodejs", "git"]
    db_host     = aws_db_instance.main.address
    db_port     = aws_db_instance.main.port
  })
}


# ═══════════════════════════════════════════════════════════════════════
# EXEMPLE: GÉNÉRATION CONFIG NGINX
# ═══════════════════════════════════════════════════════════════════════

# templates/nginx.conf.tpl
# ────────────────────────
# Nginx configuration for ${app_name}

upstream backend {
    %{ for server in backend_servers ~}
    server ${server.ip}:${server.port} weight=${server.weight};
    %{ endfor ~}
}

server {
    listen 80;
    server_name ${domain};
    
    %{ if enable_ssl ~}
    listen 443 ssl;
    ssl_certificate ${ssl_cert_path};
    ssl_certificate_key ${ssl_key_path};
    %{ endif ~}
    
    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        %{ if enable_caching ~}
        proxy_cache cache_zone;
        proxy_cache_valid 200 ${cache_ttl}m;
        %{ endif ~}
    }
    
    %{ for location in custom_locations ~}
    location ${location.path} {
        ${location.config}
    }
    %{ endfor ~}
}


# main.tf
# ───────
resource "local_file" "nginx_config" {
  filename = "/etc/nginx/sites-available/${var.app_name}.conf"
  
  content = templatefile("${path.module}/templates/nginx.conf.tpl", {
    app_name = var.app_name
    domain   = var.domain
    
    backend_servers = [
      { ip = "10.0.1.10", port = 8080, weight = 3 },
      { ip = "10.0.1.11", port = 8080, weight = 2 },
      { ip = "10.0.1.12", port = 8080, weight = 1 }
    ]
    
    enable_ssl    = true
    ssl_cert_path = "/etc/ssl/cert.pem"
    ssl_key_path  = "/etc/ssl/key.pem"
    
    enable_caching = var.environment == "prod"
    cache_ttl      = 60
    
    custom_locations = [
      {
        path   = "/api"
        config = "proxy_pass http://api_backend;"
      },
      {
        path   = "/static"
        config = "alias /var/www/static/;"
      }
    ]
  })
}


# ═══════════════════════════════════════════════════════════════════════
# EXEMPLE: KUBERNETES YAML
# ═══════════════════════════════════════════════════════════════════════

# templates/deployment.yaml.tpl
# ──────────────────────────────
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${app_name}
  namespace: ${namespace}
  labels:
    app: ${app_name}
    environment: ${environment}
spec:
  replicas: ${replica_count}
  selector:
    matchLabels:
      app: ${app_name}
  template:
    metadata:
      labels:
        app: ${app_name}
        version: ${app_version}
    spec:
      containers:
      - name: ${app_name}
        image: ${docker_image}:${app_version}
        ports:
        %{ for port in container_ports ~}
        - containerPort: ${port.port}
          protocol: ${port.protocol}
        %{ endfor ~}
        env:
        %{ for key, value in environment_vars ~}
        - name: ${key}
          value: "${value}"
        %{ endfor ~}
        resources:
          requests:
            memory: "${memory_request}"
            cpu: "${cpu_request}"
          limits:
            memory: "${memory_limit}"
            cpu: "${cpu_limit}"


# main.tf
# ───────
resource "local_file" "k8s_deployment" {
  filename = "${path.module}/manifests/deployment.yaml"
  
  content = templatefile("${path.module}/templates/deployment.yaml.tpl", {
    app_name      = "my-app"
    namespace     = "production"
    environment   = "prod"
    replica_count = 3
    app_version   = "1.0.0"
    docker_image  = "myregistry/my-app"
    
    container_ports = [
      { port = 8080, protocol = "TCP" },
      { port = 9090, protocol = "TCP" }
    ]
    
    environment_vars = {
      NODE_ENV    = "production"
      LOG_LEVEL   = "info"
      DATABASE_URL = aws_db_instance.main.endpoint
    }
    
    memory_request = "512Mi"
    memory_limit   = "1Gi"
    cpu_request    = "250m"
    cpu_limit      = "500m"
  })
}


# ═══════════════════════════════════════════════════════════════════════
# EXEMPLE: IAM POLICY JSON
# ═══════════════════════════════════════════════════════════════════════

# templates/iam-policy.json.tpl
# ──────────────────────────────
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        %{ for action in s3_actions ~}
        "s3:${action}"%{ if action != s3_actions[length(s3_actions) - 1] },%{ endif }
        %{ endfor ~}
      ],
      "Resource": [
        %{ for bucket in s3_buckets ~}
        "arn:aws:s3:::${bucket}",
        "arn:aws:s3:::${bucket}/*"%{ if bucket != s3_buckets[length(s3_buckets) - 1] },%{ endif }
        %{ endfor ~}
      ]
    }%{ if enable_dynamodb },%{ endif }
    %{ if enable_dynamodb ~}
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:Query"
      ],
      "Resource": "arn:aws:dynamodb:${region}:${account_id}:table/${table_name}"
    }
    %{ endif ~}
  ]
}


# main.tf
# ───────
resource "aws_iam_policy" "app" {
  name = "app-policy"
  
  policy = templatefile("${path.module}/templates/iam-policy.json.tpl", {
    s3_actions = ["GetObject", "PutObject", "DeleteObject"]
    s3_buckets = ["my-app-assets", "my-app-uploads"]
    
    enable_dynamodb = true
    region          = data.aws_region.current.name
    account_id      = data.aws_caller_identity.current.account_id
    table_name      = aws_dynamodb_table.main.name
  })
}
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ BEST PRACTICES TEMPLATES                                                  ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# 1. ORGANISER LES TEMPLATES
# ═══════════════════════════════════════════════════════════════════════

project/
├── main.tf
├── templates/                   # Dossier templates
│   ├── user-data/
│   │   ├── web-server.sh.tpl
│   │   ├── api-server.sh.tpl
│   │   └── worker.sh.tpl
│   ├── configs/
│   │   ├── nginx.conf.tpl
│   │   ├── app.env.tpl
│   │   └── logging.yaml.tpl
│   └── kubernetes/
│       ├── deployment.yaml.tpl
│       ├── service.yaml.tpl
│       └── ingress.yaml.tpl
└── modules/


# ═══════════════════════════════════════════════════════════════════════
# 2. VALIDATION DES TEMPLATES
# ═══════════════════════════════════════════════════════════════════════

# Utiliser locals pour prévisualiser
locals {
  rendered_template = templatefile("${path.module}/template.tpl", {
    var1 = "value1"
  })
}

output "template_preview" {
  value = local.rendered_template
}


# ═══════════════════════════════════════════════════════════════════════
# 3. COMMENTAIRES DANS TEMPLATES
# ═══════════════════════════════════════════════════════════════════════

# template.tpl
# ────────────
%{~ /* 
  Multi-line comment
  Template for ${app_name}
  Variables: environment, ports, config
*/ ~%}

#!/bin/bash
# Configuration for ${environment}


# ═══════════════════════════════════════════════════════════════════════
# 4. ESCAPAGE DE DOLLAR SIGNS
# ═══════════════════════════════════════════════════════════════════════

# Si besoin de $ littéral (pas interpolation Terraform)
user_data = <<-EOT
  #!/bin/bash
  
  # Terraform interpole: ${var.hostname}
  # Bash interpole (pas Terraform): $${SHELL}
  # Résultat: SHELL=/bin/bash (pas interpolé par Terraform)
  
  echo "Hostname is ${var.hostname}"
  echo "Current shell is $${SHELL}"
EOT


# ═══════════════════════════════════════════════════════════════════════
# 5. FICHIERS SENSIBLES
# ═══════════════════════════════════════════════════════════════════════

# NE PAS mettre secrets en clair dans templates!

# [X] MAUVAIS
# template.tpl:
# DB_PASSWORD="${db_password}"  # En clair dans Git!

# [OK] BON: Utiliser secrets manager
locals {
  config = templatefile("${path.module}/template.tpl", {
    db_password_arn = aws_secretsmanager_secret.db.arn
  })
}

# template.tpl:
# DB_PASSWORD_ARN="${db_password_arn}"
# # Script récupère password depuis Secrets Manager


# ═══════════════════════════════════════════════════════════════════════
# 6. TESTER TEMPLATES LOCALEMENT
# ═══════════════════════════════════════════════════════════════════════

# test-template.tf
locals {
  test_render = templatefile("${path.module}/template.tpl", {
    # Variables de test
    environment = "test"
    hostname    = "test-server"
  })
}

resource "local_file" "test_output" {
  filename = "${path.module}/test-output.txt"
  content  = local.test_render
}

# terraform apply
# cat test-output.txt  # Vérifier le résultat
```


═══════════════════════════════════════════════════════════════════════════════
[COURS] RÉCAPITULATIF PARTIE 4 - HCL AVANCÉ
═══════════════════════════════════════════════════════════════════════════════

```
PARTIE 4: LANGAGE HCL AVANCÉ - CE QUE VOUS AVEZ APPRIS
══════════════════════════════════════════════════════════

[OK] 4.1 SYNTAXE HCL
   • Blocs, arguments, commentaires
   • Types primitifs et collections
   • Interpolation et références
   • Règles de syntaxe

[OK] 4.2 EXPRESSIONS ET OPÉRATEURS
   • Arithmétiques: +, -, *, /, %
   • Comparaison: ==, !=, >, <, >=, <=
   • Logiques: &&, ||, !
   • Ternaire: condition ? true : false
   • Splat: [*]

[OK] 4.3 FONCTIONS (50+)
   • String: upper, lower, replace, split, join...
   • Numeric: abs, ceil, floor, max, min...
   • Collection: length, concat, merge, flatten...
   • Date: timestamp, formatdate, timeadd
   • Network: cidrhost, cidrsubnet, cidrnetmask
   • File: file, templatefile, basename...
   • Type: can, try, type, tostring...
   • Encoding: base64encode, jsonencode, yamlencode...
   • Crypto: md5, sha256, uuid...

[OK] 4.4 CONDITIONS
   • Opérateur ternaire
   • Conditions avec count
   • Patterns conditionnels avancés

[OK] 4.5 BOUCLES
   • count: boucle par index
   • for_each: boucle par clé/valeur
   • for expression: transformer collections
   • Flatten: aplatir structures imbriquées

[OK] 4.6 DYNAMIC BLOCKS
   • Répéter blocs imbriqués
   • Syntaxe dynamic + content
   • Iterator custom
   • Dynamic conditionnels et imbriqués

[OK] 4.7 TEMPLATES
   • Heredoc: <<-EOT ... EOT
   • Interpolation: ${variable}
   • Directives: %{ if }, %{ for }
   • Strip markers: ~
   • templatefile(): fichiers externes
   • Best practices
```


**FIN PARTIE 4 - MAÎTRISE COMPLÈTE DU LANGAGE HCL! [BRAVO]**

Vous maîtrisez maintenant:
• Toute la syntaxe HCL
• Les 50+ fonctions essentielles
• La logique conditionnelle
• Les boucles (count, for_each, for)
• Les dynamic blocks
• Les templates avancés

**PROCHAINE PARTIE: terraform_part5_meta_arguments.txt**
• depends_on
• count
• for_each
• lifecycle
• provider
• provisioners

# Fichier: python_cheats/cheatsheets/terraform_part5_meta.txt
# Terraform - PARTIE 5: META-ARGUMENTS COMPLETS
# Guide Ultra-Détaillé


═══════════════════════════════════════════════════════════════════════════════
[CONFIG]  PARTIE 5: META-ARGUMENTS - CONTRÔLE AVANCÉ
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ RÉSUMÉ DES META-ARGUMENTS                                                 ║
╚═══════════════════════════════════════════════════════════════════════════╝

1. **depends_on**           -> Dépendances explicites
2. **count**                -> Créer N instances (index numérique)
3. **for_each**             -> Créer avec map/set (clés)  
4. **provider**             -> Choisir provider alias
5. **lifecycle**            -> Règles de cycle de vie
   - create_before_destroy
   - prevent_destroy
   - ignore_changes
   - replace_triggered_by
   - precondition / postcondition

╔═══════════════════════════════════════════════════════════════════════════╗
║ 5.1 DEPENDS_ON - ORDRE D'EXÉCUTION                                        ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# Exemple IAM : Lambda ne démarre qu'après policy attachée
resource "aws_iam_role" "lambda" {
  name = "lambda-role"
  assume_role_policy = jsonencode({ /* ... */ })
}

resource "aws_iam_role_policy_attachment" "lambda" {
  role       = aws_iam_role.lambda.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_lambda_function" "app" {
  function_name = "my-function"
  role          = aws_iam_role.lambda.arn
  # ...
  
  depends_on = [aws_iam_role_policy_attachment.lambda]
}
```

╔═══════════════════════════════════════════════════════════════════════════╗
║ 5.2 LIFECYCLE - CONTRÔLE COMPLET                                          ║
╚═══════════════════════════════════════════════════════════════════════════╝

EXEMPLE PRODUCTION DATABASE:

```hcl
resource "aws_db_instance" "prod" {
  identifier     = "prod-db"
  engine         = "postgres"
  instance_class = var.db_instance_class
  
  lifecycle {
    # Zero downtime updates
    create_before_destroy = true
    
    # Protection suppression
    prevent_destroy = true
    
    # Ignorer rotation password (gérée ailleurs)
    ignore_changes = [password]
    
    # Validation
    precondition {
      condition     = var.db_instance_class != "db.t2.micro"
      error_message = "Production nécessite au moins db.t3.small"
    }
    
    postcondition {
      condition     = self.storage_encrypted == true
      error_message = "DB doit être chiffrée"
    }
  }
}
```

╔═══════════════════════════════════════════════════════════════════════════╗
║ 5.3 COUNT VS FOR_EACH - COMPARAISON                                       ║
╚═══════════════════════════════════════════════════════════════════════════╝

COUNT (index numérique):
```hcl
resource "aws_instance" "web" {
  count = 3
  
  ami           = "ami-123"
  instance_type = "t2.micro"
  
  tags = { Name = "web-${count.index}" }  # web-0, web-1, web-2
}

# Accès: aws_instance.web[0].id
```

FOR_EACH (clés - recommandé):
```hcl
locals {
  servers = {
    web = { type = "t2.micro" }
    api = { type = "t2.small" }
  }
}

resource "aws_instance" "servers" {
  for_each = local.servers
  
  ami           = "ami-123"
  instance_type = each.value.type
  
  tags = { Name = each.key }  # web, api
}

# Accès: aws_instance.servers["web"].id
```

QUAND UTILISER QUOI?

[OK] **FOR_EACH** si:
- Noms stables (pas d'impact si suppression d'un élément)
- Configuration différente par ressource
- Besoin d'accès par clé

[OK] **COUNT** si:
- N instances identiques
- Activation/désactivation (count = var.enable ? 1 : 0)

╔═══════════════════════════════════════════════════════════════════════════╗
║ 5.4 PROVIDER - MULTI-RÉGIONS                                              ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
provider "aws" {
  region = "us-east-1"  # Défaut
}

provider "aws" {
  alias  = "eu"
  region = "eu-west-1"
}

# Instance US (provider par défaut)
resource "aws_instance" "us" {
  ami = "ami-us..."
}

# Instance EU (provider alias)
resource "aws_instance" "eu" {
  provider = aws.eu  # <- Spécifier alias
  ami      = "ami-eu..."
}
```

═══════════════════════════════════════════════════════════════════════════════
[LISTE] BEST PRACTICES META-ARGUMENTS
═══════════════════════════════════════════════════════════════════════════════

[OK] **depends_on**
- Utiliser seulement pour dépendances implicites
- Préférer références directes quand possible
- Nécessaire pour IAM, certificats SSL, propagation

[OK] **lifecycle**
- create_before_destroy: TOUJOURS pour zero downtime
- prevent_destroy: DB, buckets S3, KMS keys
- ignore_changes: Auto Scaling, tags externes
- precondition/postcondition: Validations critiques

[OK] **count vs for_each**
- Défaut: for_each (plus stable)
- count: activation/désactivation conditionnelle
- JAMAIS mélanger count et for_each sur même ressource

[OK] **provider**
- Multi-régions: disaster recovery
- Multi-comptes: dev/staging/prod séparés
- Nommer clairement les alias

[X] **À ÉVITER**
- Provisioners (sauf absolument nécessaire)
- Trop de depends_on (complexité)
- ignore_changes = all (perte de contrôle)
- count sans gestion suppression d'éléments

# Fichier: python_cheats/cheatsheets/terraform_part6_providers.txt
# Terraform - PARTIE 6: PROVIDERS EN PROFONDEUR
# Guide Ultra-Détaillé - Maîtriser TOUS les Providers Majeurs


═══════════════════════════════════════════════════════════════════════════════
[CLOUD] PARTIE 6: PROVIDERS EN PROFONDEUR
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ INTRODUCTION AUX PROVIDERS                                                ║
╚═══════════════════════════════════════════════════════════════════════════╝

DÉFINITION:
───────────

Un PROVIDER est le pont entre Terraform et une API externe.
Il traduit votre code HCL en appels API spécifiques au service.

ARCHITECTURE:
─────────────

```
┌──────────────────────────────────────────────────────────────────────┐
│                    TERRAFORM PROVIDER ARCHITECTURE                   │
└──────────────────────────────────────────────────────────────────────┘

    Terraform Core (Go)
           │
           v
    ┌──────────────┐
    │  gRPC/Plugin │  <- Communication protocol
    │  Interface   │
    └──────┬───────┘
           │
    ┌──────┴────────────────────────────────────────┐
    │                                                │
    v                                                v
┌─────────────┐                              ┌─────────────┐
│  Provider   │                              │  Provider   │
│  AWS (Go)   │                              │  GCP (Go)   │
└──────┬──────┘                              └──────┬──────┘
       │                                            │
       v                                            v
┌─────────────┐                              ┌─────────────┐
│  AWS SDK    │                              │  GCP SDK    │
│  (Golang)   │                              │  (Golang)   │
└──────┬──────┘                              └──────┬──────┘
       │                                            │
       v                                            v
┌─────────────┐                              ┌─────────────┐
│  AWS API    │                              │  GCP API    │
│  (REST)     │                              │  (REST)     │
└─────────────┘                              └─────────────┘
```

LIFECYCLE D'UN PROVIDER:
────────────────────────

```
1. TERRAFORM INIT
   v
   Télécharge provider depuis registry.terraform.io
   v
2. TERRAFORM PLAN
   v
   Provider authentifie et interroge API
   v
   Compare état désiré vs état actuel
   v
3. TERRAFORM APPLY
   v
   Provider exécute CREATE/UPDATE/DELETE via API
   v
   Met à jour le state
```


═══════════════════════════════════════════════════════════════════════════════
- PARTIE 6.1: PROVIDER AWS - GUIDE COMPLET
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ CONFIGURATION AWS PROVIDER - TOUTES LES OPTIONS                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# versions.tf
# ═══════════════════════════════════════════════════════════════════════

terraform {
  required_version = ">= 1.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"  # Version 5.x
    }
  }
}


# ═══════════════════════════════════════════════════════════════════════
# CONFIGURATION BASIQUE
# ═══════════════════════════════════════════════════════════════════════

provider "aws" {
  region = "us-east-1"
  
  # MÉTHODE 1: Profile AWS CLI (recommandé dev local)
  profile = "default"  # Lit ~/.aws/credentials
  
  # MÉTHODE 2: Shared credentials file
  shared_credentials_files = ["~/.aws/credentials"]
  
  # MÉTHODE 3: Variables d'environnement (recommandé CI/CD)
  # AWS_ACCESS_KEY_ID et AWS_SECRET_ACCESS_KEY
  # (ne pas hardcoder dans le code!)
  
  # MÉTHODE 4: IAM Role (recommandé production EC2/Lambda)
  # Automatique si exécuté sur EC2 avec IAM role attaché
}


# ═══════════════════════════════════════════════════════════════════════
# CONFIGURATION AVANCÉE
# ═══════════════════════════════════════════════════════════════════════

provider "aws" {
  region = var.aws_region
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ ASSUME ROLE (Multi-comptes, sécurité renforcée)                 │
  # └─────────────────────────────────────────────────────────────────┘
  
  assume_role {
    role_arn     = "arn:aws:iam::123456789012:role/TerraformRole"
    session_name = "terraform-session"
    external_id  = "unique-external-id"  # Sécurité additionnelle
    
    # Durée de la session (secondes)
    duration = "3600s"  # 1 heure
    
    # Tags pour la session
    tags = {
      Automation = "Terraform"
      Team       = "DevOps"
    }
  }
  
  # Assume role avec source identity
  assume_role_with_web_identity {
    role_arn                = "arn:aws:iam::123456789012:role/GitHubActionsRole"
    web_identity_token_file = "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
    session_name            = "github-actions-session"
  }
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ TAGS PAR DÉFAUT (appliqués à toutes les ressources)             │
  # └─────────────────────────────────────────────────────────────────┘
  
  default_tags {
    tags = {
      Environment = var.environment
      ManagedBy   = "Terraform"
      Project     = var.project_name
      Owner       = var.team_email
      CostCenter  = var.cost_center
      Terraform   = "true"
    }
  }
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ ENDPOINTS PERSONNALISÉS (LocalStack, Minio, etc.)               │
  # └─────────────────────────────────────────────────────────────────┘
  
  endpoints {
    s3  = "http://localhost:4566"  # LocalStack
    ec2 = "http://localhost:4566"
    rds = "http://localhost:4566"
  }
  
  # Désactiver vérification SSL (dev uniquement!)
  skip_credentials_validation = true
  skip_requesting_account_id  = true
  skip_metadata_api_check     = true
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ RETRY ET TIMEOUT                                                 │
  # └─────────────────────────────────────────────────────────────────┘
  
  max_retries = 3  # Nombre de tentatives en cas d'erreur API
  
  # HTTP Client configuration
  http_proxy = "http://proxy.example.com:8080"
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ RESTRICTIONS RÉGIONALES                                          │
  # └─────────────────────────────────────────────────────────────────┘
  
  allowed_account_ids = ["123456789012"]  # Whitelist comptes
  forbidden_account_ids = ["999999999999"]  # Blacklist comptes
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ CONFIGURATION S3                                                 │
  # └─────────────────────────────────────────────────────────────────┘
  
  s3_use_path_style           = false  # true pour LocalStack/Minio
  s3_force_path_style         = false
  skip_s3_checksum            = false
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ AUTRES OPTIONS                                                   │
  # └─────────────────────────────────────────────────────────────────┘
  
  # Ignorer tags spécifiques (utile pour tags auto-ajoutés par AWS)
  ignore_tags {
    keys = ["kubernetes.io/cluster/*"]
    key_prefixes = ["aws:", "Name"]
  }
  
  # Custom User-Agent
  custom_ca_bundle = "/path/to/ca-bundle.pem"
  
  # EC2 metadata service timeout
  ec2_metadata_service_endpoint_mode = "IPv4"  # ou "IPv6"
}


# ═══════════════════════════════════════════════════════════════════════
# MULTI-RÉGIONS AVEC ALIAS
# ═══════════════════════════════════════════════════════════════════════

# Provider par défaut (US East)
provider "aws" {
  region = "us-east-1"
}

# Provider Europe
provider "aws" {
  alias  = "eu"
  region = "eu-west-1"
  
  default_tags {
    tags = {
      Region = "Europe"
    }
  }
}

# Provider Asie
provider "aws" {
  alias  = "asia"
  region = "ap-southeast-1"
}

# Utilisation
resource "aws_instance" "us_server" {
  # Provider par défaut (us-east-1)
  ami           = "ami-us..."
  instance_type = "t2.micro"
}

resource "aws_instance" "eu_server" {
  provider = aws.eu  # <- Utilise provider EU
  ami      = "ami-eu..."
  instance_type = "t2.micro"
}


# ═══════════════════════════════════════════════════════════════════════
# MULTI-COMPTES AWS
# ═══════════════════════════════════════════════════════════════════════

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

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

# Utilisation
resource "aws_s3_bucket" "dev_assets" {
  provider = aws.dev
  bucket   = "dev-assets-bucket"
}

resource "aws_s3_bucket" "prod_assets" {
  provider = aws.prod
  bucket   = "prod-assets-bucket"
}
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ AUTHENTIFICATION AWS - TOUTES LES MÉTHODES                                ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 1: VARIABLES D'ENVIRONNEMENT (Recommandé CI/CD)
# ═══════════════════════════════════════════════════════════════════════

# Dans votre shell ou CI/CD:
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export AWS_DEFAULT_REGION="us-east-1"
export AWS_SESSION_TOKEN="IQoJb3JpZ2..."  # Si temporary credentials

# Terraform provider (minimal)
provider "aws" {
  region = "us-east-1"
  # Credentials automatiquement lues depuis env vars
}

# Avantages:
# [OK] Pas de credentials dans le code
# [OK] Facile à changer entre environnements
# [OK] Standard CI/CD
# 
# Inconvénients:
# [X] Doivent être configurées sur chaque machine
# [X] Pas de rotation automatique


# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 2: AWS CLI PROFILE (Recommandé développement local)
# ═══════════════════════════════════════════════════════════════════════

# ~/.aws/credentials:
# [default]
# aws_access_key_id = AKIAIOSFODNN7EXAMPLE
# aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# 
# [production]
# aws_access_key_id = AKIAI44QH8DHBEXAMPLE
# aws_secret_access_key = je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
# 
# [dev]
# aws_access_key_id = AKIAIHCEOD5ZZEXAMPLE
# aws_secret_access_key = +Je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY

# ~/.aws/config:
# [default]
# region = us-east-1
# output = json
# 
# [profile production]
# region = us-east-1
# output = json
# role_arn = arn:aws:iam::123456789012:role/ProductionRole
# source_profile = default
# 
# [profile dev]
# region = us-west-2
# output = json

# Terraform
provider "aws" {
  region  = "us-east-1"
  profile = "production"  # Lit le profile "production"
}

# Ou variable
variable "aws_profile" {
  type = string
}

provider "aws" {
  region  = "us-east-1"
  profile = var.aws_profile
}

# Utilisation:
# terraform apply -var="aws_profile=production"

# Avantages:
# [OK] Gestion centralisée des credentials
# [OK] Support assume role natif
# [OK] Facile de switcher entre comptes
# [OK] Intégration AWS CLI
# 
# Inconvénients:
# [X] Spécifique à une machine
# [X] Pas adapté CI/CD


# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 3: IAM ROLE (Recommandé production EC2/ECS/Lambda)
# ═══════════════════════════════════════════════════════════════════════

# Attachez un IAM role à votre EC2/ECS/Lambda
# Terraform détecte automatiquement et utilise le role

provider "aws" {
  region = "us-east-1"
  # Pas de credentials nécessaires!
  # Utilise automatiquement l'IAM role de l'instance
}

# Créer le role pour Terraform:
resource "aws_iam_role" "terraform" {
  name = "TerraformExecutionRole"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = {
        Service = "ec2.amazonaws.com"
      }
      Action = "sts:AssumeRole"
    }]
  })
}

resource "aws_iam_role_policy_attachment" "terraform_admin" {
  role       = aws_iam_role.terraform.name
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
  # [ATTENTION] En production: créer une policy plus restrictive!
}

resource "aws_iam_instance_profile" "terraform" {
  name = "terraform-instance-profile"
  role = aws_iam_role.terraform.name
}

# Attacher à l'instance EC2
resource "aws_instance" "terraform_runner" {
  ami                  = "ami-123"
  instance_type        = "t2.micro"
  iam_instance_profile = aws_iam_instance_profile.terraform.name
}

# Avantages:
# [OK] Pas de credentials statiques
# [OK] Rotation automatique (AWS gère)
# [OK] Audit trail complet (CloudTrail)
# [OK] Le plus sécurisé
# 
# Inconvénients:
# [X] Seulement pour ressources AWS (EC2, Lambda, ECS)
# [X] Configuration initiale plus complexe


# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 4: ASSUME ROLE (Recommandé multi-comptes)
# ═══════════════════════════════════════════════════════════════════════

# Compte A (compte principal avec credentials)
# Assume role dans Compte B (compte cible)

provider "aws" {
  region = "us-east-1"
  
  assume_role {
    role_arn     = "arn:aws:iam::123456789012:role/TerraformRole"
    session_name = "terraform-session"
    external_id  = "unique-id-123"  # Sécurité additionnelle
  }
}

# Dans le compte cible (123456789012), créer le role:
resource "aws_iam_role" "terraform_assume" {
  name = "TerraformRole"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = {
        AWS = "arn:aws:iam::999999999999:root"  # Compte source
      }
      Action = "sts:AssumeRole"
      Condition = {
        StringEquals = {
          "sts:ExternalId" = "unique-id-123"
        }
      }
    }]
  })
}

# Avantages:
# [OK] Sécurisé pour multi-comptes
# [OK] Credentials temporaires
# [OK] External ID pour sécurité renforcée
# [OK] Audit trail complet
# 
# Inconvénients:
# [X] Configuration IAM plus complexe
# [X] Doit avoir credentials dans compte source


# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 5: WEB IDENTITY (OIDC - GitHub Actions, GitLab CI)
# ═══════════════════════════════════════════════════════════════════════

provider "aws" {
  region = "us-east-1"
  
  assume_role_with_web_identity {
    role_arn                = "arn:aws:iam::123456789012:role/GitHubActionsRole"
    web_identity_token_file = "/var/run/secrets/token"
    session_name            = "github-actions"
  }
}

# Configuration GitHub Actions OIDC:
# 1. Créer OIDC provider dans AWS
resource "aws_iam_openid_connect_provider" "github" {
  url = "https://token.actions.githubusercontent.com"
  
  client_id_list = ["sts.amazonaws.com"]
  
  thumbprint_list = [
    "6938fd4d98bab03faadb97b34396831e3780aea1"
  ]
}

# 2. Créer role avec trust policy
resource "aws_iam_role" "github_actions" {
  name = "GitHubActionsRole"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = {
        Federated = aws_iam_openid_connect_provider.github.arn
      }
      Action = "sts:AssumeRoleWithWebIdentity"
      Condition = {
        StringLike = {
          "token.actions.githubusercontent.com:sub" = "repo:myorg/myrepo:*"
        }
        StringEquals = {
          "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
        }
      }
    }]
  })
}

# 3. GitHub Actions workflow:
# name: Deploy
# on: [push]
# 
# permissions:
#   id-token: write
#   contents: read
# 
# jobs:
#   deploy:
#     runs-on: ubuntu-latest
#     steps:
#       - uses: aws-actions/configure-aws-credentials@v4
#         with:
#           role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
#           aws-region: us-east-1
#       
#       - name: Terraform
#         run: terraform apply -auto-approve

# Avantages:
# [OK] Pas de credentials long-terme stockés
# [OK] Tokens temporaires automatiques
# [OK] Sécurisé (OIDC standard)
# [OK] Idéal CI/CD moderne
# 
# Inconvénients:
# [X] Configuration initiale complexe
# [X] Spécifique au provider OIDC (GitHub, GitLab, etc.)


# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 6: AWS SSO (Single Sign-On)
# ═══════════════════════════════════════════════════════════════════════

# ~/.aws/config:
# [profile sso-dev]
# sso_start_url = https://my-sso-portal.awsapps.com/start
# sso_region = us-east-1
# sso_account_id = 123456789012
# sso_role_name = DeveloperAccess
# region = us-east-1

# Connexion SSO:
# aws sso login --profile sso-dev

provider "aws" {
  region  = "us-east-1"
  profile = "sso-dev"
}

# Avantages:
# [OK] Intégration entreprise (AD, Okta, etc.)
# [OK] MFA natif
# [OK] Gestion centralisée accès
# [OK] Session temporaire
# 
# Inconvénients:
# [X] Requiert AWS SSO configuré
# [X] Login manuel périodique
# [X] Pas adapté CI/CD automatisé


# ═══════════════════════════════════════════════════════════════════════
# ORDRE DE PRIORITÉ DES CREDENTIALS
# ═══════════════════════════════════════════════════════════════════════

# Terraform cherche les credentials dans cet ordre:
# 
# 1. Variables provider explicites (access_key/secret_key) [X] À ÉVITER!
# 2. Variables d'environnement (AWS_ACCESS_KEY_ID, etc.)
# 3. Shared credentials file (~/.aws/credentials)
# 4. AWS Config file (~/.aws/config)
# 5. ECS Task role (si running dans ECS)
# 6. EC2 Instance profile (si running sur EC2)
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ RESSOURCES AWS ESSENTIELLES - EXEMPLES COMPLETS                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# VPC - RÉSEAU COMPLET
# ═══════════════════════════════════════════════════════════════════════

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  tags = {
    Name = "${var.project_name}-vpc"
  }
}

# Internet Gateway
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  
  tags = {
    Name = "${var.project_name}-igw"
  }
}

# Subnets publics
resource "aws_subnet" "public" {
  count = 3
  
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.${count.index}.0/24"
  availability_zone       = data.aws_availability_zones.available.names[count.index]
  map_public_ip_on_launch = true
  
  tags = {
    Name = "${var.project_name}-public-${count.index + 1}"
    Tier = "Public"
  }
}

# Subnets privés
resource "aws_subnet" "private" {
  count = 3
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index + 10}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]
  
  tags = {
    Name = "${var.project_name}-private-${count.index + 1}"
    Tier = "Private"
  }
}

# Elastic IPs pour NAT Gateways
resource "aws_eip" "nat" {
  count  = 3
  domain = "vpc"
  
  tags = {
    Name = "${var.project_name}-nat-eip-${count.index + 1}"
  }
  
  depends_on = [aws_internet_gateway.main]
}

# NAT Gateways
resource "aws_nat_gateway" "main" {
  count = 3
  
  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id
  
  tags = {
    Name = "${var.project_name}-nat-${count.index + 1}"
  }
}

# Route table publique
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }
  
  tags = {
    Name = "${var.project_name}-public-rt"
  }
}

# Association subnets publics
resource "aws_route_table_association" "public" {
  count = 3
  
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

# Route tables privées (une par AZ)
resource "aws_route_table" "private" {
  count = 3
  
  vpc_id = aws_vpc.main.id
  
  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.main[count.index].id
  }
  
  tags = {
    Name = "${var.project_name}-private-rt-${count.index + 1}"
  }
}

# Association subnets privés
resource "aws_route_table_association" "private" {
  count = 3
  
  subnet_id      = aws_subnet.private[count.index].id
  route_table_id = aws_route_table.private[count.index].id
}


# ═══════════════════════════════════════════════════════════════════════
# EC2 - INSTANCE COMPLÈTE
# ═══════════════════════════════════════════════════════════════════════

# Security Group
resource "aws_security_group" "web" {
  name        = "${var.project_name}-web-sg"
  description = "Security group for web servers"
  vpc_id      = aws_vpc.main.id
  
  # Ingress HTTP
  ingress {
    description = "HTTP from anywhere"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  # Ingress HTTPS
  ingress {
    description = "HTTPS from anywhere"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  # Ingress SSH (restreint)
  ingress {
    description = "SSH from office"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [var.office_cidr]
  }
  
  # Egress tout
  egress {
    description = "All outbound"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  tags = {
    Name = "${var.project_name}-web-sg"
  }
}

# Launch Template
resource "aws_launch_template" "web" {
  name_prefix   = "${var.project_name}-web-"
  image_id      = data.aws_ami.amazon_linux_2.id
  instance_type = var.instance_type
  
  vpc_security_group_ids = [aws_security_group.web.id]
  
  iam_instance_profile {
    arn = aws_iam_instance_profile.web.arn
  }
  
  # User data
  user_data = base64encode(templatefile("${path.module}/user-data.sh", {
    environment = var.environment
    app_name    = var.app_name
  }))
  
  # Monitoring détaillé
  monitoring {
    enabled = var.environment == "prod"
  }
  
  # Block devices
  block_device_mappings {
    device_name = "/dev/xvda"
    
    ebs {
      volume_size           = 20
      volume_type           = "gp3"
      iops                  = 3000
      throughput            = 125
      encrypted             = true
      delete_on_termination = true
    }
  }
  
  # Metadata options (IMDSv2)
  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"  # Force IMDSv2
    http_put_response_hop_limit = 1
  }
  
  tag_specifications {
    resource_type = "instance"
    
    tags = {
      Name = "${var.project_name}-web-instance"
    }
  }
  
  tag_specifications {
    resource_type = "volume"
    
    tags = {
      Name = "${var.project_name}-web-volume"
    }
  }
}

# Auto Scaling Group
resource "aws_autoscaling_group" "web" {
  name                = "${var.project_name}-web-asg"
  vpc_zone_identifier = aws_subnet.private[*].id
  
  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
  
  launch_template {
    id      = aws_launch_template.web.id
    version = "$Latest"
  }
  
  target_group_arns = [aws_lb_target_group.web.arn]
  
  enabled_metrics = [
    "GroupDesiredCapacity",
    "GroupInServiceInstances",
    "GroupMinSize",
    "GroupMaxSize",
    "GroupTotalInstances"
  ]
  
  tag {
    key                 = "Name"
    value               = "${var.project_name}-web-asg-instance"
    propagate_at_launch = true
  }
  
  lifecycle {
    create_before_destroy = true
  }
}

# Auto Scaling Policies
resource "aws_autoscaling_policy" "scale_up" {
  name                   = "${var.project_name}-scale-up"
  autoscaling_group_name = aws_autoscaling_group.web.name
  
  policy_type            = "TargetTrackingScaling"
  estimated_instance_warmup = 300
  
  target_tracking_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ASGAverageCPUUtilization"
    }
    
    target_value = 70.0
  }
}


# ═══════════════════════════════════════════════════════════════════════
# ALB - APPLICATION LOAD BALANCER
# ═══════════════════════════════════════════════════════════════════════

resource "aws_lb" "web" {
  name               = "${var.project_name}-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = aws_subnet.public[*].id
  
  enable_deletion_protection = var.environment == "prod"
  enable_http2              = true
  enable_cross_zone_load_balancing = true
  
  access_logs {
    bucket  = aws_s3_bucket.alb_logs.id
    prefix  = "alb-logs"
    enabled = true
  }
  
  tags = {
    Name = "${var.project_name}-alb"
  }
}

# Target Group
resource "aws_lb_target_group" "web" {
  name     = "${var.project_name}-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id
  
  health_check {
    enabled             = true
    healthy_threshold   = 2
    unhealthy_threshold = 2
    timeout             = 5
    interval            = 30
    path                = "/health"
    matcher             = "200"
  }
  
  deregistration_delay = 30
  
  stickiness {
    type            = "lb_cookie"
    cookie_duration = 86400  # 24 hours
    enabled         = true
  }
  
  tags = {
    Name = "${var.project_name}-tg"
  }
}

# Listener HTTP (redirect to HTTPS)
resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.web.arn
  port              = 80
  protocol          = "HTTP"
  
  default_action {
    type = "redirect"
    
    redirect {
      port        = "443"
      protocol    = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}

# Listener HTTPS
resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.web.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS-1-2-2017-01"
  certificate_arn   = aws_acm_certificate.web.arn
  
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.web.arn
  }
}


# ═══════════════════════════════════════════════════════════════════════
# RDS - BASE DE DONNÉES
# ═══════════════════════════════════════════════════════════════════════

# Subnet Group
resource "aws_db_subnet_group" "main" {
  name       = "${var.project_name}-db-subnet-group"
  subnet_ids = aws_subnet.private[*].id
  
  tags = {
    Name = "${var.project_name}-db-subnet-group"
  }
}

# Security Group
resource "aws_security_group" "rds" {
  name        = "${var.project_name}-rds-sg"
  description = "Security group for RDS"
  vpc_id      = aws_vpc.main.id
  
  ingress {
    description     = "PostgreSQL from web servers"
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.web.id]
  }
  
  tags = {
    Name = "${var.project_name}-rds-sg"
  }
}

# RDS Instance
resource "aws_db_instance" "main" {
  identifier = "${var.project_name}-db"
  
  # Engine
  engine         = "postgres"
  engine_version = "15.4"
  
  # Instance
  instance_class        = var.db_instance_class
  allocated_storage     = 100
  max_allocated_storage = 1000  # Auto-scaling
  storage_type          = "gp3"
  storage_encrypted     = true
  kms_key_id            = aws_kms_key.rds.arn
  
  # Database
  db_name  = var.db_name
  username = var.db_username
  password = random_password.db_password.result
  port     = 5432
  
  # Network
  db_subnet_group_name   = aws_db_subnet_group.main.name
  vpc_security_group_ids = [aws_security_group.rds.id]
  publicly_accessible    = false
  
  # High Availability
  multi_az = var.environment == "prod"
  
  # Backup
  backup_retention_period = var.environment == "prod" ? 30 : 7
  backup_window           = "03:00-04:00"
  maintenance_window      = "mon:04:00-mon:05:00"
  
  # Monitoring
  enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
  monitoring_interval             = 60
  monitoring_role_arn             = aws_iam_role.rds_monitoring.arn
  
  # Performance Insights
  performance_insights_enabled    = true
  performance_insights_retention_period = 7
  
  # Deletion
  deletion_protection       = var.environment == "prod"
  skip_final_snapshot       = var.environment != "prod"
  final_snapshot_identifier = var.environment == "prod" ? "${var.project_name}-final-snapshot-${formatdate("YYYY-MM-DD-hhmm", timestamp())}" : null
  
  # Auto minor version upgrade
  auto_minor_version_upgrade = true
  
  tags = {
    Name = "${var.project_name}-db"
  }
  
  lifecycle {
    ignore_changes = [password]
  }
}

# Read Replica (production)
resource "aws_db_instance" "replica" {
  count = var.environment == "prod" ? 1 : 0
  
  identifier = "${var.project_name}-db-replica"
  
  replicate_source_db = aws_db_instance.main.identifier
  instance_class      = var.db_instance_class
  
  publicly_accessible = false
  
  tags = {
    Name = "${var.project_name}-db-replica"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# S3 - BUCKET COMPLET
# ═══════════════════════════════════════════════════════════════════════

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

# Versioning
resource "aws_s3_bucket_versioning" "assets" {
  bucket = aws_s3_bucket.assets.id
  
  versioning_configuration {
    status = "Enabled"
  }
}

# Encryption
resource "aws_s3_bucket_server_side_encryption_configuration" "assets" {
  bucket = aws_s3_bucket.assets.id
  
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.s3.id
    }
    bucket_key_enabled = true
  }
}

# Public Access Block
resource "aws_s3_bucket_public_access_block" "assets" {
  bucket = aws_s3_bucket.assets.id
  
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# Lifecycle Policy
resource "aws_s3_bucket_lifecycle_configuration" "assets" {
  bucket = aws_s3_bucket.assets.id
  
  rule {
    id     = "archive-old-versions"
    status = "Enabled"
    
    noncurrent_version_transition {
      noncurrent_days = 30
      storage_class   = "GLACIER"
    }
    
    noncurrent_version_expiration {
      noncurrent_days = 90
    }
  }
  
  rule {
    id     = "delete-incomplete-uploads"
    status = "Enabled"
    
    abort_incomplete_multipart_upload {
      days_after_initiation = 7
    }
  }
}

# CORS
resource "aws_s3_bucket_cors_configuration" "assets" {
  bucket = aws_s3_bucket.assets.id
  
  cors_rule {
    allowed_headers = ["*"]
    allowed_methods = ["GET", "HEAD"]
    allowed_origins = ["https://${var.domain_name}"]
    expose_headers  = ["ETag"]
    max_age_seconds = 3000
  }
}

# Logging
resource "aws_s3_bucket_logging" "assets" {
  bucket = aws_s3_bucket.assets.id
  
  target_bucket = aws_s3_bucket.logs.id
  target_prefix = "s3-access-logs/"
}
```


(Suite dans le prochain message - Providers GCP, Azure, etc.)

# Fichier: python_cheats/cheatsheets/terraform_part6_providers_suite.txt
# Terraform - PARTIE 6 (Suite): Providers GCP, Azure, Kubernetes, Docker
# Guide Ultra-Détaillé


═══════════════════════════════════════════════════════════════════════════════
- PARTIE 6.2: PROVIDER GOOGLE CLOUD PLATFORM (GCP)
═══════════════════════════════════════════════════════════════════════════════

╔═══════════════════════════════════════════════════════════════════════════╗
║ CONFIGURATION GCP PROVIDER - COMPLET                                      ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# versions.tf
# ═══════════════════════════════════════════════════════════════════════

terraform {
  required_version = ">= 1.0"
  
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
    
    google-beta = {
      source  = "hashicorp/google-beta"
      version = "~> 5.0"
    }
  }
}


# ═══════════════════════════════════════════════════════════════════════
# CONFIGURATION BASIQUE
# ═══════════════════════════════════════════════════════════════════════

provider "google" {
  project = var.project_id
  region  = var.region
  zone    = var.zone  # Optionnel, défaut pour ressources zonales
  
  # MÉTHODE 1: Service Account Key (développement local)
  credentials = file("${path.module}/service-account-key.json")
  # [ATTENTION] NE JAMAIS COMMITER CE FICHIER!
  
  # MÉTHODE 2: Variable d'environnement (recommandé)
  # GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json
  
  # MÉTHODE 3: Application Default Credentials (production)
  # Automatique si running sur GCP (GCE, Cloud Run, GKE)
}

# Provider beta (fonctionnalités expérimentales)
provider "google-beta" {
  project = var.project_id
  region  = var.region
  zone    = var.zone
}


# ═══════════════════════════════════════════════════════════════════════
# CONFIGURATION AVANCÉE
# ═══════════════════════════════════════════════════════════════════════

provider "google" {
  project = var.project_id
  region  = var.region
  zone    = var.zone
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ IMPERSONATION (Assume Service Account)                           │
  # └─────────────────────────────────────────────────────────────────┘
  
  impersonate_service_account = "terraform@${var.project_id}.iam.gserviceaccount.com"
  
  # Delegates pour chaîne d'impersonation
  impersonate_service_account_delegates = [
    "projects/-/serviceAccounts/delegated-account@project.iam.gserviceaccount.com"
  ]
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ SCOPES (Permissions OAuth2)                                      │
  # └─────────────────────────────────────────────────────────────────┘
  
  scopes = [
    "https://www.googleapis.com/auth/cloud-platform",
    "https://www.googleapis.com/auth/compute",
    "https://www.googleapis.com/auth/storage"
  ]
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ BILLING                                                           │
  # └─────────────────────────────────────────────────────────────────┘
  
  billing_project = var.billing_project_id  # Si différent du project
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ USER PROJECT OVERRIDE                                             │
  # └─────────────────────────────────────────────────────────────────┘
  
  user_project_override = true  # Quota sur project utilisateur
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ BATCHING (Performance)                                            │
  # └─────────────────────────────────────────────────────────────────┘
  
  batching {
    send_after      = "10s"  # Envoyer après 10s
    enable_batching = true
  }
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ REQUEST TIMEOUT                                                   │
  # └─────────────────────────────────────────────────────────────────┘
  
  request_timeout = "60s"
  
  
  # ┌─────────────────────────────────────────────────────────────────┐
  # │ CUSTOM ENDPOINT (Émulateur local)                                │
  # └─────────────────────────────────────────────────────────────────┘
  
  # compute_custom_endpoint = "http://localhost:4443/compute/v1/"
  # storage_custom_endpoint = "http://localhost:4443/storage/v1/"
}


# ═══════════════════════════════════════════════════════════════════════
# MULTI-RÉGIONS
# ═══════════════════════════════════════════════════════════════════════

# Provider US
provider "google" {
  project = var.project_id
  region  = "us-central1"
}

# Provider Europe
provider "google" {
  alias   = "eu"
  project = var.project_id
  region  = "europe-west1"
}

# Utilisation
resource "google_compute_instance" "eu_vm" {
  provider = google.eu
  
  name         = "eu-vm"
  machine_type = "e2-medium"
  zone         = "europe-west1-b"
  # ...
}


# ═══════════════════════════════════════════════════════════════════════
# MULTI-PROJETS
# ═══════════════════════════════════════════════════════════════════════

# Projet Dev
provider "google" {
  alias   = "dev"
  project = "my-project-dev"
  region  = "us-central1"
}

# Projet Prod
provider "google" {
  alias   = "prod"
  project = "my-project-prod"
  region  = "us-central1"
}
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ AUTHENTIFICATION GCP - TOUTES LES MÉTHODES                                ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 1: SERVICE ACCOUNT KEY FILE (Dev local)
# ═══════════════════════════════════════════════════════════════════════

# 1. Créer Service Account dans GCP Console
# IAM & Admin -> Service Accounts -> Create Service Account

# 2. Donner permissions (exemple: Editor)

# 3. Créer clé JSON
# Actions -> Manage keys -> Add key -> Create new key -> JSON

# 4. Télécharger le fichier (ex: service-account-key.json)

# 5. Utiliser dans Terraform
provider "google" {
  project     = "my-project-id"
  region      = "us-central1"
  credentials = file("${path.module}/service-account-key.json")
}

# [ATTENTION] SÉCURITÉ:
# .gitignore:
# *.json
# service-account-key.json

# Avantages:
# [OK] Simple pour développement local
# [OK] Permissions granulaires
# 
# Inconvénients:
# [X] Clé long-terme (risque si leak)
# [X] Rotation manuelle
# [X] Fichier sensible


# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 2: VARIABLE D'ENVIRONNEMENT (Recommandé)
# ═══════════════════════════════════════════════════════════════════════

# Dans votre shell:
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"
export GOOGLE_PROJECT="my-project-id"
export GOOGLE_REGION="us-central1"

# Terraform provider (minimal)
provider "google" {
  # Credentials automatiquement lues depuis GOOGLE_APPLICATION_CREDENTIALS
  project = var.project_id
  region  = var.region
}

# Avantages:
# [OK] Pas de credentials dans le code
# [OK] Standard GCP
# [OK] Facile CI/CD


# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 3: APPLICATION DEFAULT CREDENTIALS (Production)
# ═══════════════════════════════════════════════════════════════════════

# Automatique si running sur:
# - Compute Engine (GCE)
# - Google Kubernetes Engine (GKE)
# - Cloud Run
# - Cloud Functions
# - App Engine

provider "google" {
  project = var.project_id
  region  = var.region
  # Pas de credentials nécessaires!
  # Utilise le Service Account de la ressource GCP
}

# Configurer le Service Account de la VM:
resource "google_compute_instance" "terraform_runner" {
  name         = "terraform-runner"
  machine_type = "e2-medium"
  zone         = "us-central1-a"
  
  # Attacher Service Account
  service_account {
    email  = google_service_account.terraform.email
    scopes = ["cloud-platform"]
  }
  
  # ...
}

# Avantages:
# [OK] Pas de credentials statiques
# [OK] Rotation automatique
# [OK] Le plus sécurisé
# 
# Inconvénients:
# [X] Seulement pour workloads GCP


# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 4: GCLOUD AUTH (Dev local)
# ═══════════════════════════════════════════════════════════════════════

# 1. S'authentifier avec gcloud CLI
gcloud auth application-default login

# 2. Définir projet
gcloud config set project my-project-id

# 3. Terraform provider
provider "google" {
  # Utilise automatiquement les credentials de gcloud
  project = var.project_id
  region  = var.region
}

# Avantages:
# [OK] Simple pour dev local
# [OK] Pas de fichier de clé
# [OK] Même identité que gcloud CLI
# 
# Inconvénients:
# [X] Credentials liés à l'utilisateur (pas adapté prod)
# [X] Expire périodiquement


# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 5: IMPERSONATION (Multi-environnements)
# ═══════════════════════════════════════════════════════════════════════

# Utilise vos credentials personnels pour impersonate un Service Account

# 1. Donner permission d'impersonation
# IAM: Service Account Token Creator

# 2. Provider
provider "google" {
  project = var.project_id
  region  = var.region
  
  impersonate_service_account = "terraform@my-project.iam.gserviceaccount.com"
}

# Avantages:
# [OK] Audit trail (votre identité + SA)
# [OK] Pas de clé long-terme
# [OK] Permissions granulaires par SA
# 
# Inconvénients:
# [X] Configuration IAM plus complexe


# ═══════════════════════════════════════════════════════════════════════
# MÉTHODE 6: WORKLOAD IDENTITY (GKE -> GCP)
# ═══════════════════════════════════════════════════════════════════════

# Permet à un pod Kubernetes d'utiliser un Service Account GCP

# 1. Créer Service Account GCP
resource "google_service_account" "workload" {
  account_id   = "workload-identity-sa"
  display_name = "Workload Identity Service Account"
}

# 2. Créer Service Account Kubernetes
resource "kubernetes_service_account" "workload" {
  metadata {
    name      = "workload-sa"
    namespace = "default"
    
    annotations = {
      "iam.gke.io/gcp-service-account" = google_service_account.workload.email
    }
  }
}

# 3. Binding IAM
resource "google_service_account_iam_binding" "workload_identity" {
  service_account_id = google_service_account.workload.name
  role               = "roles/iam.workloadIdentityUser"
  
  members = [
    "serviceAccount:${var.project_id}.svc.id.goog[default/workload-sa]"
  ]
}

# 4. Pod utilise automatiquement le SA GCP
# apiVersion: v1
# kind: Pod
# metadata:
#   name: terraform-runner
# spec:
#   serviceAccountName: workload-sa  # <- Lié au SA GCP
#   containers:
#   - name: terraform
#     image: hashicorp/terraform

# Avantages:
# [OK] Pas de secrets dans Kubernetes
# [OK] Rotation automatique
# [OK] Standard GKE
```


╔═══════════════════════════════════════════════════════════════════════════╗
║ RESSOURCES GCP ESSENTIELLES - EXEMPLES COMPLETS                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

```hcl
# ═══════════════════════════════════════════════════════════════════════
# VPC NETWORK
# ═══════════════════════════════════════════════════════════════════════

resource "google_compute_network" "main" {
  name                    = "${var.project_name}-vpc"
  auto_create_subnetworks = false  # Mode custom
  
  routing_mode = "GLOBAL"  # ou "REGIONAL"
  
  description = "Main VPC for ${var.project_name}"
}

# Subnets
resource "google_compute_subnetwork" "public" {
  name          = "${var.project_name}-public-subnet"
  ip_cidr_range = "10.0.1.0/24"
  region        = var.region
  network       = google_compute_network.main.id
  
  # Private Google Access
  private_ip_google_access = true
  
  # Secondary IP ranges (pour GKE)
  secondary_ip_range {
    range_name    = "pods"
    ip_cidr_range = "10.1.0.0/16"
  }
  
  secondary_ip_range {
    range_name    = "services"
    ip_cidr_range = "10.2.0.0/16"
  }
  
  # Logging
  log_config {
    aggregation_interval = "INTERVAL_5_SEC"
    flow_sampling        = 0.5
    metadata             = "INCLUDE_ALL_METADATA"
  }
}

resource "google_compute_subnetwork" "private" {
  name          = "${var.project_name}-private-subnet"
  ip_cidr_range = "10.0.2.0/24"
  region        = var.region
  network       = google_compute_network.main.id
  
  private_ip_google_access = true
}

# Firewall Rules
resource "google_compute_firewall" "allow_ssh" {
  name    = "${var.project_name}-allow-ssh"
  network = google_compute_network.main.name
  
  allow {
    protocol = "tcp"
    ports    = ["22"]
  }
  
  source_ranges = [var.office_cidr]
  target_tags   = ["ssh-enabled"]
  
  description = "Allow SSH from office"
}

resource "google_compute_firewall" "allow_http_https" {
  name    = "${var.project_name}-allow-http-https"
  network = google_compute_network.main.name
  
  allow {
    protocol = "tcp"
    ports    = ["80", "443"]
  }
  
  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["web-server"]
}

resource "google_compute_firewall" "allow_internal" {
  name    = "${var.project_name}-allow-internal"
  network = google_compute_network.main.name
  
  allow {
    protocol = "tcp"
    ports    = ["0-65535"]
  }
  
  allow {
    protocol = "udp"
    ports    = ["0-65535"]
  }
  
  allow {
    protocol = "icmp"
  }
  
  source_ranges = ["10.0.0.0/8"]
  
  description = "Allow all internal traffic"
}

# Cloud Router (pour Cloud NAT)
resource "google_compute_router" "main" {
  name    = "${var.project_name}-router"
  region  = var.region
  network = google_compute_network.main.id
  
  bgp {
    asn = 64514
  }
}

# Cloud NAT
resource "google_compute_router_nat" "main" {
  name   = "${var.project_name}-nat"
  router = google_compute_router.main.name
  region = var.region
  
  nat_ip_allocate_option             = "AUTO_ONLY"
  source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
  
  log_config {
    enable = true
    filter = "ERRORS_ONLY"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# COMPUTE ENGINE INSTANCE
# ═══════════════════════════════════════════════════════════════════════

# Service Account pour les VMs
resource "google_service_account" "vm" {
  account_id   = "${var.project_name}-vm-sa"
  display_name = "Service Account for VMs"
  description  = "Used by Compute Engine instances"
}

# IAM Bindings
resource "google_project_iam_member" "vm_logging" {
  project = var.project_id
  role    = "roles/logging.logWriter"
  member  = "serviceAccount:${google_service_account.vm.email}"
}

resource "google_project_iam_member" "vm_monitoring" {
  project = var.project_id
  role    = "roles/monitoring.metricWriter"
  member  = "serviceAccount:${google_service_account.vm.email}"
}

# Instance Template
resource "google_compute_instance_template" "web" {
  name_prefix  = "${var.project_name}-web-"
  machine_type = var.machine_type
  region       = var.region
  
  tags = ["web-server", "ssh-enabled"]
  
  # Boot disk
  disk {
    source_image = data.google_compute_image.debian.self_link
    auto_delete  = true
    boot         = true
    disk_size_gb = 20
    disk_type    = "pd-standard"
    
    disk_encryption_key {
      kms_key_self_link = google_kms_crypto_key.vm_disk.id
    }
  }
  
  # Network
  network_interface {
    subnetwork = google_compute_subnetwork.public.id
    
    # Ephemeral external IP
    access_config {
      network_tier = "PREMIUM"
    }
  }
  
  # Service Account
  service_account {
    email  = google_service_account.vm.email
    scopes = ["cloud-platform"]
  }
  
  # Metadata
  metadata = {
    enable-oslogin = "TRUE"
  }
  
  # Startup script
  metadata_startup_script = templatefile("${path.module}/startup.sh", {
    environment = var.environment
    app_name    = var.app_name
  })
  
  # Shielded Instance Config
  shielded_instance_config {
    enable_secure_boot          = true
    enable_vtpm                 = true
    enable_integrity_monitoring = true
  }
  
  lifecycle {
    create_before_destroy = true
  }
}

# Instance Group Manager (Auto-scaling)
resource "google_compute_region_instance_group_manager" "web" {
  name   = "${var.project_name}-web-igm"
  region = var.region
  
  base_instance_name = "${var.project_name}-web"
  
  version {
    instance_template = google_compute_instance_template.web.id
  }
  
  target_size = var.instance_count
  
  # Named ports (pour Load Balancer)
  named_port {
    name = "http"
    port = 80
  }
  
  # Auto-healing
  auto_healing_policies {
    health_check      = google_compute_health_check.web.id
    initial_delay_sec = 300
  }
  
  # Update Policy
  update_policy {
    type                         = "PROACTIVE"
    minimal_action               = "REPLACE"
    max_surge_fixed              = 3
    max_unavailable_fixed        = 0
    instance_redistribution_type = "PROACTIVE"
  }
}

# Autoscaler
resource "google_compute_region_autoscaler" "web" {
  name   = "${var.project_name}-web-autoscaler"
  region = var.region
  target = google_compute_region_instance_group_manager.web.id
  
  autoscaling_policy {
    min_replicas    = var.min_instances
    max_replicas    = var.max_instances
    cooldown_period = 60
    
    cpu_utilization {
      target = 0.7
    }
  }
}

# Health Check
resource "google_compute_health_check" "web" {
  name = "${var.project_name}-web-health-check"
  
  timeout_sec        = 5
  check_interval_sec = 10
  healthy_threshold  = 2
  unhealthy_threshold = 3
  
  http_health_check {
    port         = 80
    request_path = "/health"
  }
}


# ═══════════════════════════════════════════════════════════════════════
# LOAD BALANCER (HTTP/HTTPS)
# ═══════════════════════════════════════════════════════════════════════

# IP Address
resource "google_compute_global_address" "lb" {
  name = "${var.project_name}-lb-ip"
}

# Backend Service
resource "google_compute_backend_service" "web" {
  name        = "${var.project_name}-backend"
  protocol    = "HTTP"
  port_name   = "http"
  timeout_sec = 30
  
  health_checks = [google_compute_health_check.web.id]
  
  backend {
    group           = google_compute_region_instance_group_manager.web.instance_group
    balancing_mode  = "UTILIZATION"
    capacity_scaler = 1.0
  }
  
  # CDN
  enable_cdn = true
  
  cdn_policy {
    cache_mode                   = "CACHE_ALL_STATIC"
    default_ttl                  = 3600
    max_ttl                      = 86400
    client_ttl                   = 3600
    negative_caching             = true
    serve_while_stale            = 86400
  }
  
  # IAP (Identity-Aware Proxy)
  iap {
    oauth2_client_id     = var.iap_client_id
    oauth2_client_secret = var.iap_client_secret
  }
  
  # Logging
  log_config {
    enable      = true
    sample_rate = 1.0
  }
}

# URL Map
resource "google_compute_url_map" "web" {
  name            = "${var.project_name}-url-map"
  default_service = google_compute_backend_service.web.id
  
  # Path-based routing
  host_rule {
    hosts        = [var.domain_name]
    path_matcher = "allpaths"
  }
  
  path_matcher {
    name            = "allpaths"
    default_service = google_compute_backend_service.web.id
    
    path_rule {
      paths   = ["/api/*"]
      service = google_compute_backend_service.api.id
    }
  }
}

# HTTP Proxy
resource "google_compute_target_http_proxy" "web" {
  name    = "${var.project_name}-http-proxy"
  url_map = google_compute_url_map.web.id
}

# HTTPS Proxy
resource "google_compute_target_https_proxy" "web" {
  name             = "${var.project_name}-https-proxy"
  url_map          = google_compute_url_map.web.id
  ssl_certificates = [google_compute_ssl_certificate.web.id]
}

# Forwarding Rules
resource "google_compute_global_forwarding_rule" "http" {
  name       = "${var.project_name}-http-forwarding"
  target     = google_compute_target_http_proxy.web.id
  port_range = "80"
  ip_address = google_compute_global_address.lb.address
}

resource "google_compute_global_forwarding_rule" "https" {
  name       = "${var.project_name}-https-forwarding"
  target     = google_compute_target_https_proxy.web.id
  port_range = "443"
  ip_address = google_compute_global_address.lb.address
}

# SSL Certificate
resource "google_compute_ssl_certificate" "web" {
  name        = "${var.project_name}-ssl-cert"
  private_key = file("${path.module}/ssl/private.key")
  certificate = file("${path.module}/ssl/certificate.crt")
  
  lifecycle {
    create_before_destroy = true
  }
}

# Managed SSL Certificate (Let's Encrypt-like)
resource "google_compute_managed_ssl_certificate" "web" {
  name = "${var.project_name}-managed-cert"
  
  managed {
    domains = [var.domain_name, "www.${var.domain_name}"]
  }
}


# ═══════════════════════════════════════════════════════════════════════
# CLOUD SQL (PostgreSQL)
# ═══════════════════════════════════════════════════════════════════════

# Database Instance
resource "google_sql_database_instance" "main" {
  name             = "${var.project_name}-db"
  database_version = "POSTGRES_15"
  region           = var.region
  
  settings {
    tier              = var.db_tier  # db-f1-micro, db-n1-standard-1, etc.
    availability_type = var.environment == "prod" ? "REGIONAL" : "ZONAL"
    disk_type         = "PD_SSD"
    disk_size         = 100
    disk_autoresize       = true
    disk_autoresize_limit = 500
    
    # Backup
    backup_configuration {
      enabled                        = true
      start_time                     = "03:00"
      point_in_time_recovery_enabled = true
      transaction_log_retention_days = 7
      backup_retention_settings {
        retained_backups = 30
        retention_unit   = "COUNT"
      }
    }
    
    # Maintenance
    maintenance_window {
      day          = 7  # Sunday
      hour         = 4
      update_track = "stable"
    }
    
    # IP Configuration
    ip_configuration {
      ipv4_enabled    = false  # Pas d'IP publique
      private_network = google_compute_network.main.id
      
      require_ssl = true
      
      authorized_networks {
        name  = "office"
        value = var.office_cidr
      }
    }
    
    # Insights
    insights_config {
      query_insights_enabled  = true
      query_string_length     = 1024
      record_application_tags = true
      record_client_address   = true
    }
    
    # Database flags
    database_flags {
      name  = "max_connections"
      value = "100"
    }
    
    database_flags {
      name  = "shared_buffers"
      value = "262144"  # 256MB en 8KB pages
    }
  }
  
  deletion_protection = var.environment == "prod"
  
  depends_on = [google_service_networking_connection.private_vpc_connection]
}

# Private Service Connection
resource "google_compute_global_address" "private_ip_address" {
  name          = "${var.project_name}-private-ip"
  purpose       = "VPC_PEERING"
  address_type  = "INTERNAL"
  prefix_length = 16
  network       = google_compute_network.main.id
}

resource "google_service_networking_connection" "private_vpc_connection" {
  network                 = google_compute_network.main.id
  service                 = "servicenetworking.googleapis.com"
  reserved_peering_ranges = [google_compute_global_address.private_ip_address.name]
}

# Database
resource "google_sql_database" "app" {
  name     = var.db_name
  instance = google_sql_database_instance.main.name
  
  charset   = "UTF8"
  collation = "en_US.UTF8"
}

# User
resource "google_sql_user" "app" {
  name     = var.db_username
  instance = google_sql_database_instance.main.name
  password = random_password.db_password.result
}

# Read Replica (production)
resource "google_sql_database_instance" "replica" {
  count = var.environment == "prod" ? 1 : 0
  
  name                 = "${var.project_name}-db-replica"
  database_version     = "POSTGRES_15"
  region               = var.replica_region
  master_instance_name = google_sql_database_instance.main.name
  
  replica_configuration {
    failover_target = false
  }
  
  settings {
    tier = var.db_tier
    
    ip_configuration {
      ipv4_enabled    = false
      private_network = google_compute_network.main.id
    }
  }
}


# ═══════════════════════════════════════════════════════════════════════
# CLOUD STORAGE (GCS)
# ═══════════════════════════════════════════════════════════════════════

resource "google_storage_bucket" "assets" {
  name          = "${var.project_name}-assets-${var.project_id}"
  location      = var.region
  storage_class = "STANDARD"  # STANDARD, NEARLINE, COLDLINE, ARCHIVE
  
  # Versioning
  versioning {
    enabled = true
  }
  
  # Lifecycle
  lifecycle_rule {
    condition {
      age = 30
      matches_storage_class = ["STANDARD"]
    }
    action {
      type          = "SetStorageClass"
      storage_class = "NEARLINE"
    }
  }
  
  lifecycle_rule {
    condition {
      age                   = 90
      num_newer_versions    = 3
      with_state            = "ARCHIVED"
    }
    action {
      type = "Delete"
    }
  }
  
  # Encryption
  encryption {
    default_kms_key_name = google_kms_crypto_key.storage.id
  }
  
  # CORS
  cors {
    origin          = ["https://${var.domain_name}"]
    method          = ["GET", "HEAD", "PUT", "POST"]
    response_header = ["*"]
    max_age_seconds = 3600
  }
  
  # Public access prevention
  public_access_prevention = "enforced"
  
  # Uniform bucket-level access
  uniform_bucket_level_access {
    enabled = true
  }
  
  # Logging
  logging {
    log_bucket        = google_storage_bucket.logs.name
    log_object_prefix = "gcs-logs/"
  }
  
  labels = {
    environment = var.environment
    managed_by  = "terraform"
  }
}

# IAM Binding
resource "google_storage_bucket_iam_member" "assets_viewer" {
  bucket = google_storage_bucket.assets.name
  role   = "roles/storage.objectViewer"
  member = "serviceAccount:${google_service_account.vm.email}"
}


# ═══════════════════════════════════════════════════════════════════════
# CLOUD RUN (Serverless)
# ═══════════════════════════════════════════════════════════════════════

resource "google_cloud_run_service" "api" {
  name     = "${var.project_name}-api"
  location = var.region
  
  template {
    spec {
      containers {
        image = "gcr.io/${var.project_id}/${var.app_name}:${var.app_version}"
        
        ports {
          container_port = 8080
        }
        
        resources {
          limits = {
            cpu    = "1000m"
            memory = "512Mi"
          }
        }
        
        env {
          name  = "ENVIRONMENT"
          value = var.environment
        }
        
        env {
          name = "DB_HOST"
          value_from {
            secret_key_ref {
              name = google_secret_manager_secret.db_host.secret_id
              key  = "latest"
            }
          }
        }
      }
      
      service_account_name = google_service_account.cloud_run.email
      
      timeout_seconds = 300
      
      container_concurrency = 80
    }
    
    metadata {
      annotations = {
        "autoscaling.knative.dev/minScale" = "1"
        "autoscaling.knative.dev/maxScale" = "100"
        "run.googleapis.com/cpu-throttling" = "false"
      }
    }
  }
  
  traffic {
    percent         = 100
    latest_revision = true
  }
  
  autogenerate_revision_name = true
}

# IAM - Allow unauthenticated access
resource "google_cloud_run_service_iam_member" "public" {
  count = var.allow_public_access ? 1 : 0
  
  service  = google_cloud_run_service.api.name
  location = google_cloud_run_service.api.location
  role     = "roles/run.invoker"
  member   = "allUsers"
}
```


(Suite dans le prochain message - Azure, Kubernetes, Docker...)