Aller au contenu
Infrastructure as Code medium

Contraintes de version Terraform : opérateurs, lock file et pièges

15 min de lecture

logo terraform

Une contrainte de version encadre ce que Terraform accepte : sa propre version (required_version), celle des providers (required_providers), celle des modules. Bien posée, elle évite les montées de version cassantes ; mal comprise, elle laisse deux machines résoudre des versions différentes le même jour. Deux pièges dominent : le sens des opérateurs < et <=, et ce que le lock file suit réellement.

Ce guide traite les opérateurs, le pessimiste ~>, le lock file (providers, pas modules), l'intersection des contraintes de modules, ce que le bloc terraform accepte, et la preuve par version -json. Les comportements ont été vérifiés sur Terraform v1.15.4.

  • Les opérateurs, et le vrai sens de < et <=
  • Le pessimiste ~> et ses deux formes
  • Ce que le lock file suit : les providers, pas les modules
  • L'intersection des contraintes de modules enfants
  • Ce que le bloc terraform accepte, et la preuve par version -json

Une contrainte est une suite de comparaisons. Le tableau officiel :

OpérateurSens
= (ou aucun)exactement cette version ; ne se combine avec aucun autre
!=exclut une version
> >=borne inférieure (au moins cette version)
< <=borne supérieure (au plus cette version)
~>pessimiste (voir plus bas)

Le piège le plus courant, et une erreur qu'on lit souvent : < et <= posent une borne SUPÉRIEURE, ils excluent les versions plus récentes. L'exemple canonique de la doc le montre :

version = ">= 1.2.0, < 2.0.0"

>= 1.2.0 est le plancher, < 2.0.0 le plafond. Confondre le sens de < avec « une version plus récente » inverse toute la lecture d'une plage.

~> autorise les incréments du dernier segment indiqué, jamais celui d'au dessus. Sa portée change selon la précision :

  • ~> 5.0 autorise 5.1, 5.9, 5.99, mais pas 6.0 ;
  • ~> 5.0.2 autorise 5.0.3, 5.0.10, mais pas 5.1.0.

L'erreur fréquente est d'écrire ~> 1.0.0 en croyant autoriser les correctifs : cela verrouille la mineure 1.0.x, alors qu'on voulait probablement ~> 1.0 (toute la série 1.x).

Terraform ne fait pas correspondre les pré-releases (-beta1, -rc1) avec >, >=, <, <= ou ~> : une contrainte souple ne remonte jamais d'elle même sur une pré-release.

Voici le second piège, et il coûte cher. Le fichier .terraform.lock.hcl verrouille les versions des providers, et seulement d'eux :

At present, the dependency lock file tracks only provider dependencies.

Il n'enregistre aucune version de module distant. Poser version = "~> 5.0" sur un module et croire le lock file derrière soi est une erreur : Terraform « will always select the newest available module version that meets the specified version constraints ». D'une machine à l'autre, d'un jour à l'autre, la version de module résolue peut différer, alors que celle d'un provider est figée par le lock.

Le lock file se versionne (il se committe) : il « belongs to the configuration as a whole ». Pour chaque provider, il enregistre la version retenue, les contraintes considérées, et des empreintes sous deux schémas : h1: (préféré, calculé sur le contenu du paquet) et zh: (hérité, hash du .zip du registre).

Ne devinez pas ce qu'une contrainte a produit, lisez-le. terraform version -json expose provider_selections, l'adresse complète de chaque provider vers la version réellement retenue :

Fenêtre de terminal
terraform version -json | jq '.provider_selections'
{
"registry.terraform.io/hashicorp/local": "2.9.0",
"registry.terraform.io/hashicorp/random": "3.9.0"
}

Le lock file au quotidien : upgrade, plateformes, CI

Section intitulée « Le lock file au quotidien : upgrade, plateformes, CI »

Trois commandes que le sujet impose et qu'on oublie souvent :

  • terraform init -upgrade est requis pour toute montée de version, même à contrainte inchangée : « If a particular provider already has a selection recorded in the lock file, Terraform will always re-select that version [...] even if a newer version has become available. »
  • terraform providers lock -platform=linux_arm64 -platform=linux_amd64 pré-remplit les empreintes de plusieurs plateformes d'un coup. Sans elle, un lock écrit sur macOS fait échouer une CI Linux.
  • terraform init -lockfile=readonly vérifie les empreintes sans réécrire le fichier : c'est le mode CI. Le modèle de confiance est le trust on first use : Terraform échoue si un paquet ne correspond à aucune empreinte déjà enregistrée (« doesn't match any of the checksums previously recorded »).

Une configuration doit satisfaire toutes les contraintes, y compris celles déclarées dans les modules qu'elle appelle : « You must use a Terraform version that satisfies all version constraints associated with the configuration, including constraints defined in modules », faute de quoi Terraform « prints an error and exits ». Raisonner sur un seul niveau est une erreur : l'intersection des contraintes de tous les modules doit être non vide.

Deux précisions sur les modules :

  • required_version ne couvre que le CLI, pas les providers : « applies only to the version of Terraform CLI and not versions of provider plugins ».
  • L'argument version d'un bloc module ne fonctionne qu'avec une source de registre. Un module en chemin local, Git ou S3 ne l'accepte pas, et « local modules always share the same version as their caller ».

Dernier point, source d'erreur au premier réflexe de « paramétrer » sa contrainte : le bloc terraform ne peut référencer aucun objet nommé.

terraform {
required_version = var.v # interdit
}
Error: Variables not allowed

Vérifié en 1.15.4 : une variable, un local ou un appel de fonction dans le bloc terraform fait échouer init. La contrainte est forcément un littéral.

Ces symptômes touchent aux opérateurs, au lock file ou au bloc terraform.

SymptômeCauseSolution
Une contrainte < 2.0.0 refuse une version attendue< est une borne supérieureRelire le sens : < exclut les versions plus récentes
Deux machines résolvent des modules différentsLe lock file ne suit pas les modulesÉpingler le module avec =, ou vendoriser
Une nouvelle version de provider n'est pas priseLe lock re-sélectionne la version enregistréeterraform init -upgrade
Une CI Linux échoue sur un lock écrit sur macOSEmpreintes d'une seule plateformeterraform providers lock -platform=...
Variables not allowed dans le bloc terraformUne variable ou fonction dans la contrainteLa remplacer par un littéral
  1. < et <= sont des bornes SUPÉRIEURES ; > >= des bornes inférieures.
  2. = ne se combine avec rien ; les pré-releases ne matchent pas les opérateurs de plage.
  3. ~> 5.0 = toute la 5.x ; ~> 5.0.2 = la 5.0.x seulement.
  4. Le lock file suit les providers, pas les modules, et se committe.
  5. terraform init -upgrade est requis pour toute montée ; -lockfile=readonly vérifie en CI ; providers lock -platform couvre plusieurs OS.
  6. Les contraintes des modules s'additionnent ; required_version ne couvre que le CLI.
  7. Le bloc terraform n'accepte que des constantes ; version -json prouve la sélection.

Les questions ci-dessous reprennent les confusions les plus fréquentes sur les contraintes de version : le sens de <, ce que suit le lock file, et le pessimiste ~>.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn