Chaque ressource cloud que vous avez déjà créée en cliquant dans une console web (une VM, un load balancer, un enregistrement DNS) peut aussi être décrite dans un fichier texte et créée en une seule commande. Le fichier vit dans git, passe par une pull request, et reproduit le même setup sur un second compte sans que personne ait à se souvenir des neuf réglages changés à la main. C’est ce que fait Terraform.

Ce que fait vraiment Terraform

Terraform est un outil open-source de HashiCorp qui transforme l’infrastructure en fichiers de configuration, écrits en HCL (HashiCorp Configuration Language), et les applique à l’API d’un cloud provider. Vous écrivez un bloc resource décrivant le bucket S3, l’instance EC2 ou l’enregistrement DNS Cloudflare que vous voulez, vous lancez terraform apply, et Terraform calcule les appels API nécessaires pour que cette ressource existe — puis il en garde la trace, si bien qu’un second apply ne change que ce qui a réellement changé.

Le modèle est déclaratif, pas procédural : vous décrivez l’état final, pas les étapes pour y arriver. Vous n’écrivez pas “créez un bucket, puis définissez sa policy, puis activez le versioning”. Vous écrivez à quoi le bucket doit ressembler, et Terraform détermine l’ordre des opérations, y compris ce qui doit exister en premier quand une ressource dépend d’une autre.

Seul, Terraform ne sait pas parler à AWS, Azure ou Cloudflare. Cette connaissance vit dans les providers : des plugins qui traduisent les blocs HCL en appels vers une API spécifique. Le Terraform Registry en liste des milliers, officiels ou maintenus par la communauté, couvrant tout, des trois grands clouds aux réglages de repository GitHub, en passant par les moniteurs Datadog. Le même mécanisme crée un cluster Kubernetes managé (google_container_cluster, aws_eks_cluster) ou un simple enregistrement DNS devant un CDN. Si un service a une API, quelqu’un a sans doute déjà écrit un provider pour lui.

Comment fonctionnent terraform plan et apply

Un répertoire de travail minimal contient au moins deux fichiers.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# terraform.tf — les providers dont cette configuration a besoin
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  required_version = ">= 1.5"
}

provider "aws" {
  region = "eu-west-1"
}
1
2
3
4
5
6
7
8
9
# main.tf — la ressource elle-même
resource "aws_s3_bucket" "reports" {
  bucket = "acme-monthly-reports"

  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

Quatre commandes couvrent le cycle de travail quotidien :

1
2
3
4
terraform init     # télécharge le plugin du provider aws, configure le backend
terraform plan     # montre ce qui changerait, sans rien toucher
terraform apply    # demande confirmation, puis fait les appels API
terraform destroy  # détruit tout ce que cette configuration gère

terraform init lit required_providers et télécharge les binaires des plugins correspondants dans .terraform/. Vous le lancez une fois par répertoire, et à nouveau chaque fois que vous ajoutez un provider ou un module.

terraform plan est la commande que vous utiliserez le plus. Il compare vos fichiers .tf, le state enregistré et l’infrastructure réelle telle que rapportée par l’API du provider, puis affiche un diff : ressources à créer (+), modifier (~) ou détruire (-). Rien ne se passe encore. C’est ce qui rend Terraform sûr à utiliser en production : vous lisez le plan avant que quoi que ce soit ne soit touché.

terraform apply relance le plan, l’affiche de nouveau et attend que vous tapiez yes avant d’appeler les API du provider dans l’ordre de dépendance. En CI, vous passez -auto-approve ou, mieux, vous appliquez un fichier de plan déjà relu : terraform apply tfplan. Le même pipeline qui exécute Construire et Publier une Image Docker avec GitHub Actions à chaque merge peut lancer terraform plan sur une pull request et terraform apply une fois celle-ci mergée sur main.

À quoi sert le state file

Après le premier apply, Terraform écrit terraform.tfstate — un fichier JSON qui fait correspondre chaque ressource de votre configuration à l’objet réel qu’elle a créé, ID compris. Ce n’est pas de la comptabilité facultative. C’est ainsi que plan sait ce qui existe déjà sans rescanner tout votre compte AWS à chaque exécution, et ainsi que Terraform relie aws_s3_bucket.reports dans votre code au bucket acme-monthly-reports-a8f3 dans la réalité.

Deux conséquences en découlent directement.

Le state peut contenir des secrets. Un mot de passe de base de données défini comme argument d’une ressource finit en clair dans le fichier de state, car Terraform a besoin des attributs complets de la ressource pour détecter le drift. Ne commitez jamais terraform.tfstate dans git.

Le state doit être partagé et verrouillé. Par défaut, c’est un fichier local, qui marche tant que vous êtes seul et casse dès qu’une deuxième personne lance apply depuis son propre portable : il y a alors deux sources de vérité. La solution est un backend distant : le state est stocké sur S3, Azure Blob, GCS ou Terraform Cloud, avec un verrou qui empêche deux apply de s’exécuter en même temps.

1
2
3
4
5
6
7
8
9
terraform {
  backend "s3" {
    bucket       = "acme-terraform-state"
    key          = "reports/terraform.tfstate"
    region       = "eu-west-1"
    use_lockfile = true
    encrypt      = true
  }
}

use_lockfile est le verrouillage natif du backend S3, disponible de façon stable depuis Terraform 1.11 : il écrit un fichier de verrou directement dans le bucket, donc sur les versions actuelles une table DynamoDB dédiée au verrouillage n’est plus nécessaire.

Une fois que l’équipe pointe vers le même backend, le plan de chacun reflète le dernier apply des autres.

Comment réutiliser une configuration avec des variables et des modules

Coder en dur eu-west-1 et un nom de bucket fonctionne pour un exemple de cinq lignes, pas pour un environnement réel. Terraform sépare la forme de l’infrastructure des valeurs qui changent selon l’environnement.

1
2
3
4
5
6
7
8
9
# variables.tf
variable "environment" {
  type    = string
  default = "staging"
}

variable "bucket_name" {
  type = string
}
1
2
3
4
5
6
7
8
# main.tf, référençant la variable
resource "aws_s3_bucket" "reports" {
  bucket = var.bucket_name

  tags = {
    Environment = var.environment
  }
}
1
terraform apply -var="bucket_name=acme-prod-reports" -var="environment=production"

Ou placez les valeurs dans un fichier terraform.tfvars pour ne pas retaper les flags à chaque exécution. outputs.tf fait le chemin inverse. Il expose une valeur calculée par Terraform, comme l’IP publique d’une instance ou un ARN généré, pour qu’un autre outil ou une autre configuration Terraform puisse la consommer :

1
2
3
output "bucket_arn" {
  value = aws_s3_bucket.reports.arn
}

Quand un ensemble de ressources se répète d’un projet à l’autre (un VPC standard, une stack web-app standard), vous l’encapsulez dans un module : un répertoire de fichiers .tf référencé avec un argument source, qui prend des entrées et renvoie des sorties comme une fonction. La plupart des équipes finissent avec une poignée de modules internes et beaucoup de configurations légères qui les appellent avec des variables différentes.

Quand Terraform n’est pas le bon outil

Terraform provisionne des ressources ; il ne configure pas ce qui tourne dessus. Il créera une instance EC2, mais installer des paquets, gérer les utilisateurs et garder la configuration synchronisée sur cette instance est un travail différent — traditionnellement celui d’Ansible, d’un script cloud-init, ou d’une image de conteneur que l’instance télécharge et exécute. Faire passer la configuration applicative par Terraform est la mauvaise direction. Créer le VPC par Ansible est l’autre mauvaise direction. La plupart des setups réels utilisent les deux, et Terraform transmet l’IP de l’instance directement à l’inventory d’Ansible via un output.

Pour un bucket que vous supprimerez dans une heure, ou une VM de test jetable, le fichier de state et le cycle écrire-plan-apply coûtent plus qu’ils ne font gagner. La CLI AWS ou la console sont plus rapides pour quelque chose que vous ne comptez ni garder ni reproduire.

Terraform, OpenTofu et Pulumi

Terraform n’est plus le seul outil qui lit HCL. HashiCorp l’a fait passer de la licence open-source MPL à la Business Source License en 2023, et la Linux Foundation maintient désormais OpenTofu, un fork qui a conservé la licence MPL et est resté compatible avec la syntaxe HCL et le format de state de Terraform. Basculer plus tard est un changement de configuration, pas une réécriture.

Pulumi prend la direction opposée : au lieu de HCL, vous écrivez l’infrastructure en TypeScript, Python ou Go. Cela compte si votre équipe veut un vrai contrôle de flux et des tests unitaires sur la logique d’infrastructure plutôt que les expressions plus limitées de HCL.

Comment essayer Terraform sans compte cloud

Le filet de sécurité de Terraform, c’est plan, et vous pouvez tester tout le cycle gratuitement avant de le pointer vers quelque chose de facturable. Les providers random et local ne demandent aucun identifiant cloud.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
terraform {
  required_providers {
    random = {
      source  = "hashicorp/random"
      version = "~> 3.0"
    }
  }
}

resource "random_pet" "name" {
  length = 2
}

output "generated_name" {
  value = random_pet.name.id
}

Vous lancez terraform init et terraform apply dans un répertoire vide et obtenez un vrai fichier de state et une vraie ressource gérée, sans rien à payer. Faites-le une fois, lisez la sortie du plan ligne par ligne jusqu’à ce que les symboles +, ~ et - signifient quelque chose pour vous, puis pointez les mêmes quatre commandes vers un provider qui facture à l’heure.

Articles associés