MET : Terraform, infrastructure as code (2/6)

| Digital

Créer des serveurs manuellement appartient au passé. Avec Terraform, votre infrastructure devient du code : versionnée, reproductible et déployable en quelques secondes.

Dans le premier article de cette série, nous avons présenté notre vision d’une infrastructure cloud moderne. Aujourd’hui, nous nous penchons sur le premier outil de notre stack : Terraform.

Le problème : une infrastructure artisanale

Imaginez le scénario classique :

  1. Vous vous connectez à la console de votre hébergeur
  2. Vous cliquez sur « Créer un serveur »
  3. Vous choisissez des options (RAM, CPU, OS…)
  4. Vous notez l’IP quelque part (ou pas)
  5. Vous répétez pour chaque serveur

Six mois plus tard :

  • « C’était quoi déjà la config de ce serveur ? »
  • « Qui a créé ce réseau privé ? »
  • « Peut-on recréer la même infra pour le client X ? »

La réponse est souvent : « Euh… il va falloir fouiller ».

La solution : l’Infrastructure as Code

Terraform est un outil open source créé par HashiCorp qui permet de définir votre infrastructure dans des fichiers de configuration. Au lieu de cliquer dans une interface, vous décrivez ce que vous voulez, et Terraform se charge de le créer.

« Décrivez l’état souhaité, Terraform se charge d’y parvenir. »

Les avantages sont nombreux :

  • Versioning : votre infrastructure est dans Git, avec un historique complet
  • Reproductibilité : recréez la même infra avec une seule commande
  • Documentation vivante : le code EST la documentation
  • Revue de code : les changements d’infrastructure passent par des merge requests
  • Rollback : revenez à une version précédente si nécessaire

Notre infrastructure dans Terraform

Voici comment nous définissons nos serveurs. Tout commence par la configuration du provider (l’hébergeur) :

# Configuration Terraform
terraform {
 required_version = ">= 1.6"

 required_providers {
 hcloud = {
 source = "hetznercloud/hcloud"
 version = "~> 1.45"
 }
 }
}

# Connexion à l’API de l’hébergeur
provider "hcloud" {
 token = var.hcloud_token # Token stocké dans une variable d’environnement
}

Ce bloc indique à Terraform :

  • Quelle version de Terraform utiliser
  • Quel provider (ici Hetzner Cloud) et avec quelle version
  • Comment s’authentifier (via un token API sécurisé)

Définition du serveur

Ensuite, nous déclarons nos serveurs. Voici la définition de notre serveur principal :

# Serveur principal - Kubernetes Control Plane
resource "hcloud_server" "control_plane" {
 name = "k8s-master"
 server_type = "cpxxx" # Notre modèle
 image = "ubuntu-22.04"
 location = "nbg1" # Datacenter de Nuremberg (Allemagne)
 ssh_keys = data.hcloud_ssh_keys.all.ssh_keys[*].id

  labels = {
 role = "k8s-control-plane"
 environment = "production"
 managed_by = "terraform"
 }

  lifecycle {
 prevent_destroy = true # Protection contre la suppression accidentelle
 }
}

Décomposons ce code :

  • resource : déclare une ressource à créer
  • hcloud_server : type de ressource (serveur Hetzner)
  • server_type : taille du serveur
  • location : le datacenter (Nuremberg, Allemagne = RGPD ✓)
  • labels : métadonnées pour l’organisation et le filtrage
  • prevent_destroy : empêche la suppression accidentelle

Le réseau privé

Pour que nos serveurs communiquent de manière sécurisée, nous créons un réseau privé :

# Réseau privé pour la communication inter-nœuds
resource "hcloud_network" "k8s_network" {
 name = "k8s-private-network"
 ip_range = "10.0.0.0/16"

  labels = {
 environment = "production"
 }
}

# Sous-réseau pour les serveurs
resource "hcloud_network_subnet" "k8s_subnet" {
 network_id = hcloud_network.k8s_network.id
 type = "cloud"
 network_zone = "eu-central"
 ip_range = "10.0.1.0/24"
}

# Rattacher le serveur au réseau privé
resource "hcloud_server_network" "control_plane_network" {
 server_id = hcloud_server.control_plane.id
 network_id = hcloud_network.k8s_network.id
 ip = "10.0.1.10"
}

Ce réseau privé permet :

  • Une communication sécurisée entre les nœuds Kubernetes
  • L’isolation du trafic interne (non exposé à Internet)
  • Des performances optimales (latence minimale)

Variables : flexibilité et sécurité

Les informations sensibles (tokens API, mots de passe) ne sont jamais codées en dur. Nous utilisons des variables :

# variables.tf
variable "hcloud_token" {
 description = "Token API de l’hébergeur"
 type = string
 sensitive = true # Masqué dans les logs
}

variable "ssh_keys" {
 description = "Clés SSH autorisées"
 type = list(string)
 default = []
}

Ces variables sont ensuite définies via :

  • Des variables d’environnement (TF_VAR_hcloud_token)
  • .tfvars Un fichier (non commité dans Git)
  • Un gestionnaire de secrets (Vault, etc.)

Le workflow Terraform en pratique

Utiliser Terraform au quotidien se résume à trois commandes :

1. Initialisation

# Télécharger les providers et initialiser le projet
terraform init

2. Planification

# Afficher ce qui sera créé/modifié/supprimé
terraform plan

Cette commande est cruciale : elle montre exactement ce que Terraform va faire, sans rien exécuter. Exemple de sortie :

Terraform effectuera les actions suivantes :

 # hcloud_server.control_plane sera créé
 + resource "hcloud_server" "control_plane" {
 + name = "k8s-master"
 + server_type = "cpx52"
 + location = "nbg1"
 ...
 }

Plan : 1 à ajouter, 0 à modifier, 0 à détruire.

3. Application

# Appliquer les changements (avec confirmation)
terraform apply

Terraform crée ensuite les ressources et stocke leur état dans un fichier terraform.tfstate.

Nos bonnes pratiques

🔐 Sécurité

  • Jamais de secrets dans le code : utilisez des variables et des gestionnaires de secrets
  • État distant : stockez le tfstate dans un bucket S3 chiffré, pas en local
  • Revue de code : tous les changements d’infrastructure passent par une merge request

🛡️ Protection

  • prevent_destroy : sur les ressources critiques (serveurs, bases de données)
  • ignore_changes : pour les attributs gérés manuellement

📁 Organisation

  • main.tf : ressources principales
  • variables.tf : déclarations de variables
  • outputs.tf : valeurs exportées (IP, ID…)
  • terraform.tfvars : valeurs des variables (non commitées)

Intégration à notre workflow

Pour simplifier l’usage, nous avons créé des commandes Make :

# Planifier les changements
make terraform-plan

# Appliquer les changements
make terraform-apply

# Importer des serveurs existants
make terraform-import-servers ODIN_ID=12345 ATHENA_ID=67890

Cela permet à toute l’équipe d’utiliser Terraform sans connaître les commandes exactes.

Le résultat

Avec Terraform, notre infrastructure est désormais :

📝DocumentéeLe code décrit exactement ce qui existe
🔄ReproductibleRecréation complète en 10 minutes
📊VersionnéeHistorique complet des changements
👥CollaborativeRevue de code avant chaque modification
🛡️SécuriséeSecrets externalisés, protection anti-suppression

Et ensuite ?

Terraform crée les serveurs, mais ils sont « vides ». Dans le prochain article, nous verrons comment Ansible prend le relais pour :

  • Configurer le système d’exploitation
  • Installer Kubernetes (K3s)
  • Déployer les services de base

Le tout de manière automatisée et idempotente.

🚀 Besoin d’aide pour adopter l’Infrastructure as Code ?

Nous accompagnons les entreprises dans la mise en place de Terraform, qu’il s’agisse de migrer une infrastructure existante ou de partir de zéro.

Parlons de votre projet →