TP 6 : Tester un module avec terraform test
Objectif
Section intitulée « Objectif »Créer un module Terraform simple, puis écrire des tests automatisés avec terraform test.
Le TP permet de tester :
- les variables d’entrée d’un module
- les outputs
- la logique interne d’un module
- les cas valides
- les cas invalides
- l’exécution automatique des tests
Terraform détecte les fichiers de test avec les extensions .tftest.hcl ou .tftest.json. Par défaut, terraform test cherche les tests dans le dossier courant et dans un sous-dossier tests/.
Prérequis
Section intitulée « Prérequis »- Terraform >= 1.6 installé (
terraform testest disponible depuis la version 1.6.0) - Un terminal
- Un éditeur de code
terraform versionLa version affichée doit être 1.6.0 ou supérieure. En dessous, la commande terraform test n’existe pas.
Sujet du TP
Section intitulée « Sujet du TP »Créer un module local qui génère un nom standardisé pour une ressource.
Exemples attendus :
app-dev-webapp-prod-apiapp-test-dbLe module ne crée pas d’infrastructure réelle — il est entièrement testable en local, sans provider ni compte cloud.
Arborescence
Section intitulée « Arborescence »tp-terraform-test/├── main.tf├── variables.tf├── outputs.tf├── tests/│ ├── naming_valid.tftest.hcl│ └── naming_invalid.tftest.hcl└── .gitignoreFichier .gitignore
Section intitulée « Fichier .gitignore ».terraform/*.tfstate*.tfstate.*.terraform.lock.hclFichier variables.tf
Section intitulée « Fichier variables.tf »variable "project" { description = "Nom du projet" type = string
validation { condition = length(var.project) >= 2 error_message = "Le nom du projet doit contenir au moins 2 caractères." }}
variable "environment" { description = "Environnement cible" type = string
validation { condition = contains(["dev", "test", "prod"], var.environment) error_message = "L'environnement doit être dev, test ou prod." }}
variable "component" { description = "Composant applicatif" type = string
validation { condition = length(var.component) >= 2 error_message = "Le composant doit contenir au moins 2 caractères." }}Fichier main.tf
Section intitulée « Fichier main.tf »terraform { required_version = ">= 1.6"}
locals { resource_name = lower("${var.project}-${var.environment}-${var.component}")}Le bloc
terraform { required_version = ">= 1.6" }est nécessaire pour que Terraform signale une erreur claire si la version utilisée ne supporte pasterraform test.Ce module ne déclare aucun provider car il ne crée aucune ressource réelle.
terraform initréussit sans télécharger de plugin.
Fichier outputs.tf
Section intitulée « Fichier outputs.tf »output "resource_name" { description = "Nom standardisé de la ressource" value = local.resource_name}
output "environment" { description = "Environnement utilisé" value = var.environment}Étape 1 : initialiser le projet
Section intitulée « Étape 1 : initialiser le projet »terraform initÉtape 2 : créer un premier test valide
Section intitulée « Étape 2 : créer un premier test valide »Créer le fichier tests/naming_valid.tftest.hcl :
run "valid_dev_web_name" { command = plan
variables { project = "app" environment = "dev" component = "web" }
assert { condition = output.resource_name == "app-dev-web" error_message = "Le nom généré devrait être app-dev-web." }
assert { condition = output.environment == "dev" error_message = "L'environnement devrait être dev." }}Un fichier
.tftest.hclcontient un ou plusieurs blocsrun. Chaque blocrunexécute une commande Terraform (planouapply) et évalue des assertions sur les outputs ou les attributs des ressources.
Étape 3 : exécuter les tests
Section intitulée « Étape 3 : exécuter les tests »terraform testRésultat attendu :
Success! 1 passed, 0 failed.Étape 4 : ajouter plusieurs cas valides
Section intitulée « Étape 4 : ajouter plusieurs cas valides »Remplacer le contenu de tests/naming_valid.tftest.hcl par :
run "valid_dev_web_name" { command = plan
variables { project = "app" environment = "dev" component = "web" }
assert { condition = output.resource_name == "app-dev-web" error_message = "Le nom généré devrait être app-dev-web." }}
run "valid_prod_api_name" { command = plan
variables { project = "app" environment = "prod" component = "api" }
assert { condition = output.resource_name == "app-prod-api" error_message = "Le nom généré devrait être app-prod-api." }}
run "valid_uppercase_input" { command = plan
variables { project = "APP" environment = "test" component = "DB" }
assert { condition = output.resource_name == "app-test-db" error_message = "Le nom généré devrait être en minuscules même si l'entrée est en majuscules." }}terraform testRésultat attendu :
Success! 3 passed, 0 failed.Étape 5 : tester un cas invalide
Section intitulée « Étape 5 : tester un cas invalide »Créer le fichier tests/naming_invalid.tftest.hcl :
run "invalid_environment" { command = plan
variables { project = "app" environment = "staging" component = "web" }
expect_failures = [ var.environment ]}
expect_failuresindique à Terraform que cette exécution doit échouer sur la validation devar.environment. Si le module échoue bien pour cette raison, le test est considéré réussi.
terraform testRésultat attendu :
Success! 4 passed, 0 failed.Étape 6 : ajouter un second test invalide
Section intitulée « Étape 6 : ajouter un second test invalide »Dans tests/naming_invalid.tftest.hcl, ajouter :
run "invalid_project_too_short" { command = plan
variables { project = "a" environment = "dev" component = "web" }
expect_failures = [ var.project ]}terraform testRésultat attendu :
Success! 5 passed, 0 failed.Étape 7 : provoquer volontairement une erreur
Section intitulée « Étape 7 : provoquer volontairement une erreur »Modification 1 — supprimer la normalisation en minuscules
Dans main.tf, remplacer :
locals { resource_name = lower("${var.project}-${var.environment}-${var.component}")}par :
locals { resource_name = "${var.project}-${var.environment}-${var.component}"}terraform testSeul le test valid_uppercase_input doit échouer. Remettre la version correcte avec lower().
Modification 2 — changer le séparateur
Remplacer les tirets par des underscores :
locals { resource_name = lower("${var.project}_${var.environment}_${var.component}")}terraform testTous les tests sur le format du nom échouent. Remettre la version correcte avant de continuer.
Étape 8 : afficher plus de détails
Section intitulée « Étape 8 : afficher plus de détails »terraform test -verboseAffiche chaque assertion évaluée et sa valeur — utile pour le débogage en cas d’échec.
Étape 9 : exécuter un seul fichier de test
Section intitulée « Étape 9 : exécuter un seul fichier de test »terraform test -filter=tests/naming_valid.tftest.hclterraform test -filter=tests/naming_invalid.tftest.hclUtile pour cibler un seul fichier lors du développement ou pour réexécuter uniquement les tests échoués.
Étape 10 : intégrer les tests dans un script local
Section intitulée « Étape 10 : intégrer les tests dans un script local »Créer un fichier test.sh :
#!/bin/bashset -e
echo "==> Vérification du formatage Terraform..."if ! terraform fmt -check; then echo "Erreur : des fichiers Terraform ne sont pas correctement formatés." echo "Lancer 'terraform fmt' pour corriger automatiquement." exit 1fi
echo "==> Validation de la configuration Terraform..."terraform validate
echo "==> Exécution des tests Terraform..."if [ "${1}" = "-v" ]; then terraform test -verboseelse terraform testfi
echo "==> Tous les tests sont passés."chmod +x test.sh./test.sh # exécution normale./test.sh -v # sortie détailléeVariante : tester un vrai module local
Section intitulée « Variante : tester un vrai module local »Pour structurer le projet avec un module séparé :
tp-terraform-test/├── modules/│ └── naming/│ ├── main.tf│ ├── variables.tf│ └── outputs.tf└── tests/ └── naming.tftest.hclDéplacer les fichiers dans modules/naming/, puis dans tests/naming.tftest.hcl :
run "test_naming_module" { command = plan
module { source = "./modules/naming" }
variables { project = "app" environment = "dev" component = "web" }
assert { condition = output.resource_name == "app-dev-web" error_message = "Le nom généré devrait être app-dev-web." }}terraform initterraform testVariante CI/CD GitLab
Section intitulée « Variante CI/CD GitLab »stages: - test
terraform_test: image: hashicorp/terraform:1.9 stage: test script: - terraform init - terraform fmt -check - terraform validate - terraform testL’image
hashicorp/terraform:1.9est épinglée sur une version majeure stable. Éviterlatestdans une pipeline.
Variante GitHub Actions
Section intitulée « Variante GitHub Actions »name: Terraform Tests
on: pull_request: push:
jobs: terraform-test: runs-on: ubuntu-latest
steps: - uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3 with: terraform_version: "1.9"
- name: Terraform init run: terraform init
- name: Terraform fmt run: terraform fmt -check
- name: Terraform validate run: terraform validate
- name: Terraform test run: terraform test