Aller au contenu
Sources, versions et registres de modules

Sources, versions et registres de modules

300 Concevoir ⏱ 1 h 15 terraformopentofuscalewayiachcl

À la fin, vous saurez

  • Choisir la source d'un module (local, Git, registre, archive) selon son cycle de vie
  • Épingler un module par tag ou par empreinte de commit et expliquer la différence de garantie
  • Classer un changement de module en correctif, ajout ou changement cassant
  • Évaluer la qualité et le risque d'un module public avant de l'adopter
  • Expliquer ce que contient .terraform/modules et quand init le met à jour

Prérequis

Testé avec git 2.43 terraform 1.16.1 , vérifié le 5 octobre 2026

Pourquoi

Un module local (./modules/reseau) vit dans le même dépôt que ceux qui l'appellent. C'est parfait pour un projet, et insuffisant dès qu'un module est partagé. Quand le module reseau sert à la fois à signalements-iac et au dépôt d'un autre client, il vit dans son propre dépôt, lyneko-modules, et chaque appelant doit répondre à une question : quelle version ?

Sans réponse explicite, la réponse est « celle qui est à jour », et c'est le début des ennuis. Le mardi, un plan sur la production est propre ; le mercredi, quelqu'un fusionne un changement dans le module, et le même plan sur le même code appelant propose de remplacer le réseau privé. Rien dans le dépôt de production n'a changé : c'est l'ailleurs qui a bougé. Épingler les versions rend le plan reproductible : le même dépôt appelant produit le même plan, tant que personne ne décide de monter la version.

La question a une seconde face, de sécurité. Un module est du code exécuté avec les droits de la clé d'API qui applique. Charger un module depuis un registre public, c'est accepter le code de quelqu'un d'autre au prochain init. Cette leçon traite des deux faces : comment adresser et versionner ses propres modules, et comment décider de faire confiance à ceux des autres.

Les concepts

Les sources

Le champ source d'un bloc module dit où Terraform va chercher le code. Les principales formes :

SourceExempleVersion
Chemin local./modules/reseaucelle du dépôt courant
Registre publichashicorp/consul/awsargument version
Registre privéapp.terraform.io/lyneko/reseau/scalewayargument version
Git (HTTPS ou SSH)git::https://github.com/lyneko-formation/lyneko-modules.git//modules/reseau?ref=v1.2.0paramètre ref
Raccourci GitHubgithub.com/lyneko-formation/lyneko-modules//modules/reseau?ref=v1.2.0paramètre ref
Archive (HTTP, S3)s3::https://s3.fr-par.scw.cloud/lyneko-modules/reseau-1.2.0.zipdans le nom du fichier

Quelques précisions utiles :

  • Le double slash // sépare l'adresse du dépôt du sous-répertoire à utiliser : ...lyneko-modules.git//modules/reseau signifie « clone ce dépôt, puis prends le dossier modules/reseau ». Sans lui, Terraform cherche le module à la racine du dépôt.
  • Le préfixe git:: force l'utilisation de Git pour une URL HTTPS qu'il prendrait sinon pour une archive. Pour SSH, git::ssh://git@github.com/....
  • ref accepte tout ce que git checkout comprend : une branche, un tag, une empreinte de commit (SHA-1). Le paramètre depth permet un clone superficiel (?depth=1), utile pour un dépôt volumineux.
  • Les archives (https://, s3::) servent aux organisations qui publient leurs modules sous forme de fichiers versionnés. Le téléchargement S3 s'appuie sur les identifiants d'accès de la chaîne AWS standard ; avec l'Object Storage de Scaleway, il passe par le point d'accès S3 de Scaleway. Ce montage n'a pas été exécuté pour cette leçon : vérifiez-le dans la documentation avant d'en dépendre.
  • Un chemin local doit commencer par ./ ou ../, faute de quoi Terraform y voit une adresse de registre.

Deux mécanismes de version, un seul par source

La distinction qui compte le plus dans cette leçon : l'argument version ne s'applique qu'aux modules de registre. Pour tout le reste, la version se choisit dans l'adresse elle-même.

  • Registre : version = "~> 1.2" est une contrainte (comme pour les fournisseurs, voir la leçon 4 du premier cours). Terraform interroge le registre, choisit la plus récente des versions compatibles, et la télécharge.
  • Git : ?ref=v1.2.0 désigne un point précis de l'historique. Il n'y a aucune résolution de contrainte : Terraform prend exactement ce que ref désigne.

Les mélanger est une erreur que Terraform signale (démonstration plus bas). Retenez qu'une source Git est toujours épinglée par construction, à condition que ref désigne quelque chose de stable, ce qui n'est pas toujours le cas.

Le versionnement sémantique appliqué à l'infrastructure

Le versionnement sémantique (semantic versioning, ou semver) numérote une version MAJEUR.MINEUR.CORRECTIF :

  • CORRECTIF (1.2.0 vers 1.2.1) : correction sans changement d'interface ;
  • MINEUR (1.2.0 vers 1.3.0) : ajout compatible ;
  • MAJEUR (1.2.0 vers 2.0.0) : changement incompatible.

Pour un module d'infrastructure, la notion d'incompatibilité est plus large que pour une bibliothèque, parce que le « programme » est un plan qui touche des ressources réelles. Sont cassants (donc majeurs) :

  • supprimer ou renommer une variable ou une sortie, changer leur type ;
  • ajouter une variable obligatoire (sans défaut) ;
  • changer un défaut de façon à modifier le comportement d'un appelant qui ne le précise pas ;
  • changer l'adresse d'une ressource sans moved : le plan détruit et recrée ;
  • faire passer une ressource en remplacement forcé (renommage d'un attribut qui ne se modifie pas en place) ;
  • relever la version minimale de Terraform ou d'un fournisseur au-delà de ce que les appelants utilisent.

Sont mineurs : ajouter une variable optionnelle avec un défaut neutre, une sortie, une ressource conditionnelle désactivée par défaut. Est un correctif : une correction qui ne change le plan d'aucun appelant existant (sauf pour corriger une erreur qu'il subissait).

Un test pratique : si je monte la version de ce module dans un dépôt appelant sans toucher autre chose, le plan doit-il rester vide ? Pour un correctif ou un mineur, oui. Sinon, c'est un majeur.

Notes de version

Une version sans notes oblige chaque appelant à lire le code différentiel. Un fichier CHANGELOG.md dans le dépôt de modules, tenu à jour à chaque tag, répond à trois questions : qu'est-ce qui change, est-ce cassant, que faire ? Pour un changement cassant, la note décrit la migration : les blocs moved à ajouter, les variables à renommer, l'ordre des opérations. L'argument deprecated des variables et sorties (leçon 2) prépare le terrain pendant une version mineure.

Le registre public

Le registre Terraform (registry.terraform.io) héberge des milliers de modules publics. Une adresse de registre a la forme <ESPACE>/<NOM>/<FOURNISSEUR>, par exemple terraform-aws-modules/vpc/aws. Un module est publié depuis un dépôt GitHub public nommé terraform-<FOURNISSEUR>-<NOM>, et chaque tag conforme au versionnement sémantique (v1.2.3 ou 1.2.3) devient une version. Il faut au moins un tag pour publier. Le registre offre une recherche, de la documentation générée, et un badge « vérifié » pour les modules de partenaires HashiCorp.

Les modules du registre ne sont pas signés : contrairement aux fournisseurs, dont le fichier de verrouillage enregistre des empreintes et dont les binaires sont vérifiés, un module téléchargé n'a aucune garantie cryptographique d'intégrité au-delà du transport. C'est le point central de la section sur la chaîne d'approvisionnement.

Les registres privés

HCP Terraform, Terraform Enterprise, certaines forges (GitLab) et gestionnaires d'artefacts offrent un registre privé qui parle le protocole de registre de modules : découverte de services (/.well-known/terraform.json, clé modules.v1), liste des versions (/v1/modules/<espace>/<nom>/<fournisseur>/versions), téléchargement par en-tête X-Terraform-Get. L'authentification passe par un jeton (fichier credentials de la CLI ou TF_TOKEN_<hôte>). OpenTofu a son registre public, registry.opentofu.org, avec la même syntaxe. Pour une petite équipe, un dépôt Git avec des tags suffit : c'est le choix fait pour lyneko-modules.

Le cache : .terraform/modules

terraform init télécharge chaque module dans .terraform/modules/<nom>/ et écrit .terraform/modules/modules.json, qui relie chaque appel à son répertoire et à sa source exacte. Les commandes suivantes (plan, apply) lisent cette copie locale : elles ne retéléchargent rien. Le répertoire .terraform/ ne se versionne jamais, et il est propre à chaque copie de travail : chaque poste et chaque exécution de CI en a un.

En pratique

Un dépôt de modules versionné

Le dépôt lyneko-modules regroupe les modules du cours :

lyneko-modules/
├── CHANGELOG.md
├── modules/
│   ├── reseau/
│   ├── app/
│   └── base/

Chaque version est un tag Git. Comme un seul dépôt porte plusieurs modules, on met le nom du module dans le tag : reseau-v1.2.0, app-v0.4.1. Git accepte n'importe quelle chaîne comme tag, et ref=reseau-v1.2.0 la comprend. (Cette convention a une limite : le registre, public ou privé, attend un dépôt par module et des tags vX.Y.Z nus. Si vous publiez un jour dans un registre, scindez le dépôt.)

Un exemple minimal, reproduit dans le bac à sable avec un vrai dépôt Git local, montre le cycle. Un module nommage d'une sortie, tagué v1.0.0, puis enrichi d'une seconde sortie, tagué v1.1.0 :

$ git rev-parse v1.0.0 v1.1.0 HEAD
9ea847952bf925d3f633f6e3580725c008f43d85
4253ba3ccdfb433fabdc5390362a8efbec76889f
4253ba3ccdfb433fabdc5390362a8efbec76889f

L'appelant épingle la version 1.0.0 :

module "nommage" {
  source = "git::file:///chemin/lyneko-modules//modules/nommage?ref=v1.0.0"
  env    = "preprod"
}

Dans un vrai dépôt, l'adresse serait git::https://github.com/lyneko-formation/lyneko-modules.git//modules/nommage?ref=v1.0.0 ou son équivalent SSH ; le protocole file:// ne sert qu'à cette démonstration. terraform init clone et enregistre :

$ terraform init -no-color
Initializing the backend...

Initializing modules...
Downloading git::file:///chemin/lyneko-modules?ref=v1.0.0 for nommage...
- nommage in .terraform/modules/nommage/modules/nommage
$ cat .terraform/modules/modules.json
{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"nommage","Source":"git::file:///chemin/lyneko-modules//modules/nommage?ref=v1.0.0","Dir":".terraform/modules/nommage/modules/nommage"}]}

Remarquez la clé Dir : .terraform/modules/nommage/modules/nommage. Terraform a cloné le dépôt entier dans .terraform/modules/nommage/ et utilise le sous-répertoire modules/nommage, d'où le // de la source.

Monter de version

Passer à v1.1.0 consiste à changer la valeur de ref. Mais un plan lancé tout de suite est refusé :

$ terraform plan -no-color
Error: Module source has changed

  on main.tf line 2, in module "nommage":
   2:   source = "git::file:///chemin/lyneko-modules//modules/nommage?ref=v1.1.0"

The source address was changed since this module was installed. Run
"terraform init" to install all modules required by this configuration.

Le plan ne se sert jamais d'une version qui n'a pas été installée par init. Après terraform init, la copie en cache est celle de la version 1.1.0, et le plan l'utilise. Un ref qui n'existe pas échoue franchement :

$ terraform init -no-color
...
Downloading git::file:///chemin/lyneko-modules?ref=v9.9.9 for nommage...

Error: Failed to download module
...

Épingler par empreinte de commit

Un tag est une étiquette déplaçable : git tag -f v1.0.0 <autre-commit> le repointe sans prévenir, et git push --force publie ce déplacement. Une empreinte de commit (40 caractères hexadécimaux) désigne un contenu précis : modifier le commit change l'empreinte. Pour une garantie forte, on épingle par empreinte et on garde la version en commentaire :

module "nommage" {
  # v1.0.0
  source = "git::https://github.com/lyneko-formation/lyneko-modules.git//modules/nommage?ref=9ea847952bf925d3f633f6e3580725c008f43d85"
  env    = "preprod"
}

Dans le bac à sable, cet épinglage donne bien le contenu de la version 1.0.0 :

$ terraform init -no-color
...
Downloading git::file:///chemin/lyneko-modules?ref=9ea847952bf925d3f633f6e3580725c008f43d85 for nommage...
- nommage in .terraform/modules/nommage/modules/nommage
$ terraform apply -auto-approve -no-color
...
Outputs:

prefixe = "sig-preprod"

Le coût est la lisibilité : l'empreinte ne dit rien sur la version, d'où le commentaire. Les outils de mise à jour automatique des dépendances (Renovate, par exemple, qui comprend les sources de modules Terraform) savent maintenir les deux ensemble.

Le piège du tag déplacé

Pour voir ce que l'épinglage par tag ne garantit pas, on déplace le tag v1.0.0 vers le dernier commit (qui ajoute une sortie prefixe_court) puis on relance init sur un répertoire où ref=v1.0.0 est déjà installé :

$ git tag -f v1.0.0 HEAD
$ terraform init -no-color
Initializing modules...
$ terraform plan -no-color | grep -A2 "Changes to"
Changes to Outputs:
  - prefixe       = "sig-preprod" -> null
  + prefixe_court = "absent"

L'init ordinaire n'a rien fait : la source n'a pas changé, donc la copie en cache est conservée, et le plan lit toujours l'ancienne version. La sortie prefixe_court n'existe pas dans la copie en cache (d'où "absent" : la démonstration la lit avec try()). C'est un cache qui masque le déplacement. Avec -upgrade, Terraform retélécharge :

$ terraform init -upgrade -no-color
Upgrading modules...
Downloading git::file:///chemin/lyneko-modules?ref=v1.0.0 for nommage...
$ terraform plan -no-color | grep -A2 "Changes to"
Changes to Outputs:
  - prefixe       = "sig-preprod" -> null
  + prefixe_court = "s-preprod"

Le plan voit maintenant le contenu du tag déplacé. Deux machines (le poste du développeur, qui a un cache ancien, et la CI, qui part de zéro) peuvent donc planifier deux choses différentes avec la même source. C'est l'argument pour l'empreinte de commit, ou, à défaut, pour des tags protégés (interdiction de réécrire) dans la forge.

La version sur une source Git : une erreur

Ajouter version = "~> 1.0" à une source Git ne réalise pas ce que l'on pourrait croire :

$ terraform validate -no-color
Error: Invalid registry module source address

  on main.tf line 2, in module "nommage":
   2:   source = "git::file:///chemin/lyneko-modules//modules/nommage?ref=..."

Failed to parse module registry address: a module registry source address
must have either three or four slash-separated components.

Terraform assumed that you intended a module registry source address because
you also set the argument "version", which applies only to registry modules.

Le message confirme la règle : version implique un registre. Pour Git, on ne met que ref.

Une version choisie par variable (Terraform 1.15)

On voudrait parfois que la version du module soit une variable, pour la fixer par environnement. Terraform l'interdit historiquement : la source doit être un littéral, car elle est résolue par init, avant l'évaluation de la configuration. Terraform 1.15 a levé la limite pour les variables constantes (const = true), dont la valeur est donnée dès init :

variable "version_modules" {
  type  = string
  const = true
}

module "nommage" {
  source = "git::https://github.com/lyneko-formation/lyneko-modules.git//modules/nommage?ref=${var.version_modules}"
  env    = "preprod"
}

Sans valeur, terraform init s'arrête sur No value for required variable ; avec terraform init -var version_modules=v1.1.0, le module est téléchargé, et plan doit recevoir la même valeur (testé avec Terraform 1.16.1). OpenTofu permet des variables et des valeurs locales dans les sources de modules depuis sa version 1.8, avec ses propres limites : consultez sa documentation pour la syntaxe exacte. Dans les deux outils, c'est un outil de dernier recours, qui rend la configuration plus difficile à lire et à analyser par les outils de mise à jour automatique ; un fichier de valeurs de variables par environnement est souvent plus simple qu'une version de module différente par environnement.

Authentifier l'accès à un dépôt privé

lyneko-modules est privé. Terraform appelle git, qui utilise la configuration de la machine :

  • SSH : git::ssh://git@github.com/lyneko-formation/lyneko-modules.git//..., avec une clé de déploiement en lecture seule (deploy key) dans l'agent SSH de la CI ;

  • HTTPS : git::https://github.com/..., avec un jeton fourni à Git par la configuration plutôt que dans l'URL :

    $ git config --global url."https://x-access-token:${JETON}@github.com/".insteadOf "https://github.com/"
    

Ne mettez jamais le jeton dans la source : l'adresse est écrite en clair dans .terraform/modules/modules.json, dans les journaux d'init, et dans les messages d'erreur. Elle est aussi versionnée avec le dépôt appelant.

Évaluer un module public

Avant d'écrire source = "terraform-aws-modules/..." (ou n'importe quel module d'un tiers), traitez-le comme une dépendance logicielle, avec en plus le fait qu'il crée de l'infrastructure. Une grille de lecture courte :

  1. Qui le maintient ? Une organisation connue ou un individu, nombre de mainteneurs, présence d'une politique de sécurité (SECURITY.md).
  2. Quelle vitalité ? Date de la dernière version, fréquence des versions, nombre de demandes ouvertes sans réponse, réactivité aux alertes.
  3. Quelle étendue ? Un module qui crée un bucket est limité ; un module qui crée un compte, des rôles IAM et des clés d'API a un rayon d'impact énorme. Lisez la liste de ses ressources : un module « réseau » qui crée aussi une clé d'accès est un drapeau rouge.
  4. Quelles dépendances ? Il appelle souvent d'autres modules et déclare des fournisseurs : chaque appel est une confiance supplémentaire, que vous ne voyez pas dans votre dépôt.
  5. Que dit le code ? Lisez main.tf : provisioner, local-exec, external (qui exécutent des commandes sur le poste de l'opérateur), sources de données qui appellent des API tierces, fournisseurs http : autant de portes de sortie.
  6. Quelle qualité apparente ? Tests, exemples, CHANGELOG, respect du versionnement sémantique. L'outil OpenSSF Scorecard note la santé d'un dépôt (protection des branches, revue, publication signée).

La décision n'est pas binaire. Trois attitudes de la moins à la plus exigeante : épingler la version et relire les différences à chaque montée ; copier (vendoring) le module dans modules/ de votre dépôt, ce qui le rend vôtre à partir de cette version, avec les risques d'obsolescence que cela suppose ; ne pas utiliser et écrire le sien quand le module sert peu et que ce qu'il fait tient en 50 lignes. Pour un cours de cloud souverain, la question « où est le code et qui peut le modifier ? » pèse autant que la qualité.

Sous le capot

Terraform télécharge avec go-getter. La bibliothèque de téléchargement générique qu'utilise Terraform comprend les préfixes (git::, s3::, https::), les paramètres (ref, depth) et le // du sous-répertoire. Pour Git, elle exécute git clone (avec --depth si demandé), puis git checkout de la référence. C'est pourquoi la configuration Git de la machine (clés SSH, insteadOf, proxys) s'applique, et pourquoi git doit être installé dans l'image de CI.

Pour un registre, c'est un protocole en deux temps. init demande au registre la liste des versions du module, choisit la plus haute qui respecte la contrainte, demande l'adresse de téléchargement de cette version (le registre répond avec l'en-tête X-Terraform-Get, qui peut contenir une adresse Git ou une archive), puis télécharge avec go-getter. La version choisie n'est pas verrouillée quelque part.

Il n'y a pas de fichier de verrouillage pour les modules. Le fichier .terraform.lock.hcl enregistre la version et les empreintes des fournisseurs, jamais celles des modules. Pour un module de registre avec version = "~> 1.2", un init -upgrade (ou un init sur un répertoire neuf) peut choisir une version plus récente que la veille, sans trace dans le dépôt. Pour les modules, la reproductibilité vient de l'épinglage exact : version = "1.2.3" (sans opérateur) pour le registre, ref vers une empreinte ou un tag protégé pour Git.

init détecte les changements de source en comparant la source de chaque appel à celle de modules.json. Source identique : rien n'est retéléchargé (d'où le tag déplacé qui passe inaperçu). Source différente : retéléchargement, et le plan est refusé tant que init n'a pas été relancé.

Pièges courants

  • ref=main (ou toute branche). Chaque init -upgrade récupère le dernier commit : la reproductibilité disparaît. Réservez les branches au développement du module lui-même.
  • Les tags de version non protégés. Dans la forge, interdisez la suppression et la mise à jour des tags *-v*. Sans cela, une erreur de manipulation ou une compromission de compte réécrit une version déjà utilisée.
  • version sur une source Git. Erreur Invalid registry module source address : une forme par source.
  • init oublié après un changement de ref. Module source has changed : l'erreur est claire, et il suffit de relancer terraform init.
  • Un cache périmé en CI. Si vous mettez .terraform/ en cache entre deux exécutions, un tag déplacé ou une version relevée dans le registre n'est pas vu : ne mettez en cache que le répertoire des fournisseurs (TF_PLUGIN_CACHE_DIR), pas celui des modules.
  • Un seul tag pour plusieurs modules sans préfixe. v1.2.0 ne dit pas quel module a changé. Préfixez avec le nom du module.
  • Des changements cassants dans une version mineure. Un renommage de variable glissé dans 1.3.0 casse la production du client qui avait ~> 1.2 : tout changement cassant est un majeur, même si le code change peu.

Sécurité

  • Un module est du code exécuté avec vos droits. Il peut créer une clé d'accès, ouvrir un groupe de sécurité, déclarer une source de données external qui lance un programme. Le plan le montre pour les ressources, mais pas pour les external, local-exec ou les sources de données qui contactent un service tiers. Lire le plan ne suffit pas à juger un module externe.
  • La chaîne d'approvisionnement commence à init. Un tag réécrit, un dépôt transféré à un nouveau propriétaire, un compte de mainteneur compromis : le prochain init -upgrade apporte le code modifié chez vous. Épinglez par empreinte, relisez les différences à chaque montée, et exécutez les plans de montée sur un environnement jetable avant la production.
  • Moindre privilège pour le plan et pour l'apply. La clé qui planifie en intégration continue n'a pas besoin des droits d'écriture : un module piégé n'a alors que ceux du plan. Voir la leçon 12 du premier cours.
  • Aucun secret dans l'adresse d'une source (voir plus haut) ; les jetons se donnent à Git, pas à Terraform.
  • Dépôt de modules interne : protégez-le comme du code de production. Revue obligatoire, branche principale protégée, tags protégés, commits signés si votre équipe s'y astreint (git tag -s). Celui qui peut pousser dans lyneko-modules peut modifier l'infrastructure de tous les appelants au prochain init -upgrade.
  • Souveraineté. Un module public est téléchargé depuis des serveurs américains à chaque init ; un dépôt interne ou des modules copiés (vendored) réduisent la dépendance.

En production

  • Un processus de publication court et strict. Changement relu, CHANGELOG mis à jour, tests passants (leçon 5), tag créé par la CI depuis la branche principale, jamais à la main. Le tag est l'acte de publication.
  • Montées de version contrôlées. Les dépôts appelants montent chacun leur version, en commençant par la préproduction : le plan de la montée est lu comme celui de n'importe quel changement. Un outil de mise à jour automatique (Renovate, Dependabot) ouvre les demandes de fusion ; la revue humaine reste.
  • Des versions mineures nombreuses plutôt que des majeures rares. Un module qui ne publie un majeur que tous les deux ans accumule des migrations ingérables. Annoncez les dépréciations d'avance (leçon 2), et gardez les migrations de majeure courtes et documentées.
  • OpenTofu. Les adresses de registre, ref et // fonctionnent de la même manière ; le registre par défaut est registry.opentofu.org, et l'ensemble des modules publics n'y est pas nécessairement identique à celui de Terraform : vérifiez la présence de vos modules avant de migrer. Les sources avec variables sont arrivées dans OpenTofu 1.8, bien avant Terraform 1.15.

Exercices

Exercice 1 : classer des changements

Pour chaque changement du module reseau (version actuelle 1.4.2), donnez la version suivante : (a) corriger une faute dans la description d'une variable ; (b) ajouter la variable optionnelle mtu avec un défaut qui conserve le comportement actuel ; (c) renommer sous_reseau en plage ; (d) changer le défaut de adresse_repartiteur de 172.16.20.5 à 172.16.20.10 ; (e) ajouter un bloc moved après avoir renommé une ressource interne.

Solution

(a) 1.4.3 : correctif, aucun comportement ne change. (b) 1.5.0 : ajout compatible, le plan des appelants reste vide. (c) 2.0.0 : changement cassant ; un appelant qui passe sous_reseau n'a plus d'entrée. On peut préparer le terrain avec une version 1.5.0 qui garde les deux variables et déclare l'ancienne deprecated. (d) 2.0.0 : changer le défaut modifie l'adresse réservée pour les appelants qui ne le précisent pas, donc leur plan (remplacement d'une ressource IPAM). (e) 1.4.3 si le renommage interne est entièrement couvert par moved et que le plan des appelants reste vide ; sans moved, ce serait un majeur.

Exercice 2 : épingler et vérifier

Dans un dépôt Git de test, créez un module avec une sortie, taguez v1.0.0, appelez-le avec ref=v1.0.0, puis ajoutez un second commit, déplacez le tag dessus avec git tag -f, et relancez terraform init puis terraform init -upgrade. Que voyez-vous ? Reprenez ensuite avec ref=<empreinte> : que change l'épinglage par empreinte ?

Solution

Avec le tag, init ne fait rien (la source est inchangée, le cache est conservé) et le plan lit toujours l'ancien contenu ; init -upgrade retélécharge et le plan voit le nouveau contenu. Deux copies de travail (une avec cache, une sans) divergent donc sur la même source. Avec l'empreinte du premier commit, init et init -upgrade reproduisent le même contenu, quoi que l'on fasse au tag : l'empreinte est calculée à partir du contenu, et la modifier change l'adresse.

Exercice 3 : évaluer un module

Un collègue propose d'adopter exemple/reseau-scaleway/scaleway depuis le registre, avec version = "~> 2.0". Listez les vérifications que vous demandez avant d'accepter, et la forme finale de l'appel si vous l'acceptez.

Solution

Vérifications : maintenance (dernière version, mainteneurs, issues), liste des ressources créées (aucune clé d'accès, aucun rôle superflu), présence de provisioner, local-exec ou sources external dans le code, modules et fournisseurs dépendants, tests et CHANGELOG, santé du dépôt (Scorecard). Si accepté : version exacte (version = "2.3.1", pas ~> 2.0, car les modules n'ont pas de fichier de verrouillage), montée de version par demande de fusion avec lecture des différences, plan lancé sur la préproduction d'abord. Si le module est peu utilisé ou volumineux, copier son code dans modules/ et le maintenir soi-même est une alternative défendable.

Récapitulatif

  • Un module partagé vit dans son propre dépôt ; sa source dit où le trouver, et la version quelle révision utiliser.
  • version ne s'applique qu'aux registres ; pour Git, la version est dans ref (branche, tag ou empreinte de commit). Les mélanger est une erreur.
  • Le versionnement sémantique appliqué à l'infrastructure : est cassant tout ce qui change le plan d'un appelant existant (variable obligatoire, nouveau défaut, adresse sans moved, remplacement forcé).
  • Un tag se déplace ; une empreinte non. Pour une garantie forte, épinglez par empreinte et notez la version en commentaire, ou protégez les tags dans la forge.
  • terraform init télécharge dans .terraform/modules et ne retélécharge pas si la source est inchangée ; -upgrade force. Aucun fichier de verrouillage ne couvre les modules.
  • Un module public est une dépendance exécutée avec vos droits : évaluez le mainteneur, l'étendue, le code, épinglez exactement, et envisagez de copier ou d'écrire le vôtre.
  • Terraform 1.15 autorise des variables const dans les sources, OpenTofu des variables et valeurs locales depuis 1.8.

Pour aller plus loin

  • La documentation HashiCorp sur les sources de modules et sur la publication dans le registre.
  • La leçon 4 : composer des modules versionnés en couches et en environnements.
  • La leçon 5 : tester un module avant de le tagger.
  • Terraform: Up & Running, chapitre 4 : versionnement des modules et pratiques de publication.
Voir ma constellation →

Sources