
Une EC2 seule reste un bon exercice, mais ce n'est pas encore un service résilient. Dès que vous devez conserver plusieurs instances disponibles, absorber une variation de charge ou remplacer progressivement une version applicative, vous avez besoin d'un niveau d'abstraction supplémentaire. Dans AWS, ce duo s'appelle launch template plus autoscaling group.
L'idée à retenir avant même d'écrire le code est simple : le launch template décrit à quoi doit ressembler une instance, tandis que l'ASG décide combien d'instances doivent exister en permanence. Ce guide relie explicitement ces deux rôles pour éviter l'impression de manipuler deux objets AWS sans rapport.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Créer un launch template avec AMI, type d'instance et tags
- Créer un ASG avec min/max/desired capacity
- Utiliser
instance_refreshpour mettre à jour les instances sans interruption - Utiliser
random_integerpour donner un nom unique à l'ASG - Vérifier les instances créées dans la console AWS
Pourquoi ces deux ressources vont ensemble
Section intitulée « Pourquoi ces deux ressources vont ensemble »Beaucoup de débutants lisent aws_launch_template puis aws_autoscaling_group comme deux chapitres séparés. En réalité, l'un fournit le modèle, l'autre orchestre les copies de ce modèle. Sans template, l'ASG ne sait pas quoi lancer. Sans ASG, le template ne crée rien à lui seul.
Ce guide vous montre donc le couple complet, avec une idée pratique en tête : garder une capacité minimale et mettre à jour progressivement les instances lorsque le modèle change.
Prérequis
Section intitulée « Prérequis »- Terraform ≥ 1.11
- AWS CLI configuré
- Compréhension de variables, resources et outputs
Objectif
Section intitulée « Objectif »Le résultat visible à la fin est court à décrire : deux instances EC2 tournent
en permanence, vous n'en avez déclaré aucune individuellement, et si vous en
supprimez une à la main dans la console, AWS en relance une sans intervention
de Terraform. C'est cette boucle de réconciliation côté AWS qui distingue
un ASG d'un simple aws_instance répété avec count.
Construire une infrastructure auto-scalable :
- Un launch template = modèle pour les instances
- Un ASG qui maintient 2 instances (min=2, max=4)
- Observation que l'ASG crée automatiquement les instances
Durée estimée : 15-20 minutes (création d'instances) Coût : Gratuit/quasi-gratuit (t2.micro dans tier gratuit)
Préparation
Section intitulée « Préparation »Cet exercice lance de vraies instances EC2 sur votre compte AWS. Travaillez
dans un répertoire dédié : le terraform destroy de la fin s'appuie sur le
state local qui y sera créé, et le mélanger avec un autre projet vous
exposerait à détruire les mauvaises ressources.
Créez le répertoire :
mkdir -p ~/terraform-aws-launch-template-asgcd ~/terraform-aws-launch-template-asgÉtape 1, Déclarer le provider
Section intitulée « Étape 1, Déclarer le provider »Créez versions.tf :
terraform { required_version = ">= 1.11.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } random = { source = "hashicorp/random" version = "~> 3.0" } }}
provider "aws" { region = var.aws_region}Notez le second provider déclaré, random. Il fournit random_integer, qui
servira à suffixer le nom de l'ASG pour éviter tout conflit de nommage dans la
région. Deux providers dans un même projet est la situation normale dès qu'on
sort de l'exemple minimal : terraform init téléchargera les deux.
Étape 2, Déclarer les variables
Section intitulée « Étape 2, Déclarer les variables »Les trois variables de dimensionnement portent des noms imposés par AWS et
qu'il vaut mieux ne pas confondre. min_size et max_size sont des bornes
que l'ASG ne franchira jamais. desired_capacity est la cible courante,
celle qui pilote réellement le nombre d'instances lancées ; c'est aussi la
seule des trois qu'une politique de scaling modifiera d'elle-même par la
suite.
Créez variables.tf :
variable "aws_region" { type = string default = "us-east-1"}
variable "instance_type" { type = string default = "t2.micro"}
variable "min_size" { type = number default = 2}
variable "max_size" { type = number default = 4}
variable "desired_capacity" { type = number default = 2}Étape 3, Créer le launch template
Section intitulée « Étape 3, Créer le launch template »Ce fichier contient trois blocs de nature différente. Le bloc data interroge
AWS pour récupérer l'identifiant d'AMI Ubuntu le plus récent, ce qui évite
d'écrire en dur un ami-… qui deviendra faux dans quelques semaines et qui
change d'une région à l'autre. Le numéro 099720109477 est le compte
propriétaire officiel des images Ubuntu chez Canonical : le filtrer est ce qui
vous protège d'une AMI homonyme publiée par un tiers. Le bloc random_integer
sert uniquement à fabriquer un suffixe de nom, son rôle est expliqué plus bas.
Le bloc aws_launch_template est le modèle proprement dit.
Créez main.tf :
# Lire l'AMI Ubuntu la plus récentedata "aws_ami" "ubuntu" { most_recent = true owners = ["099720109477"] filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] }}
# Suffixe aléatoire pour garantir un nom d'ASG unique dans la régionresource "random_integer" "launch_template_version" { min = 1 max = 1000}
# Créer le launch templateresource "aws_launch_template" "lab05_template" { name_prefix = "lab05-template-" image_id = data.aws_ami.ubuntu.id instance_type = var.instance_type
tag_specifications { resource_type = "instance" tags = { Name = "lab05-asg-instance" } }
lifecycle { create_before_destroy = true }}Points clés :
- name_prefix : génère un nom unique (ex :
lab05-template-abc123) - tag_specifications : les instances héritent des tags
- create_before_destroy : crée le nouveau template avant de supprimer l'ancien
Étape 4, Créer l'autoscaling group
Section intitulée « Étape 4, Créer l'autoscaling group »L'ASG a besoin de savoir où lancer les instances : c'est le rôle du bloc
data "aws_subnets", qui récupère les sous-réseaux par défaut de la région et
les passe à vpc_zone_identifier. Sur un compte AWS dont le VPC par défaut
a été supprimé, ce data source renvoie une liste vide et l'ASG sera créé sans
jamais démarrer d'instance.
Un point mérite votre attention dans le bloc launch_template ci-dessous.
L'alias "$Latest" est souvent recommandé, mais le provider AWS documente
explicitement qu'un instance refresh ne démarre pas quand
version = "$Latest" est configuré : l'attribut ne change pas aux yeux de
Terraform, donc aucune mise à jour n'est déclenchée. Référencer
latest_version du template produit au contraire un numéro qui change à chaque
nouvelle version, ce qui déclenche bien le remplacement progressif.
Toujours dans main.tf, ajoutez :
# Lire les subnets par défautdata "aws_subnets" "default" { filter { name = "default-for-az" values = ["true"] }}
# Créer l'ASGresource "aws_autoscaling_group" "lab05_asg" { name = "lab05-asg-${random_integer.launch_template_version.result}" min_size = var.min_size max_size = var.max_size desired_capacity = var.desired_capacity vpc_zone_identifier = data.aws_subnets.default.ids
launch_template { id = aws_launch_template.lab05_template.id # Numéro de version résolu par Terraform, et non l'alias "$Latest" : # sans cela, l'instance refresh ne se déclencherait jamais. version = aws_launch_template.lab05_template.latest_version }
instance_refresh { strategy = "Rolling" preferences { min_healthy_percentage = 50 } }
lifecycle { create_before_destroy = true }
tag { key = "Name" value = "lab05-asg-instance" propagate_at_launch = true }}Explications :
| Paramètre | Rôle |
|---|---|
min_size | Minimum d'instances (2) |
max_size | Maximum d'instances (4) |
desired_capacity | Cible actuelle (2) = créer 2 instances |
vpc_zone_identifier | AZs où lancer les instances |
launch_template { version = ...latest_version } | Numéro de la dernière version du template, résolu par Terraform |
instance_refresh { strategy = "Rolling" } | Remplacer les instances progressivement |
min_healthy_percentage = 50 | Garder au moins 50% des instances saines pendant l'update |
Étape 5, Définir les outputs
Section intitulée « Étape 5, Définir les outputs »Ces sorties ne sont pas décoratives : elles vous donnent les valeurs à réutiliser
dans les commandes de vérification. asg_name contient le nom généré, que vous
ne pouvez pas deviner à l'avance puisqu'il embarque un tirage aléatoire.
asg_availability_zones est calculé par AWS et non par vous : il révèle dans
quelles zones de disponibilité l'ASG répartit réellement ses instances, ce
qui permet de contrôler que vpc_zone_identifier a bien récupéré plusieurs
sous-réseaux.
Créez outputs.tf :
output "launch_template_id" { value = aws_launch_template.lab05_template.id}
output "launch_template_name_prefix" { value = aws_launch_template.lab05_template.name_prefix}
output "asg_name" { value = aws_autoscaling_group.lab05_asg.name}
output "asg_min_size" { value = aws_autoscaling_group.lab05_asg.min_size}
output "asg_max_size" { value = aws_autoscaling_group.lab05_asg.max_size}
output "asg_desired_capacity" { value = aws_autoscaling_group.lab05_asg.desired_capacity}
output "asg_availability_zones" { value = aws_autoscaling_group.lab05_asg.availability_zones}Étape 6, Renseigner les valeurs
Section intitulée « Étape 6, Renseigner les valeurs »Le fichier variables.tf déclare les variables et leurs valeurs par défaut ;
terraform.tfvars fournit les valeurs effectives de ce déploiement. Ici les
deux coïncident, ce qui est volontaire : vous obtenez un fichier unique à
éditer pour changer la taille du groupe, sans toucher au code. Ce fichier
contient souvent des valeurs propres à un compte ou à un environnement,
ajoutez-le à votre .gitignore avant le premier commit.
Créez terraform.tfvars :
aws_region = "us-east-1"instance_type = "t2.micro"min_size = 2max_size = 4desired_capacity = 2Vérification
Section intitulée « Vérification »Quatre commandes, dont deux pour confirmer que le résultat existe côté AWS
et pas seulement dans le state Terraform. Le point de contrôle le plus parlant
est la troisième : terraform apply annonce 3 ressources créées, alors que
describe-instances en retourne deux. Rien d'anormal, les instances ne sont
pas des ressources Terraform, elles sont lancées par l'ASG. Remplacez
lab05-asg-121 par la valeur réelle de la sortie asg_name avant d'exécuter
les deux dernières commandes.
-
Initialiser :
Fenêtre de terminal terraform init -
Appliquer (⚠️ cela prend 3-4 minutes, création de 2 instances) :
Fenêtre de terminal terraform apply -auto-approveSortie attendue (fin) :
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.Outputs:asg_availability_zones = toset(["us-east-1a","us-east-1b","us-east-1c",...])asg_desired_capacity = 2asg_max_size = 4asg_min_size = 2asg_name = "lab05-asg-121"launch_template_id = "lt-06738d00c2e67dafb"launch_template_name_prefix = "lab05-template-" -
Vérifier les instances dans la console AWS :
Fenêtre de terminal aws ec2 describe-instances \--filters "Name=tag:Name,Values=lab05-asg-instance" \--query 'Reservations[*].Instances[*].[InstanceId,State.Name,InstanceType,SubnetId]' \--output tableSortie attendue :
| i-0abc123... | running | t2.micro | subnet-xx... || i-0def456... | running | t2.micro | subnet-yy... | -
Voir l'ASG :
Fenêtre de terminal aws autoscaling describe-auto-scaling-groups \--auto-scaling-group-names lab05-asg-121 \--query 'AutoScalingGroups[0].[MinSize,MaxSize,DesiredCapacity,Instances]' \--output table
Anatomie
Section intitulée « Anatomie »Launch Template vs Auto Scaling Group
Section intitulée « Launch Template vs Auto Scaling Group »La ligne à retenir est la deuxième. Un launch template appliqué seul ne
produit rien : c'est une définition stockée chez AWS, versionnée, que plusieurs
ASG peuvent référencer en même temps. Cette séparation explique le
comportement observé pendant la mise à jour : modifier le template crée une
nouvelle version sans perturber les instances existantes, et c'est l'ASG,
via son instance_refresh, qui décide du moment où les instances seront
remplacées.
| Aspect | Launch Template | ASG |
|---|---|---|
| Quoi | Modèle pour les instances | Gestionnaire d'instances |
| Crée des instances ? | ❌ Non | ✅ Oui |
| Instances réelles | ❌ Non | ✅ Oui (plusieurs) |
| Mise à jour | Nouvelle version | Utilise la nouvelle version |
Instance Refresh
Section intitulée « Instance Refresh »Quand le template change, l'ASG remplace les instances progressivement. Le
provider AWS n'accepte qu'une seule valeur pour strategy : Rolling. Toute
autre valeur, y compris BlueGreen, est rejetée à la validation. Le réglage se
fait donc entièrement dans preferences, et le curseur principal est
min_healthy_percentage :
instance_refresh { strategy = "Rolling" # Seule valeur acceptée par le provider preferences { min_healthy_percentage = 50 # Par défaut : 90 instance_warmup = 300 # Secondes avant de compter l'instance saine }}Avec min_healthy_percentage = 50 et une capacité désirée de 2, AWS s'autorise
à retirer une instance à la fois. Monté à 90, la valeur par défaut, le
remplacement exige d'abord de lancer la nouvelle instance : plus prudent, mais
la capacité dépasse temporairement la cible et il faut que max_size le
permette. instance_warmup retarde le moment où une instance fraîchement
lancée est comptée comme saine, le temps que l'application démarre réellement.
Bonnes pratiques
Section intitulée « Bonnes pratiques »1. Toujours utiliser des templates
Section intitulée « 1. Toujours utiliser des templates »aws_launch_configuration est l'ancêtre du launch template, et la voie est
fermée : AWS ne prend plus en charge les nouveaux types d'instances dans les
launch configurations depuis janvier 2023, et les comptes créés à partir du
1er octobre 2024 ne peuvent plus en créer, quelle que soit la méthode. Un
launch configuration est de surcroît immuable : pour le changer, il faut en
créer un autre puis repointer l'ASG. Le launch template apporte le
versionnement, indispensable au mécanisme d'instance refresh vu plus haut.
# ❌ Ancien style (déprécié)resource "aws_autoscaling_group" "bad" { launch_configuration = aws_launch_configuration.example.id}
# ✅ Nouveau styleresource "aws_autoscaling_group" "good" { launch_template { id = aws_launch_template.example.id version = aws_launch_template.example.latest_version }}2. Utiliser create_before_destroy
Section intitulée « 2. Utiliser create_before_destroy »Sans ce bloc, Terraform détruit la ressource avant de recréer la nouvelle, et
le service passe par une fenêtre à zéro instance. Sur un ASG dont le name
change, la différence se compte en minutes d'indisponibilité. Le prix à payer
est un pic de capacité transitoire, puisque les deux générations coexistent
le temps de la bascule.
lifecycle { create_before_destroy = true}Cela évite les gaps de 0 instances pendant les updates.
3. Configurer les instance refresh preferences
Section intitulée « 3. Configurer les instance refresh preferences »L'argument s'appelle instance_warmup, sans suffixe _seconds : c'est une
erreur de frappe classique, et le provider la rejette à terraform validate.
Sans cette valeur, AWS reprend le health check grace period de l'ASG, qui
est souvent trop court pour une application qui met une minute à démarrer.
instance_refresh { strategy = "Rolling" preferences { min_healthy_percentage = 90 instance_warmup = 300 }}Cela maintient le service disponible pendant les mises à jour.
4. Tracer les changements de capacity
Section intitulée « 4. Tracer les changements de capacity »Passer desired_capacity par une variable documentée plutôt que par une
valeur écrite en dur laisse une trace du dimensionnement dans le dépôt Git.
Attention toutefois : si vous ajoutez plus tard une politique de scaling
automatique, c'est AWS qui pilotera desired_capacity et Terraform verra une
dérive à chaque plan. Dans ce cas, retirez l'argument de votre
configuration ou ajoutez lifecycle { ignore_changes = [desired_capacity] }.
variable "desired_capacity" { description = "Desired number of instances" type = number default = 2}
# Changez `terraform.tfvars` pour scaler# desired_capacity = 5 # ASG créera 3 instances supplémentairesDépannage
Section intitulée « Dépannage »Le piège commun à ces quatre situations est que terraform apply réussit
malgré tout : l'ASG est bien créé, le state est cohérent, et pourtant aucune
instance ne tourne. Le diagnostic ne se fait donc pas dans Terraform mais dans
l'historique d'activité de l'ASG, consultable avec
aws autoscaling describe-scaling-activities --auto-scaling-group-name <nom> :
c'est là qu'AWS écrit la raison d'un lancement refusé.
| Symptôme | Cause | Solution |
|---|---|---|
| ASG créé mais 0 instance | Pas de subnets disponibles | Vérifier vpc_zone_identifier |
| Les instances sont correctes mais ASG unhealthy | Health check failed | Vérifier les security groups et ports |
| Plan montre "replacement" de l'ASG | Le name de l'ASG a changé, il force un remplacement | Vérifier ce qui fait varier le nom ; random_integer n'en est pas la cause, sa valeur est figée dans le state |
| Impossible de SSH aux instances | Pas de key pair ni security group | Ajouter key_name au template et SSH ingress |
Nettoyage
Section intitulée « Nettoyage »Ne sautez pas cette étape : les instances continuent d'être facturées tant
qu'elles tournent, et un ASG relance automatiquement toute instance que
vous arrêteriez à la main. La seule façon propre d'arrêter la facturation est
de détruire le groupe lui-même. Vérifiez ensuite dans la console EC2 qu'aucune
instance lab05-asg-instance ne subsiste.
Important : la destruction prend 3 à 4 minutes (arrêt des instances) :
terraform destroy -auto-approverm -rf .terraform* terraform.tfstate*À retenir
Section intitulée « À retenir »- Launch template = modèle réutilisable pour les instances
- ASG = gestionnaire automatique d'instances (min/max/desired)
- Instance refresh = mise à jour progressive des instances
latest_versionplutôt que l'alias"$Latest": ce dernier bloque le déclenchement de l'instance refresh- create_before_destroy = éviter les gaps de 0 instances
- random_integer = suffixe de nom unique, tiré une seule fois et conservé dans le state
- Scaling = changez
desired_capacitypour ajouter/retirer des instances
Pour aller plus loin
Section intitulée « Pour aller plus loin »- count : N ressources identiques : Crée plusieurs ressources identiques quand l'autoscaling ne suffit pas, avec le piège de l'index.
- for_each : instances nommées : Nomme chaque instance par une clé stable plutôt que par une position.
- Le bloc lifecycle : Détaille
create_before_destroy, la règle qui évite la coupure quand le template change.