infra / terraform
Modules : créer et réutiliser
Explication
Ce que vous allez apprendre
- Structurer un module Terraform réutilisable avec ses propres variables et outputs
- Appeler un même module plusieurs fois avec des paramètres différents
- Référencer les sorties d'un module depuis la racine du projet
- Utiliser un module public du Terraform Registry plutôt que d'en réécrire un
- Savoir quand
terraform getest nécessaire en plus deterraform init
Dans quel contexte ?
L'équipe doit déployer un serveur web et un serveur worker, tous deux des instances EC2 très similaires (même AMI, même groupe de sécurité de base), qui ne diffèrent que par leur nom et leur taille. Copier-coller le même bloc resource deux fois fonctionne, mais la moindre correction de sécurité devrait alors être répétée manuellement partout où ce bloc a été dupliqué.
D'abord, il faut comprendre ce qu'est réellement un module
Un module Terraform n'est rien de plus qu'un dossier contenant des fichiers .tf, structuré comme n'importe quel projet Terraform : un main.tf avec les ressources, un variables.tf pour ses entrées paramétrables, et un outputs.tf pour exposer ce qui doit être réutilisé à l'extérieur. Même la configuration racine d'un projet Terraform est, techniquement, un module — le "module racine".
Cette symétrie explique pourquoi un module se comporte, une fois appelé, presque comme une ressource ordinaire : il prend des entrées (variables) et produit des sorties (outputs).
| Fichier | Rôle dans un module |
|---|---|
variables.tf | Entrées paramétrables du module (ce que l'appelant doit fournir) |
main.tf | Ressources créées par le module |
outputs.tf | Sorties consultables par l'appelant du module |
Prérequis
Une bonne maîtrise des blocs resource, variable et output (leçons précédentes) est indispensable : un module n'est qu'une façon d'organiser et de réutiliser ces mêmes briques.
Une fois le module écrit, comment l'utiliser concrètement ?
Un bloc module "nom_local" { source = "./modules/ec2-instance" ... } appelle le module en lui passant ses paramètres, exactement comme on passerait des arguments à une fonction. Rien n'empêche d'appeler le MÊME module plusieurs fois avec des paramètres différents — c'est exactement ce qui résout le problème de duplication posé en introduction.
Bonne pratique
Dès qu'un même schéma de ressources (une instance + son groupe de sécurité, par exemple) se répète plus de deux fois dans une configuration, c'est le signal qu'il devrait devenir un module. Cette règle simple évite à la fois la sur-ingénierie (module trop tôt, pour un usage unique) et la duplication incontrôlée.
Il reste une question de référencement : comment récupérer une sortie d'un module ?
Depuis la racine du projet, une sortie de module se référence avec le préfixe module.<nom_local>., exactement comme un attribut de ressource se référence avec type.nom.. Cette cohérence de syntaxe rend les modules faciles à composer les uns avec les autres.
Maintenant, une découverte qui change la donne : pas besoin de tout réécrire soi-même
Astuce
Le Terraform Registry héberge des milliers de modules communautaires, testés et versionnés, pour les besoins les plus courants — un VPC complet AWS (terraform-aws-modules/vpc/aws) en est l'exemple le plus utilisé au monde. Avant d'écrire un module depuis zéro pour un besoin standard, vérifie toujours si un module public de qualité existe déjà : réinventer un VPC complet représente des centaines de lignes de HCL déjà écrites, testées et maintenues par d'autres.
Le piège à connaître avant de continuer
Piège fréquent
Après avoir ajouté un nouveau module dans la configuration, terraform init seul suffit généralement, mais terraform get reste utile pour rafraîchir explicitement les modules sans relancer toute l'initialisation des providers — pratique lors d'itérations rapides sur un module en cours de développement.
Une fois les modules maîtrisés, la prochaine leçon explore les expressions et fonctions HCL qui permettent de rendre ces modules encore plus flexibles : boucles for, conditions, et génération dynamique de blocs.
Commandes & code
Modules : créer et réutiliser
# Structure d'un module réutilisable "modules/ec2-instance/"
modules/ec2-instance/
├── main.tf # ressources du module
├── variables.tf # entrées du module
└── outputs.tf # sorties du module# modules/ec2-instance/variables.tf
variable "nom" {
type = string
}
variable "instance_type" {
type = string
default = "t3.micro"
}
variable "ami_id" {
type = string
}
# modules/ec2-instance/main.tf
resource "aws_instance" "cette_instance" {
ami = var.ami_id
instance_type = var.instance_type
tags = {
Name = var.nom
}
}
resource "aws_security_group" "sg" {
name = "${var.nom}-sg"
# ... règles ingress/egress
}
# modules/ec2-instance/outputs.tf
output "instance_id" {
value = aws_instance.cette_instance.id
}
output "ip_privee" {
value = aws_instance.cette_instance.private_ip
}# racine du projet : appel du module, deux fois avec des paramètres différents
module "serveur_web" {
source = "./modules/ec2-instance"
nom = "web-prod"
instance_type = "t3.small"
ami_id = data.aws_ami.ubuntu_dernier.id
}
module "serveur_worker" {
source = "./modules/ec2-instance"
nom = "worker-prod"
instance_type = "t3.medium"
ami_id = data.aws_ami.ubuntu_dernier.id
}
# Référencer une sortie d'un module depuis la racine : module.<nom>.<output>
output "ip_web" {
value = module.serveur_web.ip_privee
}# Modules provenant du Registry public (versionnés, testés par la communauté)
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.8.1"
name = "vpc-prod"
cidr = "10.0.0.0/16"
azs = ["eu-west-3a", "eu-west-3b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = true
}terraform get # télécharge/actualise les modules référencés
terraform init # (télécharge aussi les modules au premier init)Résumé
- Un module est un dossier
.tfréutilisable avec ses propresvariables.tf/outputs.tf, appelé viamodule "nom" { source = ... }. - Le Terraform Registry héberge des modules communautaires versionnés (ex :
terraform-aws-modules/vpc/aws), à préférer à réinventer un VPC complet. - Les sorties d'un module se référencent en préfixant
module.<nom_local>..
Exercices pratiques
Mission : dupliquer un module sans dupliquer le code
Objectif : Réutiliser un module existant pour un nouveau besoin et raisonner sur l'encapsulation et le versionnage des modules.
Contexte
Le module ./modules/ec2-instance est déjà utilisé deux fois à la racine du projet, pour serveur_web et serveur_worker. L'équipe doit maintenant déployer un troisième serveur, serveur_scheduler, en t3.micro, sans dupliquer le contenu du module lui-même. Par ailleurs, le module public terraform-aws-modules/vpc/aws est appelé ailleurs dans le projet sans argument version.