TP partie 10 - Déploiement Multi-Environnements avec Terraform
Dans cette dixième partie, nous allons apprendre à structurer et déployer une infrastructure Terraform pour plusieurs environnements (développement, staging, production). Cette partie se concentre sur l’organisation avancée des projets et les bonnes pratiques de déploiement multi-environnements.
Les enjeux du multi-environnement
Section titled “Les enjeux du multi-environnement”Pourquoi plusieurs environnements ?
Section titled “Pourquoi plusieurs environnements ?”Le déploiement multi-environnements permet de :
- Tester les changements sans impacter la production
- Valider les configurations dans un environnement similaire
- Réduire les risques lors des déploiements
- Permettre le développement parallèle
Les défis à relever
Section titled “Les défis à relever”- Isolation des environnements : Éviter les interférences entre environnements
- Gestion des configurations : Maintenir des paramètres différents par environnement
- Cohérence du code : Utiliser le même code pour tous les environnements
- Sécurité : Protéger l’environnement de production
Stratégie de gestion multi-environnements : Approche par Répertoires
Section titled “Stratégie de gestion multi-environnements : Approche par Répertoires”Pour ce TP, nous utilisons l’approche par répertoires séparés, qui est la méthode la plus robuste et claire pour gérer plusieurs environnements.
Avantages de l’approche par répertoires :
- Isolation complète : Chaque environnement a son propre état Terraform
- Sécurité renforcée : Impossible de modifier accidentellement le mauvais environnement
- Flexibilité : Configurations différentes par environnement
- CI/CD friendly : Pipelines simples à mettre en place
Structure à construire :
250_multi_environnements/├── modules/│ ├── vpc/│ ├── webserver/│ └── loadbalancer/├── environments/│ ├── dev/│ │ ├── main.tf│ │ ├── variables.tf│ │ ├── outputs.tf│ │ └── terraform.tfvars│ ├── staging/│ │ ├── main.tf│ │ ├── variables.tf│ │ ├── outputs.tf│ │ └── terraform.tfvars│ └── prod/│ ├── main.tf│ ├── variables.tf│ ├── outputs.tf│ └── terraform.tfvars├── scripts/│ ├── deploy.sh│ └── validate.sh└── global/ └── backend-config.tfPourquoi éviter les workspaces ?
- Risques d’erreur : Facile de se tromper d’environnement
- État partagé : Tous les workspaces utilisent le même backend
- Complexité : Gestion des variables et configurations plus difficile
- Manque de flexibilité : Difficile d’avoir des configurations très différentes
Mise en place de la structure multi-environnements
Section titled “Mise en place de la structure multi-environnements”Étape 1 : Copier la structure modulaire existante
Section titled “Étape 1 : Copier la structure modulaire existante”Partez du code de la partie 9 (copiez le dossier ou commitez les changements). Dans le dossier part10, nous allons créer une architecture multi-environnements basée sur les modules existants :
# Copier la structure modulaire depuis la Part 9cp -r ../240_refactorisation_modules/modules .
# Créer la structure multi-environnementmkdir -p environments/{dev,staging,prod}mkdir -p global scriptsÉtape 2 : Copier les fichiers de base vers tous les environnements
Section titled “Étape 2 : Copier les fichiers de base vers tous les environnements”Nous allons partir des fichiers de la Part 9 et les adapter pour chaque environnement :
# Copier les fichiers pour l'environnement devcp ../240_refactorisation_modules/main.tf environments/dev/cp ../240_refactorisation_modules/variables.tf environments/dev/cp ../240_refactorisation_modules/outputs.tf environments/dev/
# Copier les fichiers pour l'environnement stagingcp ../240_refactorisation_modules/main.tf environments/staging/cp ../240_refactorisation_modules/variables.tf environments/staging/cp ../240_refactorisation_modules/outputs.tf environments/staging/
# Copier les fichiers pour l'environnement prodcp ../240_refactorisation_modules/main.tf environments/prod/cp ../240_refactorisation_modules/variables.tf environments/prod/cp ../240_refactorisation_modules/outputs.tf environments/prod/Étape 3 : Vérification de la configuration du backend S3
Section titled “Étape 3 : Vérification de la configuration du backend S3”Chaque fichier environments/*/main.tf contient déjà la configuration minimale du backend :
terraform { # ... providers ...
# Backend configuré dynamiquement backend "s3" {}}Cette configuration vide backend "s3" {} est intentionnelle. Elle indique à Terraform d’utiliser S3 comme backend, mais les paramètres spécifiques (bucket, key, region, profile) seront fournis lors de l’initialisation via la commande terraform init -backend-config.
Pourquoi cette approche ?
- Chaque environnement doit avoir son propre état dans S3
- La clé (path) dans S3 doit être différente pour chaque environnement
- Les paramètres du backend sont passés dynamiquement pour éviter toute confusion
Étape 4 : Adapter les fichiers main.tf pour chaque environnement
Section titled “Étape 4 : Adapter les fichiers main.tf pour chaque environnement”Pour chaque environnement, nous devons modifier le fichier main.tf pour :
- Utiliser le backend S3 sans configuration statique
- Ajuster les paramètres workspace dans les modules
Pourquoi fixer le workspace en dur ?
Dans l’approche multi-environnements par dossiers (au lieu d’utiliser les workspaces Terraform), nous fixons le nom du workspace en dur dans chaque environnement pour plusieurs raisons :
- Clarté : Le nom de l’environnement est explicite dans le code, pas dépendant d’un état externe
- Isolation : Impossible de déployer accidentellement dans le mauvais environnement
- Simplicité : Pas besoin de changer de workspace avant chaque déploiement
- Cohérence : Les tags et noms de ressources correspondent toujours à l’environnement réel
Cette approche élimine le risque d’erreur humaine où quelqu’un oublierait de changer de workspace avant un déploiement.
Environnement dev
Section titled “Environnement dev”Éditez environments/dev/main.tf et modifiez :
Le fichier main.tf est déjà correctement configuré avec :
backend "s3" {}pour permettre la configuration dynamiqueworkspace = "dev"passé directement aux modules (pas de variable, juste la valeur en dur)
Vérifiez que les modules reçoivent bien le nom de l’environnement :
# Module VPCmodule "vpc" { source = "../../modules/vpc" # ... autres variables ... workspace = "dev" # Valeur fixe pour l'environnement dev}
# Module Webservermodule "webserver" { source = "../../modules/webserver" # ... autres variables ... workspace = "dev" # Valeur fixe pour l'environnement dev}
# Module Load Balancermodule "loadbalancer" { source = "../../modules/loadbalancer" # ... autres variables ... workspace = "dev" # Valeur fixe pour l'environnement dev}Environnement staging
Section titled “Environnement staging”Éditez environments/staging/main.tf et effectuez les mêmes modifications que pour dev, mais avec :
# Dans tous les modules :workspace = "staging"Environnement prod
Section titled “Environnement prod”Éditez environments/prod/main.tf et effectuez les mêmes modifications que pour dev, mais avec :
# Dans tous les modules :workspace = "prod"Étape 5 : Créer les fichiers terraform.tfvars spécifiques
Section titled “Étape 5 : Créer les fichiers terraform.tfvars spécifiques”Au lieu de modifier les variables.tf, nous utilisons des fichiers .tfvars spécifiques à chaque environnement :
# Environnement devcat > environments/dev/terraform.tfvars << 'EOF'instance_count = 1instance_type = "t2.micro"feature_name = "dev"EOF
# Environnement stagingcat > environments/staging/terraform.tfvars << 'EOF'instance_count = 2instance_type = "t2.small"feature_name = "staging"EOF
# Environnement prodcat > environments/prod/terraform.tfvars << 'EOF'instance_count = 3instance_type = "t2.small"feature_name = "prod"EOFÉtape 6 : Créer le script de déploiement
Section titled “Étape 6 : Créer le script de déploiement”Créez un script automatisé pour déployer sur tous les environnements :
# Créer le script de déploiementcat > scripts/deploy.sh << 'EOF'#!/bin/bashset -e
ENVIRONMENT=$1
if [ -z "$ENVIRONMENT" ]; then echo "Usage: ./deploy.sh [dev|staging|prod]" exit 1fi
# Validation de l'environnementif [[ ! "$ENVIRONMENT" =~ ^(dev|staging|prod)$ ]]; then echo "Error: Environment must be dev, staging, or prod" exit 1fi
# Protection pour la productionif [ "$ENVIRONMENT" == "prod" ]; then echo "⚠️ WARNING: You are about to deploy to PRODUCTION!" read -p "Are you sure? Type 'yes' to continue: " confirmation if [ "$confirmation" != "yes" ]; then echo "Deployment cancelled." exit 0 fifi
cd "environments/$ENVIRONMENT"
# Initialisation avec le backend spécifique# Les autres paramètres (bucket, region, profile) sont définis dans main.tfterraform init \ -backend-config="bucket=terraform-state-<YOUR-BUCKET-NAME>" \ -backend-config="key=tp-fil-rouge-${ENVIRONMENT}/terraform.tfstate" \ -backend-config="region=eu-west-3" \ -backend-config="profile=default"
# Planecho "📋 Creating execution plan for $ENVIRONMENT..."terraform plan -out=tfplan
# Apply avec confirmationecho "🚀 Applying changes to $ENVIRONMENT..."terraform apply tfplan
echo "✅ Deployment to $ENVIRONMENT completed!"EOF
# Rendre le script exécutablechmod +x scripts/deploy.shÉtape 7 : Créer le script de validation
Section titled “Étape 7 : Créer le script de validation”Créez également un script pour valider tous les environnements :
# Créer le script de validationcat > scripts/validate.sh << 'EOF'#!/bin/bashset -e
echo "🔍 Validating Terraform configurations..."
for env in dev staging prod; do echo "Checking $env environment..." cd "environments/$env"
# Format check terraform fmt -check
# Validation terraform validate
cd ../..done
echo "✅ All environments validated successfully!"EOF
# Rendre le script exécutablechmod +x scripts/validate.shVérification de la structure
Section titled “Vérification de la structure”Vérifiez que tous les fichiers ont été correctement copiés et adaptés :
# Vérifier la structure crééetree environments/
# Valider la configuration de tous les environnements./scripts/validate.sh
# Tester l'initialisation pour chaque environnementfor env in dev staging prod; do echo "Testing $env environment..." cd environments/$env terraform init -backend-config="key=tp-fil-rouge-${env}/terraform.tfstate" terraform plan cd ../..doneRésultat attendu :
- La structure doit montrer 3 environnements avec les mêmes fichiers (main.tf, variables.tf, outputs.tf, terraform.tfvars)
- La validation doit passer pour tous les environnements
- L’initialisation et le plan doivent fonctionner pour dev, staging et prod
Cette vérification confirme que la structure multi-environnements est correctement configurée et que chaque environnement est isolé avec ses propres paramètres.
Résumé de l’approche multi-environnements
Section titled “Résumé de l’approche multi-environnements”Architecture choisie
Section titled “Architecture choisie”Nous avons implémenté une approche basée sur des dossiers séparés plutôt que sur les workspaces Terraform :
environments/├── dev/ # Environnement de développement├── staging/ # Environnement de staging└── prod/ # Environnement de productionPoints clés de l’implémentation
Section titled “Points clés de l’implémentation”-
Backend S3 dynamique : Chaque environnement a
backend "s3" {}dans son main.tf. La configuration complète est passée lors de l’init. -
Workspace fixé en dur : Dans chaque main.tf, on passe directement
workspace = "dev",workspace = "staging"ouworkspace = "prod"aux modules (pas de variable). -
Isolation complète : Chaque environnement a :
- Son propre état Terraform dans S3 (
tp-fil-rouge-dev/terraform.tfstate, etc.) - Ses propres valeurs dans
terraform.tfvars - Son propre dossier isolé
- Son propre état Terraform dans S3 (
-
Scripts d’automatisation :
deploy.sh: Gère l’initialisation du backend et le déploiementvalidate.sh: Valide tous les environnements
Déploiement et test des trois environnements
Section titled “Déploiement et test des trois environnements”Déploiement de l’environnement de développement
Section titled “Déploiement de l’environnement de développement”Commencez par déployer l’environnement de développement :
# Déployer l'environnement de développement./scripts/deploy.sh devValidation du déploiement dev
Section titled “Validation du déploiement dev”Vérifiez que l’infrastructure est correctement déployée :
# Tester l'accès au load balancercd environments/devcurl $(terraform output -raw web_url)
# Vérifier les outputsterraform outputcd ../..Résultat attendu :
- 1 instance EC2 t2.micro déployée
- Load balancer fonctionnel
- Page web accessible
Déploiement staging
Section titled “Déploiement staging”Une fois le développement validé, déployez staging avec 2 instances :
# Déploiement staging./scripts/deploy.sh staging
# Validation stagingcd environments/stagingterraform outputcurl $(terraform output -raw web_url)cd ../..Résultat attendu :
- 2 instances EC2 t2.small déployées
- Load balancer avec plus de capacité
Déploiement production
Section titled “Déploiement production”Enfin, déployez la production avec 3 instances :
# Déploiement production (avec confirmation)./scripts/deploy.sh prod
# Validation productioncd environments/prodterraform outputcurl $(terraform output -raw web_url)cd ../..Résultat attendu :
- 3 instances EC2 t2.medium déployées
- Infrastructure de production complète
- Confirmation manuelle requise avant déploiement
Exemple de pipeline CI/CD
Section titled “Exemple de pipeline CI/CD”Exemple de configuration GitLab CI :
# .gitlab-ci.yml - Pipeline CI/CD pour multi-environnements Terraform
# Définition des étapes du pipelinestages: - validate # Validation et formatage du code - plan # Création des plans d'exécution - deploy # Déploiement sur les environnements
# Variables globales du pipelinevariables: TF_ROOT: ${CI_PROJECT_DIR}/environments # Chemin vers les environnements TF_IN_AUTOMATION: "true" # Indique à Terraform qu'il est en mode automatisé
# Template de base réutilisé par tous les jobs Terraform.terraform-base: image: hashicorp/terraform:1.5 # Image Docker officielle Terraform before_script: - cd ${TF_ROOT}/${ENVIRONMENT} # Se placer dans le bon environnement # Initialiser avec la clé spécifique à l'environnement - terraform init -backend-config="key=${ENVIRONMENT}/terraform.tfstate"
# Job de validation : vérifie le formatage et la syntaxevalidate: extends: .terraform-base stage: validate script: - terraform fmt -check # Vérifier le formatage du code - terraform validate # Valider la syntaxe Terraform # Exécution en parallèle pour tous les environnements parallel: matrix: - ENVIRONMENT: [dev, staging, prod]
# Job de planification : créer les plans d'exécutionplan: extends: .terraform-base stage: plan script: - terraform plan -out=plan.tfplan # Créer le plan d'exécution # Sauvegarder le plan comme artefact pour l'étape suivante artifacts: paths: - ${TF_ROOT}/${ENVIRONMENT}/plan.tfplan expire_in: 7 days # Les plans expirent après 7 jours # Exécution en parallèle pour tous les environnements parallel: matrix: - ENVIRONMENT: [dev, staging, prod]
# Déploiement automatique sur dev (branche develop)deploy:dev: extends: .terraform-base stage: deploy environment: name: dev # Nom de l'environnement GitLab variables: ENVIRONMENT: dev script: - terraform apply plan.tfplan # Appliquer le plan sauvegardé dependencies: - plan # Dépend du job plan pour récupérer l'artefact only: - develop # Se déclenche uniquement sur la branche develop
# Déploiement automatique sur staging (branche main)deploy:staging: extends: .terraform-base stage: deploy environment: name: staging # Nom de l'environnement GitLab variables: ENVIRONMENT: staging script: - terraform apply plan.tfplan # Appliquer le plan sauvegardé dependencies: - plan # Dépend du job plan pour récupérer l'artefact only: - main # Se déclenche uniquement sur la branche main
# Déploiement manuel sur production (tags uniquement)deploy:prod: extends: .terraform-base stage: deploy environment: name: production # Nom de l'environnement GitLab variables: ENVIRONMENT: prod script: - terraform apply plan.tfplan # Appliquer le plan sauvegardé dependencies: - plan # Dépend du job plan pour récupérer l'artefact when: manual # Déclenchement manuel obligatoire pour la production only: - tags # Se déclenche uniquement sur les tags (releases)