Exercice 08 : Projet Capstone - Terraform en Production avec Workspaces, for_each et CI/CD
🎯 Objectifs
À la fin de cet exercice, vous serez capable de :
- ✅ Créer et basculer entre des workspaces Terraform (
dev,staging,prod) - ✅ Utiliser
for_eachpour créer plusieurs ressources dynamiquement - ✅ Utiliser des expressions conditionnelles pour adapter l'infrastructure par environnement
- ✅ Importer une ressource existante dans le state avec
terraform import - ✅ Intégrer
terraform planetterraform applydans un pipeline GitHub Actions
Durée estimée : 1h
Difficulté : ⭐⭐⭐⭐⭐ (Expert)
Prérequis :
- Exercices 06 et 07 Terraform complétés
- Module Terraform Avancé complété
📖 Contexte
Votre infrastructure Terraform est modulaire. Il est temps de l'industrialiser : gérer plusieurs environnements avec les workspaces, utiliser des méta-arguments avancés (for_each, conditionnels), importer des ressources existantes, et automatiser les déploiements via CI/CD.
📋 Énoncé
Industrialisez l'infrastructure Terraform avec les workspaces multi-environnements, les ressources dynamiques for_each, l'import d'une ressource existante, et un pipeline CI/CD Terraform.
🧭 Déroulement de l'exercice
Tâche 1 : Créer et utiliser des workspaces
Créez trois workspaces Terraform : dev, staging, prod. Basculez entre eux et observez comment terraform.workspace change le comportement.
Indice :
terraform workspace new dev,terraform workspace new staging,terraform workspace new prod.terraform workspace listaffiche les workspaces.terraform workspace select devbascule.terraform.workspaceest accessible dans les fichiers HCL pour conditionner les ressources.
Vérification : terraform workspace list affiche les 3 workspaces. terraform workspace show affiche le workspace courant.
Tâche 2 : Adapter l'infrastructure par workspace
Utilisez terraform.workspace dans une expression conditionnelle pour définir le nombre de replicas (1 en dev, 2 en staging, 3 en prod) et le port HTTP (8080 en dev, 9080 en staging, 80 en prod).
Indice :
`hcllocals {
replicas = terraform.workspace == "prod" ? 3 : (
terraform.workspace == "staging" ? 2 : 1
)
http_port = terraform.workspace == "prod" ? 80 : (
terraform.workspace == "staging" ? 9080 : 8080
)
}
`Chaque workspace a son propre state file, donc les ressources sont indépendantes par workspace.
Vérification : En workspace dev, terraform console → local.replicas retourne 1. En workspace prod, retourne 3.
Tâche 3 : Utiliser for_each pour plusieurs conteneurs
Définissez une variable services de type map(object) contenant plusieurs services Docker (nginx, httpd, redis). Utilisez for_each pour créer un conteneur Docker pour chaque service.
Indice :
`hclvariable "services" {
type = map(object({
image = string
port = number
}))
default = {
nginx = { image = "nginx:1.25-alpine", port = 8080 }
httpd = { image = "httpd:2.4-alpine", port = 8081 }
}
}
>
resource "docker_container" "services" {
for_each = var.services
name = "${each.key}-${terraform.workspace}"
image = docker_image.services[each.key].image_id
# ...
}
`
each.keyest la clé de la map (ex:nginx),each.valueest l'objet associé.
Vérification : terraform plan affiche une ressource par service défini dans la variable. terraform state list affiche docker_container.services["nginx"], etc.
Tâche 4 : Importer une ressource existante
Créez manuellement un réseau Docker avec docker network create legacy-network. Puis importez ce réseau dans le state Terraform sans le recréer.
Indice :
1. Créer en dehors de Terraform :
docker network create legacy-network2. Déclarer la ressource dans HCL :
resource "docker_network" "legacy" { name = "legacy-network" }3. Importer :
terraform import docker_network.legacy $(docker network inspect legacy-network --format '{{.ID}}')Après l'import, Terraform gère cette ressource sans la recréer.
Vérification : terraform state show docker_network.legacy affiche les détails du réseau importé. terraform plan affiche 0 to add pour cette ressource.
Tâche 5 : Créer le pipeline CI/CD GitHub Actions
Créez .github/workflows/terraform.yml qui :
- Se déclenche sur push vers
mainet surpull_request - Sur PR : exécute
terraform fmt -check,terraform validate,terraform planet poste le plan en commentaire - Sur push vers
main: exécuteterraform apply -auto-approve
Indice : Utilisez
hashicorp/setup-terraform@v3pour installer Terraform. Pour poster le plan dans la PR, redirigez la sortie avecterraform plan -out=tfplanetterraform show -no-color tfplan. Stockez le token backend (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY) comme secrets GitHub.
Vérification : Sur une PR, un commentaire automatique affiche le plan Terraform. Sur un push vers main, le workflow applique les changements.
Tâche 6 : Appliquer les bonnes pratiques de sécurité Terraform
Ajoutez dans main.tf :
- Un fichier
.terraform.lock.hclcommitté pour figer les versions des providers - Une contrainte
required_version = ">= 1.6"dans le blocterraform - Des
lifecycle { prevent_destroy = true }sur les ressources critiques en prod - Un
sensitive = truesur les outputs contenant des informations sensibles
Indice :
terraform providers lockgénère le fichier lock.lifecycle { prevent_destroy = true }empêcheterraform destroyde supprimer la ressource (erreur explicite).sensitive = truedans un output masque sa valeur dans les logs.
Vérification : terraform providers lock crée .terraform.lock.hcl. terraform destroy sur une ressource avec prevent_destroy = true retourne une erreur.
🗂️ Mini-Projet : Infrastructure multi-environnements avec CI/CD
Workflow Terraform CI/CD :
PR ouverte → fmt check + validate + plan (commentaire)
↓
Merge → apply (workspace = prod)Commandes workspaces :
# Initialiser
terraform init
# Créer les workspaces
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# Déployer en dev
terraform workspace select dev
terraform apply -var-file=dev.tfvars
# Déployer en staging
terraform workspace select staging
terraform apply -var-file=staging.tfvars
# Lister l'état par workspace
terraform workspace list
terraform state list
# Importer une ressource existante
docker network create legacy-network
terraform import docker_network.legacy $(docker network inspect legacy-network -f '{{.ID}}')
terraform state show docker_network.legacyCheckpoints de validation :
terraform workspace listaffichedev,staging,prodlocal.replicasvaut 1 en dev et 3 en prodterraform state listcontientdocker_container.services["nginx"]et["httpd"]terraform importréussit etterraform planaffiche0 to addpour la ressource importée.terraform.lock.hclest présent et commité- Le pipeline GitHub Actions poste un commentaire de plan sur la PR