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

Organiser un dépôt Terraform

12 min de lecture

logo terraform

Un projet Terraform bien organisé se structure en fichiers par responsabilité : versions.tf pour les providers, variables.tf pour les entrées, outputs.tf pour les sorties, et un ou plusieurs fichiers de ressources (network.tf, storage.tf…). Cette convention, utilisée par HashiCorp et la communauté, permet à n’importe quel contributeur d’ouvrir un projet et de savoir immédiatement où trouver chaque élément.

Prérequis : savoir écrire une configuration de base avec resource, variable et output. Avoir lu Séparer dev, staging et prod pour comprendre la structure modules + environnements.

  • Identifier les fichiers standard d’un projet Terraform et leur rôle
  • Découper un fichier monolithique en fichiers par responsabilité
  • Vérifier que le découpage ne casse rien avec terraform validate
  • Formater automatiquement le code avec terraform fmt
  • Configurer le .gitignore pour ne versionner que l’utile

Un projet Terraform débute souvent par un seul main.tf qui contient tout : providers, variables, ressources et outputs. À 50 lignes, c’est lisible. À 200 lignes, c’est pénible. À 500 lignes, c’est ingérable.

Voici un exemple de fichier monolithique de 100 lignes :

mon-projet/
└── main.tf ← tout est ici : providers, variables, 3 ressources, outputs

Le problème n’est pas technique (Terraform s’en moque), mais humain :

  • Impossible de scanner rapidement : où sont les variables ? les outputs ?
  • Conflits Git fréquents : deux personnes modifient le même fichier
  • Revues de code pénibles : il faut lire 100 lignes pour trouver le changement

Terraform charge automatiquement tous les fichiers .tf d’un répertoire. L’organisation en fichiers est une convention pour les humains, pas une contrainte technique.

FichierContenuObligatoire ?
versions.tfBloc terraform {} (version requise, providers), bloc provider {}Oui
backend.tfBackend distant ou configuration de state dédiée à l’environnementSi backend distant
variables.tfToutes les variable {}Oui
outputs.tfTous les output {}Oui
main.tfRessources principales (si projet simple)Selon la taille
network.tfRessources réseauSelon le domaine
storage.tfRessources stockageSelon le domaine
compute.tfVMs, instancesSelon le domaine
locals.tfBloc locals {}Si nécessaire
data.tfBlocs data {} (data sources)Si nécessaire
terraform.tfvarsValeurs par défaut des variablesOptionnel
.gitignoreFichiers à exclure du versionnementOui
  1. Extraire versions.tf

    Déplacez le bloc terraform {} et le bloc provider {} dans versions.tf :

    versions.tf
    terraform {
    required_version = ">= 1.11.0"
    required_providers {
    libvirt = {
    source = "dmacvicar/libvirt"
    version = "~> 0.8"
    }
    }
    }
    provider "libvirt" {
    uri = "qemu:///system"
    }
  2. Extraire variables.tf

    Regroupez toutes les déclarations variable {} :

    variables.tf
    variable "env_name" {
    description = "Nom de l'environnement"
    type = string
    default = "demo"
    }
    variable "base_image_path" {
    description = "Chemin vers l'image de base"
    type = string
    default = "/chemin/vers/image.qcow2"
    }
  3. Séparer les ressources par domaine

    Un fichier par domaine de responsabilité :

    # network.tf — tout ce qui touche au réseau
    resource "libvirt_network" "main" {
    name = "${var.env_name}-network"
    autostart = true
    forward = {
    mode = "nat"
    }
    dns = {
    enabled = true
    }
    ips = [{
    address = "10.10.80.1"
    netmask = "255.255.255.0"
    dhcp = {
    ranges = [{
    start = "10.10.80.10"
    end = "10.10.80.100"
    }]
    }
    }]
    }
    # storage.tf — tout ce qui touche aux volumes
    resource "libvirt_volume" "system" {
    name = "${var.env_name}-system.qcow2"
    pool = "default"
    backing_store = {
    path = var.base_image_path
    format = { type = "qcow2" }
    }
    target = {
    format = { type = "qcow2" }
    }
    capacity = 4 * 1024 * 1024 * 1024
    }
    resource "libvirt_volume" "data" {
    name = "${var.env_name}-data.qcow2"
    pool = "default"
    target = {
    format = { type = "qcow2" }
    }
    capacity = 8 * 1024 * 1024 * 1024
    }
  4. Extraire outputs.tf

    outputs.tf
    output "network_name" {
    description = "Nom du réseau"
    value = libvirt_network.main.name
    }
    output "system_volume" {
    description = "Chemin du volume système"
    value = libvirt_volume.system.id
    }
    output "data_volume" {
    description = "Chemin du volume données"
    value = libvirt_volume.data.id
    }
  5. Valider le découpage

    Terraform doit produire exactement le même plan qu’avant le découpage :

    Fenêtre de terminal
    terraform validate
    Success! The configuration is valid.

    Le découpage est une opération sans risque : si validate passe, le plan sera identique.

Le résultat après découpage :

mon-projet/
├── versions.tf 14 lignes — providers
├── variables.tf 11 lignes — entrées
├── network.tf 23 lignes — réseau
├── storage.tf 32 lignes — stockage
└── outputs.tf 14 lignes — sorties

Chaque fichier fait moins de 35 lignes et a une responsabilité claire. Ouvrir network.tf montre immédiatement la configuration réseau, sans bruit.

Terraform fournit un formateur intégré qui aligne les =, indente correctement et normalise les espaces.

Le mode --check signale les fichiers mal formatés sans les toucher :

Fenêtre de terminal
terraform fmt -check

Si un fichier est mal formaté, Terraform affiche son nom et renvoie un code de sortie non-zéro — utile en CI/CD.

Le mode -diff montre ce qui serait modifié :

Fenêtre de terminal
terraform fmt -diff
--- old/variables.tf
+++ new/variables.tf
@@ -1,11 +1,11 @@
variable "env_name" {
-description="Nom de l'environnement"
- type=string
- default = "demo"
+ description = "Nom de l'environnement"
+ type = string
+ default = "demo"
}
variable "base_image_path" {
- description = "Chemin vers l'image de base"
-type = string
+ description = "Chemin vers l'image de base"
+ type = string
}

Sans option, terraform fmt corrige les fichiers directement :

Fenêtre de terminal
terraform fmt

Certains fichiers et répertoires ne doivent jamais être versionnés :

# Répertoire des plugins téléchargés (volumineux, recréé par terraform init)
.terraform/
# State local (contient des données sensibles)
*.tfstate
*.tfstate.*
# Fichiers de sauvegarde
*.backup
# Variables sensibles (mots de passe, tokens)
*.auto.tfvars
secrets.tfvars
# Fichiers de crash Terraform
crash.log
crash.*.log
# Fichiers de plan (binaires)
*.tfplan
# Fichiers générés par override (usage avancé)
override.tf
override.tf.json
*_override.tf
*_override.tf.json

Pour un projet avec modules et environnements (en combinant les conventions vues dans les guides précédents) :

mon-projet/
├── .gitignore
├── .terraform.lock.hcl ← Versionné !
├── README.md
├── modules/
│ └── reseau/
│ ├── versions.tf
│ ├── variables.tf
│ ├── main.tf
│ └── outputs.tf
└── envs/
├── dev/
│ ├── backend.tf
│ ├── versions.tf
│ ├── main.tf ← Appelle le module
│ ├── outputs.tf
│ └── terraform.tfvars
├── staging/
│ ├── backend.tf
│ ├── versions.tf
│ ├── main.tf
│ ├── outputs.tf
│ └── terraform.tfvars
└── prod/
├── backend.tf
├── versions.tf
├── main.tf
├── outputs.tf
└── terraform.tfvars
ErreurPourquoi c’est un problèmeSolution
main.tf de plus de 200 lignesIllisible, conflits Git, revues péniblesDécouper par domaine (network.tf, storage.tf…)
Variables déclarées dans main.tfImpossible de les trouver rapidementToujours dans variables.tf
Provider dans main.tfMélange providers/ressourcesToujours dans versions.tf
.terraform/ versionnéRépertoire volumineux, spécifique à la machineAjouter .terraform/ au .gitignore
.terraform.lock.hcl dans .gitignoreVersions de providers différentes selon les machinesRetirer du .gitignore, versionner
terraform.tfstate versionnéDonnées sensibles exposées dans GitAjouter *.tfstate au .gitignore + utiliser un backend distant
Fichiers nommés au hasard (infra.tf, stuff.tf)Aucune convention reconnaissableUtiliser les noms standards de la communauté
  • Un fichier par responsabilité : versions.tf, variables.tf, outputs.tf, et des fichiers de ressources par domaine
  • backend.tf mérite son propre fichier dès que vous utilisez un backend distant ou une configuration différente selon l’environnement
  • Le découpage est sans risque : terraform validate confirme que le plan reste identique
  • terraform fmt formate automatiquement le code — à utiliser en CI/CD et pre-commit
  • Le .terraform.lock.hcl se versionne, le .terraform/ et les *.tfstate non
  • Ces conventions permettent à n’importe qui d’ouvrir un projet Terraform et de s’y retrouver immédiatement

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