Retour au cours

infra / terraform

Resources et attributs

Leçon 31 exercice

Explication

Ce que vous allez apprendre

  • Lire et écrire la syntaxe générale d'un bloc resource
  • Référencer un attribut d'une ressource depuis une autre pour créer une dépendance
  • Distinguer les arguments fournis par toi des attributs calculés par le cloud
  • Créer plusieurs ressources similaires avec count et for_each
  • Protéger une ressource critique avec le bloc lifecycle

Dans quel contexte ?

Une équipe backend a déjà créé un bucket S3 simple (leçon précédente) ; elle doit maintenant déployer une vraie instance de serveur EC2, lui attacher une adresse IP publique fixe, puis dupliquer ce schéma pour trois serveurs de traitement identiques (worker[0], worker[1], worker[2]).

D'abord, il faut maîtriser la syntaxe de base

Un bloc resource suit toujours la même forme : resource "<type>" "<nom_local>" { ... }. Le type (aws_instance) indique QUEL objet créer, défini par le provider ; le nom local (web) n'existe que dans ta configuration Terraform, pour pouvoir référencer cette ressource ailleurs dans le code.

À l'intérieur des accolades, on distingue deux catégories de valeurs bien différentes : les arguments qu'on fournit nous-mêmes (ami, instance_type) et les attributs calculés par le cloud, connus seulement APRÈS la création (public_ip, id).

ÉlémentQui le fournitConnu quand
Argument (ami, instance_type)Toi, dans le codeAvant la création
Attribut calculé (id, public_ip)Le cloud, après créationAprès apply
Référence (aws_instance.web.id)Terraform, via le graphe de dépendancesRésolu automatiquement dans l'ordre

Prérequis

Il faut avoir déjà écrit et appliqué un premier fichier .tf avec un provider configuré (voir la leçon précédente) pour suivre les exemples de cette leçon.

Une fois une ressource créée, comment en référencer une autre ?

En écrivant aws_instance.web.id dans un autre bloc, Terraform comprend automatiquement qu'il doit créer web AVANT la ressource qui la référence. C'est ce qu'on appelle une dépendance implicite : aucune configuration supplémentaire n'est nécessaire, Terraform déduit l'ordre de création à partir des références présentes dans le code lui-même.

Il reste un problème quand il faut créer PLUSIEURS ressources similaires

Copier-coller trois fois le même bloc resource pour trois workers identiques fonctionne, mais devient vite ingérable dès qu'un paramètre commun doit changer. count résout ça en créant N instances numérotées (worker[0], worker[1], worker[2]), accessibles via count.index dans les attributs.

Piège fréquent

count numérote les ressources par position. Si tu supprimes l'élément du milieu d'une liste utilisée avec count, Terraform décale tous les index suivants et recrée inutilement des ressources qui n'avaient pourtant pas changé. for_each, indexé par CLÉ plutôt que par position, ne souffre pas de ce problème et doit être préféré dès que l'ordre des éléments peut varier.

Maintenant, une question de sécurité : comment protéger une ressource critique d'une suppression accidentelle ?

Le bloc lifecycle répond exactement à ce besoin, avec trois options principales : prevent_destroy bloque tout destroy sur cette ressource précise, create_before_destroy crée le remplaçant avant de supprimer l'ancien (zéro interruption de service), et ignore_changes ignore certains changements faits manuellement en dehors de Terraform.

Bonne pratique

Active systématiquement prevent_destroy = true sur toute ressource dont la perte serait catastrophique (base de données de production, bucket de state Terraform lui-même, vu dans une leçon ultérieure). Un simple terraform destroy lancé par erreur sur le mauvais dossier échoue alors proprement, plutôt que de tout supprimer.

Le piège à connaître avant de continuer

Écrire des valeurs en dur (comme l'ID d'AMI ami-0c55b159cbfafe1f0 utilisé dans cette leçon) fonctionne pour apprendre, mais devient vite fragile : un ID d'AMI change à chaque nouvelle version d'Ubuntu publiée. La prochaine leçon introduit les variables, la première étape pour rendre une configuration Terraform réellement réutilisable et paramétrable.

Commandes & code

Resources et attributs

hcl
# Syntaxe générale : resource "<type>" "<nom_local>" { ... }
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name        = "serveur-web"
    Environment = "production"
  }
}

# Référencer un attribut d'une autre ressource : <type>.<nom_local>.<attribut>
resource "aws_eip" "web_ip" {
  instance = aws_instance.web.id            # dépendance implicite : Terraform crée web AVANT web_ip
  domain   = "vpc"
}

# Attributs calculés (connus seulement APRÈS création) vs arguments (fournis par vous)
output "adresse_ip_publique" {
  value = aws_instance.web.public_ip         # attribut calculé par AWS, connu après apply
}

# meta-arguments disponibles sur TOUTE ressource
resource "aws_instance" "worker" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.small"

  count = 3                                  # crée 3 instances : worker[0], worker[1], worker[2]

  tags = {
    Name = "worker-${count.index}"           # index 0, 1, 2 dans le nom
  }
}

# for_each : comme count, mais indexé par clé (plus stable si l'ordre change)
resource "aws_instance" "service" {
  for_each      = toset(["api", "worker", "scheduler"])
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.small"

  tags = {
    Name = "service-${each.key}"             # each.key = "api", "worker" ou "scheduler"
  }
}

lifecycle_config_example = null  # (placeholder pédagogique, retiré ci-dessous)
hcl
# Le bloc "lifecycle" contrôle le comportement de création/destruction
resource "aws_instance" "critique" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.medium"

  lifecycle {
    prevent_destroy       = true             # bloque tout "terraform destroy" sur cette ressource
    create_before_destroy = true             # crée le remplaçant AVANT de détruire l'ancien (zéro downtime)
    ignore_changes         = [tags]           # ignore les changements manuels sur "tags" (faits hors Terraform)
  }
}

Résumé

  • count indexe les ressources par nombre entier (0, 1, 2...) ; for_each les indexe par clé stable, plus robuste aux réordonnancements.
  • Les dépendances implicites s'expriment en référençant type.nom.attribut d'une autre ressource.
  • lifecycle { prevent_destroy, create_before_destroy, ignore_changes } protège les ressources critiques.

Exercices pratiques

1 disponible
1

Mission : stabiliser un cluster de workers instable

Objectif : Diagnostiquer un recalcul en cascade causé par `count` et sécuriser une ressource critique contre la suppression accidentelle.

Contexte

Une liste de trois workers (aws_instance.worker avec count = 3) tourne en production. Un développeur supprime worker[1] au milieu de la liste source, puis relance terraform plan : celui-ci annonce que worker[1] doit être modifié ET que worker[2] doit être détruit puis recréé, alors qu'aucun des deux n'a réellement changé de configuration.

Résoudre l’exercice →