Aller au contenu
Infrastructure as Code medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Variables et outputs d'un module Terraform : paramétrer et exposer

12 min de lecture

logo terraform

Un module sans variables est figé. Un module sans outputs est isolé. Les variables sont le mécanisme qui rend un module paramétrable — chaque appel peut fournir des valeurs différentes. Les outputs sont le mécanisme qui rend les résultats accessibles — sans eux, la configuration racine ne peut rien lire des ressources créées par le module.

Ce guide approfondit ces deux mécanismes : types simples et complexes (dont object), validations personnalisées, valeurs par défaut, et outputs structurés.

Prérequis : avoir lu Créer un module Terraform et Structure d’un module.

  • Utiliser les types simples (string, number, bool) et complexes (object)
  • Valider les entrées avec des règles personnalisées
  • Distinguer variables obligatoires et optionnelles via default
  • Exposer des outputs structurés depuis un module
  • Lire les outputs d’un module depuis la configuration racine

Chaque variable se déclare dans variables.tf avec cette structure :

variable "nom" {
type = string
# ↑ le type de donnée attendu
description = "Nom du réseau libvirt"
# ↑ documentation pour l'utilisateur du module
default = "mon-reseau"
# ↑ optionnel : si présent, la variable est optionnelle
}
AttributRôleObligatoire
typeType de la valeur attendueRecommandé (sinon any)
descriptionExplication pour l’utilisateurRecommandé
defaultValeur par défautNon — sans default, la variable est obligatoire
validationRègle de vérification personnaliséeNon

Les trois types de base couvrent la majorité des cas :

variable "nom" {
type = string
# ↑ texte : "dev-network", "10.10.80.0"
}
variable "memoire" {
type = number
default = 512
# ↑ nombre entier ou décimal : 512, 1024, 2.5
}
variable "activer_dns" {
type = bool
default = true
# ↑ vrai ou faux : true, false
}

Quand plusieurs variables forment un ensemble cohérent, le type object les regroupe en une seule structure :

La logique est : « plutôt que 3 variables séparées (adresse, masque, passerelle), je crée une variable unique cidr qui contient les trois ».

variable "cidr" {
type = object({
adresse = string
masque = string
})
description = "Bloc CIDR du réseau (adresse + masque)"
}

L’appelant passe les valeurs en bloc :

module "reseau_dev" {
source = "./modules/reseau"
nom = "dev-vars"
cidr = {
adresse = "10.10.84.0"
masque = "255.255.255.0"
}
# ...
}

Dans le module, on accède aux champs avec la notation pointée :

resource "libvirt_network" "this" {
# ...
ips = [{
address = var.cidr.adresse
# ↑ var.NOM_VARIABLE.CHAMP
netmask = var.cidr.masque
}]
}

La présence ou l’absence de default détermine si l’appelant doit fournir la valeur :

variable "nom" {
type = string
# Pas de default → OBLIGATOIRE
# L'appelant doit écrire : nom = "mon-reseau"
}
variable "activer_dns" {
type = bool
default = true
# default présent → OPTIONNEL
# Si l'appelant ne le spécifie pas, la valeur sera true
}

Si une variable obligatoire est omise, Terraform refuse de continuer :

Error: No value for required variable
on main.tf line 1:
The input variable "nom" is not set.

Les blocs validation permettent de rejeter des valeurs invalides avant le terraform plan, avec un message d’erreur personnalisé :

variable "nom" {
type = string
description = "Nom du réseau libvirt"
validation {
condition = length(var.nom) >= 3 && length(var.nom) <= 30
# ↑ expression booléenne : doit être true pour être valide
error_message = "Le nom doit contenir entre 3 et 30 caractères."
# ↑ message affiché si la condition est false
}
}

Résultat avec nom = "ab" :

Error: Invalid value for variable
on main.tf line 3, in module "reseau":
3: nom = "ab"
│ var.nom is "ab"
Le nom doit contenir entre 3 et 30 caractères.

La validation avec contains est utile pour les valeurs à choix multiples :

variable "mode_forward" {
type = string
default = "nat"
validation {
condition = contains(["nat", "route", "bridge", "none"], var.mode_forward)
error_message = "Le mode doit être nat, route, bridge ou none."
}
}

Résultat avec mode_forward = "invalide" :

Error: Invalid value for variable
on main.tf line 12, in module "reseau":
12: mode_forward = "invalide"
│ var.mode_forward is "invalide"
Le mode doit être nat, route, bridge ou none.

Sans outputs, les ressources créées par un module sont invisibles pour l’appelant. Même si le module crée un réseau avec un UUID, la configuration racine ne peut pas lire cet UUID — sauf si le module l’expose via un output.

Les outputs servent à :

  • Afficher des informations après terraform apply
  • Chaîner des modules entre eux (l’output de l’un devient l’input de l’autre)
  • Exporter des données pour des scripts ou d’autres outils
output "id" {
value = libvirt_network.this.id
# ↑ la valeur exposée (attribut d'une ressource)
description = "UUID du réseau créé"
}

Un output peut renvoyer un objet complet — utile pour résumer la configuration :

output "configuration" {
value = {
adresse = var.cidr.adresse
masque = var.cidr.masque
dns_actif = var.activer_dns
mode = var.mode_forward
}
description = "Résumé de la configuration réseau"
}

Résultat de terraform output reseau_config :

{
"adresse" = "10.10.84.0"
"dns_actif" = true
"masque" = "255.255.255.0"
"mode" = "nat"
}

Le format JSON est aussi disponible avec terraform output -json reseau_config :

{"adresse":"10.10.84.0","dns_actif":true,"masque":"255.255.255.0","mode":"nat"}

Depuis la configuration racine, la syntaxe est :

module.<NOM_DU_BLOC>.<NOM_OUTPUT>

Exemple concret :

output "reseau_id" {
value = module.reseau_dev.id
# ↑ module.reseau_dev = nom du bloc module
# ↑ .id = nom de l'output dans le module
}
output "reseau_config" {
value = module.reseau_dev.configuration
}

L’utilisation la plus puissante des outputs est le chaînage de modules : l’output d’un module devient l’input d’un autre.

module "reseau" {
source = "./modules/reseau"
nom = "infra-lab"
# ...
}
module "vm" {
source = "./modules/vm"
network_name = module.reseau.nom
# ↑ l'output "nom" du module reseau
# devient l'input "network_name" du module vm
}

Terraform détecte automatiquement cette dépendance : il créera le réseau avant la VM, sans avoir besoin de depends_on.

TypeExemple de valeurCas d’usage
string"dev-network"Noms, adresses IP, chemins
number512, 2.5Mémoire, CPU, compteurs
booltrue, falseActivation/désactivation de fonctionnalités
list(string)["a", "b"]Listes de noms, de tags
map(string){env = "dev"}Paires clé-valeur
object({...}){adresse = "...", masque = "..."}Structures avec champs nommés et typés
SymptômeCause probableSolution
No value for required variableVariable sans default non fournieAjouter l’argument dans le bloc module
Invalid value for variableLa validation a rejeté la valeurLire le error_message et corriger la valeur
This object does not have an attribute named "X"Tentative de lire un output inexistantVérifier le nom dans outputs.tf du module
Unsuitable value typeMauvais type passé (string au lieu de number)Vérifier le type dans variables.tf
module.X.Y videL’output existe mais la ressource n’a pas encore été crééeRelancer terraform apply
  1. Les variables sont les entrées d’un module — elles le rendent paramétrable
  2. Sans default, une variable est obligatoire ; avec default, elle est optionnelle
  3. Le type object regroupe des paramètres qui forment un ensemble cohérent
  4. Les validation bloquent les valeurs invalides avant le plan, avec un message clair
  5. Les outputs sont les sorties — sans eux, les ressources du module sont invisibles
  6. Le chaînage de modules passe par les outputs : module.A.output_name → input du module B
  7. Terraform crée automatiquement les dépendances entre modules chaînés

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn