Introduction à Terraform

Terraform est un outil d'Infrastructure as Code (IaC) développé par HashiCorp. Il permet de décrire, versionner et automatiser le déploiement de ton infrastructure cloud ou on-premise.

[Code Terraform] → terraform plan → [Plan] → terraform apply → [Infrastructure]
(Déclaratif, Idempotent, Versionnable)

Fonctionnement de Terraform

Terraform utilise un modèle déclaratif :

  1. Déclaration : Tu écris ce que tu veux dans des fichiers .tf (ex: "Je veux une instance EC2 avec 2 Go de RAM").
  2. Comparaison : Terraform compare ta déclaration avec le fichier d'état (tfstate), qui contient l'état actuel de ton infrastructure.
  3. Planification : Terraform génère un plan d'exécution pour atteindre l'état souhaité.
  4. Application : Tu valides le plan avec terraform apply, et Terraform applique les changements.
Attention : Terraform ne vérifie pas automatiquement si l'infrastructure réelle correspond au tfstate. C'est à toi de détecter les dérives (drift) avec terraform plan.

Concepts Clés

Concepts Fondamentaux

Provider : Un plugin qui permet à Terraform d'interagir avec une API (AWS, Azure, Google Cloud, etc.). Chaque provider a ses propres ressources.

Exemple :
provider "aws" {
  region = "eu-west-1"
}
Ressource : Un composant d'infrastructure (ex: une instance EC2, un bucket S3, un VPC). Chaque ressource est déclarée dans un bloc resource.

Exemple :
resource "aws_instance" "mon_serveur" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}
Module : Un groupe de ressources pré-configurées et réutilisables. Idéal pour standardiser des architectures (ex: un module "VPC" ou "Kubernetes").

Exemple :
module "vpc" {
  source = "terraform-aws-modules/vpc/aws"
  version = "3.0.0"
  # ... paramètres ...
}
State File (tfstate) : Fichier JSON qui enregistre l'état actuel de ton infrastructure. Ne jamais le modifier manuellement !

Backend : Où stocker le tfstate (local, S3, Terraform Cloud, etc.).
terraform {
  backend "s3" {
    bucket = "mon-bucket-tfstate"
    key    = "prod/terraform.tfstate"
    region = "eu-west-1"
  }
}

Modules Terraform

Pourquoi utiliser des modules ?

Les modules permettent de :

  • Réutiliser des configurations (ex: un VPC standardisé).
  • Simplifier la maintenance (modifier un module = mise à jour partout).
  • Partager des architectures entre équipes (via un registry public ou privé).
Registry Terraform : https://registry.terraform.io/
Tu y trouveras des modules prêts à l'emploi pour AWS, Azure, GCP, etc.

Commandes Utiles

Gestion de l'État (terraform state)

Ces commandes permettent de manipuler le fichier d'état (tfstate). À utiliser avec prudence !

# Supprime une ressource de l'état (sans la détruire dans le cloud)
terraform state rm aws_instance.mon_serveur

# Télécharge l'état distant (ex: depuis S3)
terraform state pull > terraform.tfstate

# Envoie un état local vers le backend distant
terraform state push terraform.tfstate

# Affiche les détails d'une ressource dans l'état
terraform state show aws_instance.mon_serveur

# Liste toutes les ressources dans l'état
terraform state list

# Déplace une ressource dans l'état (ex: renommage)
terraform state mv aws_instance.ancien_nom aws_instance.nouveau_nom

# Remplace un provider dans l'état (ex: changement de version)
terraform state replace-provider registry.terraform.io/hashicorp/aws registry.terraform.io/hashicorp/aws/4.0.0
Cas d'usage typique :
  • terraform state rm : Pour supprimer une ressource de l'état après l'avoir détruite manuellement dans le cloud.
  • terraform state mv : Pour renommer une ressource sans la recréer.
  • terraform state pull/push : Pour sauvegarder ou restaurer un état.

Sortie & Visualisation

Ces commandes aident à comprendre ce que Terraform va faire ou a fait.

# Génère un graphe de dépendances (au format DOT)
terraform graph | dot -Tsvg > graph.svg

# Affiche l'état actuel ou un plan sauvegardé
terraform show

# Affiche les outputs définis dans ton code
terraform output

# Affiche les changements prévus (sans appliquer)
terraform plan -out=tfplan
terraform show -json tfplan | jq
Astuce :
  • terraform graph : Utilise Graphviz pour visualiser les dépendances entre ressources.
  • terraform plan -out=... : Sauvegarde le plan pour une application ultérieure.
  • terraform show -json : Formate la sortie en JSON pour un traitement automatisé (ex: avec jq).

Importation / Remplacement

Pour intégrer des ressources existantes (créées manuellement) dans ton état Terraform.

# Importe une ressource existante dans l'état Terraform
# Syntax: terraform import ADRESSE_RESSOURCE ID_REEL
terraform import aws_instance.mon_serveur i-0123456789abcdef0

# Remplace une ressource sans la détruire (ex: changement de type)
terraform apply -replace="aws_instance.ancienne[0]"
Processus d'import :
  1. Crée un bloc resource vide dans ton code pour la ressource à importer.
  2. Exécute terraform import pour lier la ressource réelle à ce bloc.
  3. Complète le bloc resource avec les paramètres actuels de la ressource.
  4. Exécute terraform plan pour vérifier que tout est synchronisé.

Blocs de Configuration

Blocs Principaux

Bloc Description Exemple
provider Configure l'accès à un cloud provider (AWS, Azure, etc.).
provider "aws" {
  region = "eu-west-1"
  profile = "mon-profil"
}
resource Déclare une ressource à créer (instance, bucket, etc.).
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}
variable Déclare une variable pour paramétrer ton code.
variable "instance_type" {
  description = "Type d'instance EC2"
  type        = string
  default     = "t2.micro"
}
output Exporte une valeur après application (ex: IP publique).
output "instance_ip" {
  value = aws_instance.web.public_ip
}
locals Déclare des variables locales pour simplifier ton code.
locals {
  instance_name = "mon-serveur-${terraform.workspace}"
}

Meta-Arguments

Utilisation Avancée

Meta-Argument Description Exemple
count Crée plusieurs instances identiques d'une ressource.
resource "aws_instance" "web" {
  count         = 3
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  tags = {
    Name = "web-server-${count.index}"
  }
}
for_each Crée une ressource pour chaque élément d'un map ou set.
variable "users" {
  type = map(string)
  default = {
    alice = "admin"
    bob   = "user"
  }
}

resource "aws_iam_user" "example" {
  for_each = var.users
  name     = each.key
  tags = {
    Role = each.value
  }
}
depends_on Force une dépendance explicite entre ressources.
resource "aws_instance" "web" {
  depends_on = [aws_db_instance.mon_db]
  # ...
}
lifecycle Contrôle le cycle de vie d'une ressource (ex: ignorer les changements).
resource "aws_instance" "web" {
  lifecycle {
    ignore_changes = [tags]
    prevent_destroy = true
  }
}

CI/CD Pipelines

Intégration Continue

Pour automatiser le déploiement de ton infrastructure, tu peux intégrer Terraform dans un pipeline CI/CD (GitHub Actions, GitLab CI, etc.).

Outils recommandés :
  • TFLint : Vérifie la syntaxe et les bonnes pratiques dans tes fichiers Terraform.
  • Terraform Docs : Génère automatiquement de la documentation.
  • Terragrunt : Pour gérer des configurations Terraform complexes.
  • Infracost : Estime le coût de ton infrastructure avant déploiement.
# Exemple de pipeline GitHub Actions pour Terraform
name: Terraform CI

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2

      - name: Terraform Init
        run: terraform init

      - name: Terraform Validate
        run: terraform validate

      - name: Terraform Plan
        run: terraform plan -no-color

      - name: Terraform Apply (Auto-merge only)
        if: github.event_name == 'push'
        run: terraform apply -auto-approve

Bonnes Pratiques

Éviter les Dérives (Drift)

1. Toujours utiliser Terraform :

Évite de modifier manuellement les ressources dans la console AWS/Azure. Toute modification doit passer par Terraform.

2. Restreindre les accès manuels :

Utilise des politiques IAM pour empêcher les modifications manuelles sur les ressources gérées par Terraform.

# Exemple de politique IAM pour protéger une instance EC2
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": [
        "ec2:TerminateInstances",
        "ec2:StopInstances",
        "ec2:ModifyInstanceAttribute"
      ],
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringNotLike": {
          "aws:RequestTag/Terraform": "true"
        }
      }
    }
  ]
}
3. Détecter les dérives :

Exécute régulièrement terraform plan pour identifier les écarts entre ton code et l'infrastructure réelle.

Utilise des outils comme Terraform Cloud/Enterprise pour une détection automatique.

4. Versionner ton état :

Active le versioning sur ton backend (ex: S3) pour pouvoir restaurer un état précédent en cas d'erreur.

Cas Pratique : Gestion des Dérives

Scénario

Problème : Tu as créé une instance EC2 manuellement dans la console AWS, mais elle n'est pas déclarée dans ton code Terraform. terraform plan ne détecte pas cette ressource → dérive !

État actuel :
  • Code Terraform : 1 instance EC2 (aws_instance.web).
  • Console AWS : 2 instances EC2 (1 gérée par Terraform, 1 manuelle).
  • terraform plan : Ne voit que la première instance.
Solution :
  1. Ajoute un bloc resource vide pour la ressource manuelle dans ton code.
  2. Utilise terraform import pour lier la ressource réelle à ce bloc.
  3. Complète le bloc avec les paramètres actuels de la ressource.
  4. Exécute terraform plan pour vérifier la synchronisation.

Inspection et Modification de l'État

Outils Disponibles

Terraform fournit des commandes pour inspecter et modifier l'état (tfstate). À utiliser avec prudence !

# Affiche l'état actuel au format lisible
terraform show

# Liste toutes les ressources dans l'état
terraform state list

# Affiche les détails d'une ressource spécifique
terraform state show aws_instance.mon_serveur

# Supprime une ressource de l'état (sans la détruire dans le cloud)
terraform state rm aws_instance.mon_serveur

# Déplace une ressource dans l'état (ex: renommage)
terraform state mv aws_instance.ancien_nom aws_instance.nouveau_nom

# Remplace un provider dans l'état (ex: mise à jour de version)
terraform state replace-provider registry.terraform.io/hashicorp/aws registry.terraform.io/hashicorp/aws/4.0.0
Attention :
  • Les modifications de l'état ne modifient pas l'infrastructure réelle.
  • Une erreur dans l'état peut rendre ton infrastructure incohérente.
  • Toujours faire un terraform plan après une modification de l'état.

Attributs Calculés ou Read-Only

Documentation Officielle

Dans la documentation de chaque ressource (ex: aws_instance), les attributs sont classés en 3 catégories :

Type Description Exemple
Required Attributs obligatoires pour créer la ressource. ami, instance_type (pour aws_instance)
Optional Attributs optionnels que tu peux définir. tags, subnet_id
Computed Attributs calculés automatiquement par le provider. Read-only ! public_ip, arn, id
Exemple avec aws_instance :
resource "aws_instance" "web" {
  # Required
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  # Optional
  tags = {
    Name = "mon-serveur"
  }

  # Computed (read-only, définis automatiquement par AWS)
  # public_ip = "54.123.45.67"  # ❌ Ne pas définir manuellement !
}

# Pour utiliser un attribut computed :
output "instance_public_ip" {
  value = aws_instance.web.public_ip  # ✅ OK : lecture seule
}

Exercice : Module Terraform pour un Portfolio

Contexte

Tu veux déployer un site web statique (portfolio) avec :

  • Une image Docker basée sur Nginx (portfolio:1.3.0).
  • Une infrastructure légère et économique.
  • Un accès public via une IP ou un nom de domaine.

Besoin :

  • Un VPC avec un sous-réseau public.
  • Un IAM Role pour permettre à ECS d'accéder aux ressources AWS.
  • Un service ECS (ou une instance EC2) pour héberger ton container.
  • Un Load Balancer ou une IP publique pour exposer le site.

Problème : Changement d'IP Publique

Après un redémarrage de ton instance EC2 (sans Elastic IP ni Load Balancer), son IP publique change. Terraform détecte ce changement lors du prochain plan.

Solution :
  1. Si c'est juste un output (ex: public_ip) qui a changé, tu n'as rien à faire : Terraform mettra à jour l'état automatiquement au prochain apply.
  2. Si la ressource elle-même a été modifiée manuellement (ex: type d'instance), tu dois :
    1. Mettre à jour sa définition dans ton code .tf.
    2. Exécuter terraform apply pour synchroniser.
  3. Si la ressource a été recréée manuellement (ex: nouvelle instance avec un nouvel ID) :
    1. Supprime l'ancienne ressource de l'état : terraform state rm aws_instance.mon_ec2.
    2. Importe la nouvelle ressource : terraform import aws_instance.mon_ec2 i-nouvelid.
    3. Vérifie avec terraform plan.
# Exemple de correction pour une IP publique changée
# 1. Vérifie le changement
terraform plan

# 2. Applique les changements (Terraform mettra à jour l'état automatiquement)
terraform apply

# Si la ressource a été recréée manuellement :
# 1. Supprime l'ancienne référence dans l'état
terraform state rm aws_instance.mon_ec2

# 2. Importe la nouvelle ressource
terraform import aws_instance.mon_ec2 i-0435a04ca22d472ee

# 3. Vérifie que tout est synchronisé
terraform plan

Conclusion

Terraform est un outil puissant pour gérer ton infrastructure en tant que code. En suivant les bonnes pratiques et en maîtrisant les commandes présentées ici, tu peux :

  • Automatiser le déploiement de ton infrastructure.
  • Éviter les dérives et maintenir une infrastructure stable et reproductible.
  • Collaborer efficacement avec ton équipe grâce au versioning et aux modules.
  • Détecter et corriger rapidement les écarts entre ton code et l'infrastructure réelle.

Pour aller plus loin, explore la documentation officielle et les modules publics !