Des modules Terraform qui passent à l'échelle avec votre équipe, pas contre elle
Un module Terraform, c'est la façon dont une équipe arrête de copier-coller de l'infrastructure. Mais un module qui n'est ni versionné, ni revu, ni testé ne fait pas passer l'équipe à l'échelle — il fait passer à l'échelle le rayon des dégâts. Une modification négligente d'un module 'vpc' partagé et chaque environnement qui le référence hérite du bug au prochain apply. La solution : cesser de traiter les modules comme des dossiers partagés et commencer à les traiter comme des produits versionnés.
Voici la version pratique : ce qui rend un module fiable, comment le publier via un vrai pipeline, et comment les équipes le consomment — en local et en CI — directement depuis GitHub, épinglé à un tag ou, quand la sécurité l'exige, à un commit hash immuable. Sans avoir besoin d'un registry privé.
Ce qui rend un module fiable
La fiabilité commence avant tout pipeline. Un module auquel d'autres équipes font confiance est petit et fait une seule chose — un réseau, une base de données, un service — pas une 'plateforme' qui fait tout. Il expose un ensemble clair et documenté d'inputs et d'outputs, embarque des defaults raisonnables, et épingle ses propres versions de provider pour se comporter pareil aujourd'hui et dans six mois.
- Responsabilité unique — un module, une préoccupation ; on les compose, plutôt que d'imbriquer un monolithe.
- Interface explicite — des variables typées avec descriptions et validation, et les outputs dont les autres modules ont vraiment besoin.
- Providers épinglés — required_providers avec contraintes de version, pour qu'une nouvelle version du provider ne change pas le comportement sans prévenir.
- Exemples et documentation — un dossier examples/ qui est aussi ce contre quoi tournent les tests.
- Pas de surprises — pas de noms, de régions ni d'IDs de compte codés en dur ; rien qui aille chercher des choses hors de ses inputs.
Rien de tout cela ne survit à une équipe qui grandit sans que deux choses soient non négociables : chaque changement est revu et chaque changement est testé contre une infrastructure réelle et jetable. Un module est le seul morceau de code dont les bugs sont multipliés par chaque consommateur — il mérite la barre la plus haute que vous ayez.
La version, c'est le contrat
À mesure que l'organisation grandit et que de nouveaux environnements apparaissent — un sandbox ici, une seconde région là — la version cesse d'être un luxe et devient le contrat entre le module et tous ceux qui l'utilisent. Chaque consommateur épingle une version explicite ; personne ne suit main. Le versionnage sémantique dit alors au consommateur ce que signifie un saut : un patch est sûr, un minor ajoute, un major l'oblige à changer du code.
Épingler, c'est aussi comme ça qu'on avance et qu'on recule en confiance. Promouvoir en production est un saut de version ; une mauvaise release est un saut retour vers la précédente. Sans versions, 'faire un rollback du module' veut vraiment dire 'aller retrouver à quel commit il était mardi dernier'.
Consommez-le depuis GitHub — sans registry
Vous n'avez besoin ni d'un registry privé ni d'Artifactory pour partager des modules correctement. Référencez le module directement par sa source Git et un ref. La même source fonctionne sur un portable et en CI ; l'authentification n'est qu'un token GitHub — un PAT en local, le token du runner en CI — branché une fois pour que les clones HTTPS portent la credential.
# main.tf — épingler à un tag publié
module "vpc" {
source = "git::https://github.com/acme/tf-modules.git//modules/vpc?ref=v1.4.0"
cidr_block = "10.20.0.0/16"
azs = ["eu-west-1a", "eu-west-1b"]
}La credential se définit une fois avec le insteadOf de git, donc ni votre code ni votre state ne contiennent de secret — Terraform se contente de cloner en HTTPS en votre nom :
# local + CI : laisser git utiliser un token pour github.com en HTTPS
git config --global url."https://oauth2:${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/"Immutabilité : épinglez un commit hash quand il doit rester verrouillé
Un tag est commode, mais il est mobile : quiconque a les droits d'écriture peut le repointer ou forcer v1.4.0 vers un autre code, et votre prochain apply le récupère. Pour la plupart des modules, ce compromis convient. Pour ceux qui ne peuvent pas bouger sous vos pieds — tout ce qui est sensible côté sécurité ou conformité — épinglez plutôt le commit hash complet. Un commit ne peut pas être échangé : ref=<sha> résout toujours exactement vers les octets que vous avez revus.
# verrouillé à un commit exact, impossible à échanger
module "kms" {
source = "git::https://github.com/acme/tf-modules.git//modules/kms?ref=3f9a1c4e8b7d6c5a4f3e2d1c0b9a8f7e6d5c4b3a"
}Publiez le module comme un produit
Si les consommateurs épinglent des versions, quelque chose doit produire ces versions de façon fiable — un pipeline dans le dépôt du module. Et il vaut la peine d'être précis : un module ne se 'déploie' pas. Il est validé, testé et publié. Le déploiement, c'est le travail du consommateur.
- À chaque pull request, lancez les vérifications statiques : terraform fmt -check, terraform validate et tflint.
- Puis testez pour de vrai — montez l'exemple du module dans un compte sandbox jetable, validez-le (terraform test, ou Terratest pour des vérifications plus riches) et détruisez-le. Un module qui n'a jamais été appliqué est un module non testé.
- Au merge sur main, laissez GitVersion calculer la prochaine version sémantique à partir de l'historique des commits, créer le tag Git correspondant, et publier — annoncez la release dans le canal Slack ou Teams de l'équipe (et miroir vers un registry, si d'aventure vous en avez un).
# .github/workflows/release.yml (esquisse)
on: { pull_request: {}, push: { branches: [main] } }
jobs:
check:
steps:
- run: terraform fmt -check -recursive
- run: terraform validate
- run: tflint
- run: terraform test # applique les exemples dans un sandbox et detruit
release:
if: github.ref == 'refs/heads/main'
needs: check
steps:
- uses: gittools/actions/gitversion/execute@v3 # -> prochaine semver
- run: gh release create "v${VERSION}"
- run: ./notify.sh "tf-modules ${VERSION} publie" # Slack / TeamsL'outil exact importe peu — GitVersion, release-please et semantic-release font tous l'affaire. Ce qui compte, c'est qu'une version ne commence à exister qu'après que le changement a été revu, validé et réellement appliqué.
Maintenant, utilisez-le : de Terraform à Terragrunt
Avec un module testé et taggé, un projet Terraform simple se contente de l'épingler — le bloc module ci-dessus, c'est toute l'histoire. Ça suffit pour un ou deux environnements. Mais dès que vous faites tourner la même stack en dev, staging et production, sur une ou plusieurs régions, copier-coller ce lien par environnement, c'est exactement la duplication que les modules étaient venus tuer.
Terragrunt garde les environnements DRY
Terragrunt enveloppe Terraform pour retirer cette répétition : il génère la configuration de backend et de provider, garde la version du module à un seul endroit par stack, et laisse chaque environnement n'être qu'un fichier mince qui dit 'ce module, cette version, ces inputs'. Promouvoir le long de la chaîne devient un saut de version d'une ligne.
# live/prod/eu-west-1/vpc/terragrunt.hcl
terraform {
source = "git::https://github.com/acme/tf-modules.git//modules/vpc?ref=v1.4.0"
}
include "root" { path = find_in_parent_folders() }
inputs = {
cidr_block = "10.30.0.0/16"
azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
}Une arborescence qui passe à l'échelle : projet / environnement / région
Organisez le dépôt live pour que le chemin soit l'adresse de l'infrastructure. Une façon délibérément ennuyeuse : environnement → région → composant — adaptez les niveaux à la façon dont votre organisation pense vraiment (certains séparent d'abord par projet, compte ou équipe) :
live/
├── root.hcl # state distant, provider, tags communs
├── dev/
│ └── eu-west-1/
│ ├── vpc/terragrunt.hcl
│ └── eks/terragrunt.hcl
├── staging/
│ └── eu-west-1/ ...
└── prod/
├── eu-west-1/
│ ├── vpc/terragrunt.hcl
│ └── eks/terragrunt.hcl
└── eu-central-1/ ...Chaque feuille épingle une version de module, et le dossier vous dit exactement ce qui tourne où. Mettre la production à jour devient une PR revue qui change un ref= de v1.4.0 à v1.5.0 — petit, évident et trivial à annuler.
Des modules fiables et à but unique ; revus et testés à chaque changement ; versionnés et publiés par un pipeline ; consommés depuis GitHub par tag — ou par commit hash immuable quand ça compte — et assemblés avec une arborescence Terragrunt qui reflète votre parc. Voilà la différence entre des modules qui passent à l'échelle avec votre équipe et des modules qui, en silence, passent à l'échelle contre elle.