Terraform
Terraform décrit l'infrastructure dans des fichiers .tf (langage HCL). Il compare ce que décrit le code, ce que contient le state, et ce qui existe réellement, puis calcule les changements.
Le cycle de travail
terraform init # télécharge providers et modules, configure le backend
terraform fmt -recursive
terraform validate # syntaxe et cohérence
terraform plan -out=tfplan
terraform apply tfplan # applique exactement le plan revu
terraform destroy
Toujours appliquer un plan sauvegardé en CI : on applique ce qui a été relu, pas un nouveau calcul qui pourrait différer.
Le state
Le fichier terraform.tfstate associe chaque ressource du code à son ID réel dans le cloud. Sans lui, Terraform ne sait pas ce qu'il gère.
Règles :
- En équipe, jamais de state local. Backend distant avec verrouillage, pour que deux personnes n'appliquent pas en même temps.
- Le state contient souvent des secrets en clair (mots de passe de base générés, clés). Il faut restreindre l'accès au bucket.
- Versioning activé sur le bucket pour pouvoir revenir en arrière.
- Un state par environnement et par périmètre. Un state géant, c'est des plans lents et un rayon d'explosion énorme.
terraform {
backend "gcs" {
bucket = "renault-dbb-tfstate"
prefix = "kafka/prod"
}
}
Le backend GCS gère le verrouillage nativement.
Commandes de state
terraform state list
terraform state show google_compute_instance.web
terraform state mv google_compute_instance.web module.web.google_compute_instance.this
terraform state rm google_storage_bucket.legacy # Terraform l'oublie, sans le détruire
terraform import google_storage_bucket.logs mon-projet/logs-bucket
terraform force-unlock <LOCK_ID>
force-unlock : uniquement après avoir vérifié que personne n'est en train d'appliquer (un job CI planté a pu laisser le verrou).
Depuis Terraform 1.5, on peut importer en déclaratif, ce qui passe en revue de code :
import {
to = google_storage_bucket.logs
id = "mon-projet/logs-bucket"
}
Et moved {} remplace state mv pour les refactorings :
moved {
from = google_compute_instance.web
to = module.web.google_compute_instance.this
}
La dérive (drift)
Quelqu'un a modifié une ressource à la main dans la console. Le plan suivant montre la différence et propose de revenir à ce que dit le code.
Deux cas :
- La modif manuelle était une erreur ou un dépannage temporaire : on applique, le code reprend la main.
- La modif était légitime : on la reporte dans le code, puis le plan doit être vide.
terraform plan -refresh-only montre ce qui a changé côté cloud sans proposer de modifications. Un job planifié qui lance un plan chaque nuit et alerte s'il n'est pas vide détecte la dérive tôt.
Variables, outputs, locals
variable "env" {
type = string
description = "Environnement cible"
validation {
condition = contains(["dev", "staging", "prod"], var.env)
error_message = "env doit valoir dev, staging ou prod."
}
}
variable "db_password" {
type = string
sensitive = true
}
locals {
name_prefix = "dbb-${var.env}"
labels = {
env = var.env
owner = "digital-backbone"
}
}
output "vm_ip" {
value = google_compute_instance.web.network_interface[0].network_ip
}
Ordre de priorité des valeurs, du plus faible au plus fort : valeur par défaut, variables d'environnement TF_VAR_x, terraform.tfvars, *.auto.tfvars, puis -var et -var-file en ligne de commande (le dernier cité gagne).
sensitive = true cache la valeur dans l'affichage du plan, pas dans le state.
count contre for_each
# count : index numériques
resource "google_compute_instance" "broker" {
count = 3
name = "broker-${count.index}"
}
# for_each : clés stables
resource "google_compute_instance" "broker" {
for_each = toset(["a", "b", "c"])
name = "broker-${each.key}"
}
Avec count, si tu retires l'élément du milieu d'une liste, tous les suivants changent d'index : Terraform les détruit et les recrée. Avec for_each, chaque ressource est identifiée par sa clé, on ne touche qu'à celle qu'on retire.
count reste utile pour un booléen : count = var.enable_monitoring ? 1 : 0.
Modules
Un module = un dossier de .tf réutilisable avec des variables en entrée et des outputs en sortie.
module "network" {
source = "terraform-google-modules/network/google"
version = "~> 9.0"
project_id = var.project_id
network_name = "${local.name_prefix}-vpc"
subnets = [...]
}
Toujours épingler la version d'un module distant. Un module interne à l'entreprise se référence souvent par tag Git : source = "git::https://gitlab.renault.com/infra/modules.git//vpc?ref=v1.4.0".
Providers et versions
terraform {
required_version = ">= 1.6"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 6.0"
}
}
}
Le fichier .terraform.lock.hcl fige les versions exactes : à commiter.
lifecycle
resource "google_sql_database_instance" "main" {
lifecycle {
prevent_destroy = true # refuse tout plan qui détruirait la base
create_before_destroy = true # crée la nouvelle avant de supprimer l'ancienne
ignore_changes = [settings[0].tier] # ne pas revenir sur un changement fait ailleurs
}
}
Data sources
Lire une ressource existante sans la gérer :
data "google_compute_network" "shared" {
name = "shared-vpc"
project = "host-project"
}
Organiser plusieurs environnements
| Approche | Pour | Contre |
|---|---|---|
Un dossier par environnement (envs/dev, envs/prod) appelant les mêmes modules | Clair, states séparés, différences explicites | Un peu de duplication |
| Workspaces Terraform | Pas de duplication | Facile de se tromper de workspace, même backend |
| Terragrunt | Évite la duplication de config backend | Un outil de plus |
En entreprise, le dossier par environnement est le plus courant.
Terraform et OpenTofu
OpenTofu est le fork open source de Terraform, créé après le passage de Terraform sous licence BSL en 2023. Syntaxe quasi identique. Bon à savoir si on te pose la question.
Questions d'entretien à préparer
- Différence entre Terraform et Ansible ?
- C'est quoi le state, où le mets-tu, pourquoi le verrouiller ?
- Le state contient-il des secrets ? (oui, d'où l'accès restreint au bucket)
- Quelqu'un a modifié une ressource à la main, que se passe-t-il ?
countoufor_each, pourquoi ?- Comment importer une ressource existante ?
- Le verrou du state est bloqué après un job CI planté, que fais-tu ?
- Comment tu structures plusieurs environnements ?