Aller au contenu
  1. Blog/

Terraform pour débutants : automatiser son infrastructure pas à pas

Sommaire

Vous gérez encore vos serveurs à la main, via une console web ou des scripts bricolés ? Il est temps de passer à l’étape suivante. Terraform est l’outil de référence pour décrire, provisionner et gérer votre infrastructure de manière déclarative, reproductible et versionnée.

Ce guide vous accompagne pas à pas, de l’installation jusqu’au déploiement concret d’un serveur chez Hetzner, en passant par la gestion du state et l’introduction aux modules. Aucun prérequis cloud exotique : on reste sur des providers européens accessibles.

Qu’est-ce que Terraform ?
#

Terraform est un outil open source créé par HashiCorp en 2014. Il permet de définir votre infrastructure sous forme de code (Infrastructure as Code, ou IaC) dans un langage déclaratif appelé HCL (HashiCorp Configuration Language).

Le principe est simple : vous décrivez l’état souhaité de votre infrastructure dans des fichiers .tf, et Terraform se charge de créer, modifier ou supprimer les ressources nécessaires pour atteindre cet état.

Pourquoi adopter Terraform ?
#

  • Reproductibilité — Le même code produit la même infrastructure, à chaque fois. Fini les « ça marchait sur mon setup ».
  • Versionnement — Votre infra vit dans Git, avec historique, reviews et rollback.
  • Multi-provider — Un seul outil pour Hetzner, Infomaniak, Cloudflare, et des centaines d’autres.
  • Planification — Terraform montre exactement ce qu’il va faire avant de le faire (plan).
  • Collaboration — Toute l’équipe travaille sur la même base de code, avec des conventions partagées.

Terraform vs Ansible, Pulumi et les autres
#

Terraform n’est pas le seul outil d’IaC, mais il occupe une place bien définie :

OutilApprocheForce principale
TerraformDéclaratif, provisionnementGestion du cycle de vie des ressources cloud
AnsibleImpératif/déclaratif, configurationConfiguration des serveurs une fois créés
PulumiImpératif (langages généraux)IaC en Python, Go, TypeScript

En pratique, Terraform et Ansible sont souvent complémentaires : Terraform crée les serveurs, Ansible les configure.

Installation de Terraform
#

Terraform est un binaire unique, sans dépendance. L’installation prend moins d’une minute.

Sur macOS (Homebrew)
#

1
2
3
# Installation via Homebrew — la méthode recommandée sur macOS
brew tap hashicorp/tap
brew install hashicorp/tap/terraform

Sur Linux (Debian/Ubuntu)
#

1
2
3
4
5
6
# Ajout du dépôt officiel HashiCorp
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring-hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list

sudo apt update && sudo apt install terraform

Vérification
#

1
2
3
# Vérifiez que Terraform est bien installé
terraform version
# Terraform v1.x.x

Astuce : Utilisez tfenv pour gérer plusieurs versions de Terraform en parallèle, surtout si vous travaillez sur plusieurs projets.

Les concepts fondamentaux
#

Avant d’écrire du code, quelques notions essentielles à comprendre.

Providers
#

Un provider est un plugin qui permet à Terraform de communiquer avec une API. Chaque service (Hetzner, Cloudflare, GitHub…) a son propre provider. C’est le pont entre votre code HCL et l’infrastructure réelle.

Resources
#

Une resource représente un élément d’infrastructure : un serveur, un réseau, un enregistrement DNS, un volume de stockage. Chaque resource a un type (défini par le provider) et des attributs configurables.

State (état)
#

Le state est un fichier JSON (terraform.tfstate) qui contient la cartographie entre vos resources déclarées et les ressources réelles qui existent chez le provider. C’est la mémoire de Terraform — on y reviendra en détail plus loin.

Le workflow Terraform
#

Le cycle de travail avec Terraform suit trois commandes clés :

1
2
3
4
5
6
7
8
# 1. Initialiser le projet (télécharge les providers)
terraform init

# 2. Prévisualiser les changements
terraform plan

# 3. Appliquer les changements
terraform apply

Ce workflow init → plan → apply deviendra votre routine quotidienne.

Premier projet : un serveur Hetzner
#

Passons à la pratique. Nous allons déployer un serveur cloud chez Hetzner, un hébergeur allemand réputé pour ses prix compétitifs et ses datacenters européens (Falkenstein, Nuremberg, Helsinki, Ashburn).

Prérequis
#

  1. Un compte Hetzner Cloud (inscription gratuite)
  2. Un API token généré dans la console Hetzner (Cloud Console → Projet → Security → API Tokens → Generate API Token avec permissions Read & Write)

Structure du projet
#

Créez un répertoire propre pour votre premier projet :

1
mkdir terraform-hetzner-demo && cd terraform-hetzner-demo

Nous allons créer trois fichiers — une convention classique en Terraform :

1
2
3
4
terraform-hetzner-demo/
├── main.tf          # Ressources principales
├── variables.tf     # Déclaration des variables
└── outputs.tf       # Valeurs de sortie

Configurer le provider Hetzner
#

Commençons par variables.tf pour déclarer nos variables :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# variables.tf — Déclaration des variables du projet

variable "hcloud_token" {
  description = "Token API Hetzner Cloud"
  type        = string
  sensitive   = true  # Masque la valeur dans les logs
}

variable "server_name" {
  description = "Nom du serveur"
  type        = string
  default     = "web-01"
}

variable "server_type" {
  description = "Type d'instance Hetzner (cpx11, cx22, cx32...)"
  type        = string
  default     = "cx22"  # 2 vCPU, 4 Go RAM — bon rapport qualité/prix
}

variable "location" {
  description = "Datacenter Hetzner (nbg1, fsn1, hel1)"
  type        = string
  default     = "nbg1"  # Nuremberg, Allemagne
}

Ensuite, main.tf avec le provider et notre première resource :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# main.tf — Configuration principale

# Déclaration du provider Hetzner et de sa version minimale
terraform {
  required_providers {
    hcloud = {
      source  = "hetznercloud/hcloud"
      version = "~> 1.45"  # Accepte les versions 1.45.x
    }
  }

  # Version minimale de Terraform requise
  required_version = ">= 1.6.0"
}

# Configuration du provider avec notre token API
provider "hcloud" {
  token = var.hcloud_token
}

# --- Clé SSH ---
# On importe notre clé publique pour se connecter au serveur
resource "hcloud_ssh_key" "default" {
  name       = "ma-cle-ssh"
  public_key = file("~/.ssh/id_ed25519.pub")  # Adaptez le chemin si nécessaire
}

# --- Serveur ---
# Notre premier serveur cloud
resource "hcloud_server" "web" {
  name        = var.server_name
  image       = "ubuntu-24.04"     # Image OS
  server_type = var.server_type     # Type d'instance
  location    = var.location        # Datacenter
  ssh_keys    = [hcloud_ssh_key.default.id]  # Référence à la clé SSH ci-dessus

  # Labels pour organiser vos ressources
  labels = {
    environment = "dev"
    managed_by  = "terraform"
  }
}

Enfin, outputs.tf pour récupérer les informations utiles après le déploiement :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# outputs.tf — Valeurs affichées après le déploiement

output "server_ip" {
  description = "Adresse IPv4 publique du serveur"
  value       = hcloud_server.web.ipv4_address
}

output "server_status" {
  description = "Statut du serveur"
  value       = hcloud_server.web.status
}

Premier déploiement
#

C’est le moment de vérité. Suivez ces étapes dans l’ordre :

1
2
3
4
5
6
7
8
# 1. Initialiser le projet — télécharge le provider Hetzner
terraform init

# 2. Définir le token (ne le mettez JAMAIS en dur dans le code)
export TF_VAR_hcloud_token="votre-token-api-ici"

# 3. Prévisualiser les changements
terraform plan

La commande plan affiche un résumé détaillé de ce que Terraform va créer :

1
Plan: 2 to add, 0 to change, 0 to destroy.

Tout est clair ? On applique :

1
2
# 4. Appliquer — Terraform demande confirmation avant d'agir
terraform apply

Tapez yes à la confirmation. En quelques secondes, votre serveur est provisionné. Terraform affiche les outputs :

1
2
3
4
5
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:
  server_ip     = "88.99.xxx.xxx"
  server_status = "running"

Vous pouvez maintenant vous connecter :

1
ssh root@$(terraform output -raw server_ip)

Comprendre le State
#

Le state est un concept central en Terraform, et souvent source de confusion pour les débutants. Prenons le temps de bien le comprendre.

À quoi sert le state ?
#

Après un terraform apply, Terraform crée un fichier terraform.tfstate dans votre répertoire. Ce fichier JSON contient la correspondance exacte entre vos ressources déclarées en HCL et les ressources réelles chez le provider.

Sans ce fichier, Terraform ne saurait pas :

  • Quelles ressources il a déjà créées
  • Quels attributs ont changé depuis le dernier apply
  • Quelles ressources supprimer si vous les retirez du code

Le state local vs distant
#

Par défaut, le state est stocké localement dans terraform.tfstate. Cela pose deux problèmes :

  1. Collaboration — Si deux personnes appliquent en même temps, le state est corrompu.
  2. Sécurité — Le state contient des données sensibles (IPs, identifiants…).

La solution : un backend distant. Voici un exemple avec un bucket S3-compatible (disponible chez beaucoup de providers, dont Infomaniak) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Backend distant — stocke le state dans un bucket S3-compatible
terraform {
  backend "s3" {
    bucket   = "mon-terraform-state"
    key      = "hetzner-demo/terraform.tfstate"
    region   = "eu-west-1"
    endpoint = "https://s3.pub1.infomaniak.cloud"  # Exemple Infomaniak

    # Désactiver les validations spécifiques AWS
    skip_credentials_validation = true
    skip_region_validation      = true
    skip_metadata_api_check     = true
  }
}

Règles d’or pour le state
#

  • Ne jamais commit le fichier terraform.tfstate dans Git. Ajoutez-le à votre .gitignore.
  • Toujours utiliser un backend distant dès que vous travaillez en équipe.
  • Activer le locking pour empêcher les modifications concurrentes.
  • Ne jamais éditer le state manuellement, sauf avec terraform state (commandes dédiées).
1
2
3
4
5
# .gitignore recommandé pour tout projet Terraform
*.tfstate
*.tfstate.*
.terraform/
*.tfvars       # Contient potentiellement des secrets

Introduction aux modules
#

Une fois votre premier projet fonctionnel, vous allez vite vouloir réutiliser du code. C’est exactement le rôle des modules.

Un module Terraform est simplement un répertoire contenant des fichiers .tf. Votre projet actuel est déjà un module — le module racine (root module).

Créer un module réutilisable
#

Imaginons un module pour créer un serveur Hetzner avec des paramètres standardisés :

1
2
3
4
5
modules/
└── hetzner-server/
    ├── main.tf
    ├── variables.tf
    └── outputs.tf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# modules/hetzner-server/variables.tf

variable "name" {
  description = "Nom du serveur"
  type        = string
}

variable "server_type" {
  description = "Type d'instance"
  type        = string
  default     = "cx22"
}

variable "image" {
  description = "Image OS"
  type        = string
  default     = "ubuntu-24.04"
}

variable "location" {
  description = "Datacenter"
  type        = string
  default     = "nbg1"
}

variable "ssh_key_ids" {
  description = "Liste des IDs de clés SSH"
  type        = list(string)
}

variable "labels" {
  description = "Labels à appliquer au serveur"
  type        = map(string)
  default     = {}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# modules/hetzner-server/main.tf

resource "hcloud_server" "this" {
  name        = var.name
  image       = var.image
  server_type = var.server_type
  location    = var.location
  ssh_keys    = var.ssh_key_ids

  labels = merge(
    { managed_by = "terraform" },  # Label toujours présent
    var.labels                       # Labels personnalisés
  )
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# modules/hetzner-server/outputs.tf

output "id" {
  value = hcloud_server.this.id
}

output "ipv4_address" {
  value = hcloud_server.this.ipv4_address
}

output "status" {
  value = hcloud_server.this.status
}

Utiliser le module
#

De retour dans votre module racine, appelez le module pour créer plusieurs serveurs :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# main.tf — Utilisation du module pour créer deux serveurs

module "web_server" {
  source      = "./modules/hetzner-server"
  name        = "web-prod-01"
  server_type = "cx22"
  location    = "fsn1"            # Falkenstein
  ssh_key_ids = [hcloud_ssh_key.default.id]

  labels = {
    role        = "web"
    environment = "production"
  }
}

module "db_server" {
  source      = "./modules/hetzner-server"
  name        = "db-prod-01"
  server_type = "cx32"            # Plus puissant pour la BDD
  location    = "fsn1"
  ssh_key_ids = [hcloud_ssh_key.default.id]

  labels = {
    role        = "database"
    environment = "production"
  }
}

Après un terraform init (nécessaire quand on ajoute un module), le plan montrera la création des deux serveurs avec leurs configurations respectives.

Bonnes pratiques pour bien démarrer
#

Voici les conventions qui vous épargneront des heures de débogage et rendront vos projets maintenables sur le long terme.

1. Structurez vos fichiers
#

Même pour un petit projet, séparez vos fichiers par responsabilité :

1
2
3
4
5
6
7
projet/
├── main.tf          # Ressources principales
├── variables.tf     # Toutes les variables
├── outputs.tf       # Toutes les sorties
├── versions.tf      # Contraintes de versions (providers, Terraform)
├── terraform.tfvars # Valeurs des variables (ne pas commit si secrets)
└── .gitignore

2. Épinglez vos versions
#

Ne laissez jamais Terraform choisir la version du provider :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# versions.tf — Verrouillez les versions pour la reproductibilité
terraform {
  required_version = ">= 1.6.0, < 2.0.0"

  required_providers {
    hcloud = {
      source  = "hetznercloud/hcloud"
      version = "~> 1.45"   # Accepte 1.45.x, pas 1.46.0
    }
  }
}

Commitez aussi le fichier .terraform.lock.hcl — c’est le lockfile des providers, comme un package-lock.json.

3. Utilisez des variables, jamais de valeurs en dur
#

Les tokens, noms, tailles d’instance : tout passe par des variables. Les secrets passent par des variables d’environnement préfixées TF_VAR_ :

1
2
# Méthode sécurisée pour passer un secret
export TF_VAR_hcloud_token="hc_xxxxxxxxxxxxx"

4. Nommez avec intention
#

Adoptez une convention de nommage cohérente dès le début :

1
2
3
4
5
6
7
# ✅ Bon — descriptif et cohérent
resource "hcloud_server" "web_production" { ... }
resource "hcloud_server" "db_production"  { ... }

# ❌ Mauvais — générique et ambigu
resource "hcloud_server" "server1" { ... }
resource "hcloud_server" "main"    { ... }

5. Validez avant de commit
#

Intégrez ces commandes dans votre workflow (ou votre CI) :

1
2
3
4
5
6
7
8
# Vérifier la syntaxe HCL
terraform fmt -check

# Valider la configuration
terraform validate

# Formater automatiquement le code
terraform fmt -recursive

6. Utilisez terraform plan religieusement
#

Ne faites jamais un apply sans avoir lu le plan. C’est votre filet de sécurité. En CI/CD, sauvegardez le plan pour l’appliquer de manière identique :

1
2
3
4
5
# Sauvegarder le plan dans un fichier binaire
terraform plan -out=tfplan

# Appliquer exactement ce plan (sans reconfirmation)
terraform apply tfplan

7. Nettoyez après vous
#

Pour détruire proprement toute l’infrastructure d’un projet de test :

1
2
3
4
5
# Prévisualiser la destruction
terraform plan -destroy

# Détruire les ressources (avec confirmation)
terraform destroy

Aller plus loin
#

Ce guide couvre les fondations. Voici les prochaines étapes pour progresser :

  • Workspaces — Gérer plusieurs environnements (dev, staging, prod) avec le même code.
  • Data sources — Interroger des ressources existantes sans les gérer.
  • Provisioners — Exécuter des scripts sur les serveurs après création (préférez Ansible pour ça).
  • Terraform Cloud / Spacelift — Automatiser les apply dans un pipeline CI/CD.
  • Terragrunt — Orchestrer des projets Terraform complexes avec moins de répétition.
  • Import — Importer des ressources existantes dans le state avec terraform import ou le bloc import (Terraform 1.5+).

Conclusion
#

Terraform transforme la gestion d’infrastructure en un processus prévisible, versionné et collaboratif. Avec les bases couvertes dans ce guide — providers, resources, state, modules et bonnes pratiques — vous avez tout ce qu’il faut pour démarrer concrètement.

Le meilleur conseil : commencez petit. Un serveur, un réseau, un DNS. Itérez. Chaque terraform apply réussi renforce votre compréhension. Et surtout, commitez votre code — votre infrastructure mérite le même soin que votre application.

La courbe d’apprentissage est réelle, mais le retour sur investissement est massif. Une fois que vous aurez goûté au terraform plan avant chaque changement d’infra, vous ne reviendrez plus en arrière.


Cet article fait partie de la série Infrastructure as Code sur devopslab.ch. Retrouvez nos autres guides pour approfondir Terraform, Ansible et les pratiques DevOps modernes.

Articles connexes