
Créer une EC2 sans parler du réseau fonctionne pour un premier test, mais ce n'est pas encore une infrastructure exploitable. Très vite, vous devez répondre à trois questions concrètes : dans quel subnet l'instance doit vivre, quel security group filtre le trafic, et quels ports sont réellement ouverts. C'est cette transition entre "ça démarre" et "c'est joignable proprement" que ce guide traite.
Vous allez relire des ressources AWS déjà présentes, puis ajouter votre première couche réseau explicite. L'idée importante pour un débutant est la suivante : vous ne recréez pas tout. Vous branchez votre instance sur un réseau existant et vous lui attachez un pare-feu virtuel qui définit ses règles d'entrée.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Lire les ressources existantes avec les data sources (
aws_vpc,aws_subnet) - Créer un security group pour contrôler les règles réseau
- Autoriser SSH et HTTP via
aws_vpc_security_group_ingress_rule - Attacher une instance à un subnet spécifique avec des règles réseau précises
- Vérifier les sorties : subnet, security group, IP publique/privée
Pourquoi cette étape compte
Section intitulée « Pourquoi cette étape compte »Dans AWS, le réseau n'est pas un détail caché derrière l'instance. Une EC2 hérite d'un contexte réseau précis : VPC, subnet, security group. Si vous ne maîtrisez pas cet enchaînement, vous obtenez facilement une machine qui existe mais que vous ne pouvez ni joindre, ni sécuriser correctement.
Ce guide vous apprend donc une logique qui reviendra partout ensuite : lire ce qui existe, ajouter seulement la couche nécessaire, puis vérifier les identifiants réseau obtenus.
Prérequis
Section intitulée « Prérequis »- Terraform ≥ 1.11 installé
- AWS CLI configuré avec credentials valides
- Avoir lu Déployer une première EC2
Objectif
Section intitulée « Objectif »Vous allez construire une infrastructure qui :
- Récupère le VPC par défaut d'AWS
- Sélectionne un subnet existant
- Crée un security group avec règles d'ingress SSH et HTTP
- Lance une EC2 dans ce subnet protégée par le security group
Durée estimée : 15 minutes Coût : Gratuit ou ~$0.01 (tier gratuit AWS)
Préparation
Section intitulée « Préparation »Terraform lit tous les fichiers .tf du répertoire courant et les traite comme une seule configuration : l'ordre des fichiers et des blocs n'a aucune importance. Travaillez donc dans un répertoire vide et dédié, sinon une ancienne configuration oubliée sera fusionnée avec la nouvelle.
mkdir -p ~/terraform-aws-sg-subnetcd ~/terraform-aws-sg-subnetÉtape 1, Déclarer le provider AWS
Section intitulée « Étape 1, Déclarer le provider AWS »Créez versions.tf pour configurer le provider et les versions :
terraform { required_version = ">= 1.11.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } }}
provider "aws" { region = var.aws_region}Cela dit à Terraform :
- Utiliser Terraform ≥ 1.11.0
- Charger le provider AWS version ~5.0 (5.x stable)
- Se connecter à AWS dans la région définie par
var.aws_region
Étape 2, Déclarer les variables
Section intitulée « Étape 2, Déclarer les variables »Créez variables.tf :
variable "aws_region" { description = "AWS region" type = string default = "us-east-1"}
variable "instance_name" { description = "Name tag for the EC2 instance" type = string default = "lab02-instance-with-sg"}
variable "instance_type" { description = "EC2 instance type" type = string default = "t2.micro"}Ces variables vous permettront de changer de région ou de type d'instance sans modifier le code principal.
Étape 3, Lire VPC, subnet et AMI existants
Section intitulée « Étape 3, Lire VPC, subnet et AMI existants »Créez main.tf avec les data sources :
# Récupérer le VPC par défautdata "aws_vpc" "default" { default = true}
# Récupérer le premier subnet disponible du VPC par défautdata "aws_subnets" "default" { filter { name = "vpc-id" values = [data.aws_vpc.default.id] }}
# Lire l'AMI Ubuntu la plus récentedata "aws_ami" "ubuntu" { most_recent = true owners = ["099720109477"] # Canonical (Ubuntu)
filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] }
filter { name = "virtualization-type" values = ["hvm"] }}Ce bloc utilise les data sources Terraform pour lire l'infrastructure existante d'AWS sans la recréer :
data "aws_vpc" "default": récupère le VPC par défautdata "aws_subnets" "default": récupère tous les subnets du VPCdata "aws_ami" "ubuntu": récupère la dernière Ubuntu 22.04
Étape 4, Créer le security group
Section intitulée « Étape 4, Créer le security group »Toujours dans main.tf, ajoutez :
resource "aws_security_group" "lab02_sg" { name = "lab02-sg" description = "Security group for Lab 02 - SSH and HTTP access" vpc_id = data.aws_vpc.default.id
tags = { Name = "lab02-sg" }}Un security group est un firewall virtuel pour vos instances. Vous allez lui ajouter des règles ensuite.
Étape 5, Autoriser SSH et HTTP
Section intitulée « Étape 5, Autoriser SSH et HTTP »Toujours dans main.tf, ajoutez les règles d'ingress :
# Autoriser SSH (port 22)resource "aws_vpc_security_group_ingress_rule" "ssh" { security_group_id = aws_security_group.lab02_sg.id description = "Allow SSH from anywhere" from_port = 22 to_port = 22 ip_protocol = "tcp" cidr_ipv4 = "0.0.0.0/0"
tags = { Name = "allow-ssh" }}
# Autoriser HTTP (port 80)resource "aws_vpc_security_group_ingress_rule" "http" { security_group_id = aws_security_group.lab02_sg.id description = "Allow HTTP from anywhere" from_port = 80 to_port = 80 ip_protocol = "tcp" cidr_ipv4 = "0.0.0.0/0"
tags = { Name = "allow-http" }}Chaque rule dit : "Autoriser le trafic TCP entrant sur le port X depuis n'importe quel IP (0.0.0.0/0)".
Étape 6, Créer l'instance dans le subnet avec le security group
Section intitulée « Étape 6, Créer l'instance dans le subnet avec le security group »Toujours dans main.tf, complétez :
resource "aws_instance" "lab02_vm" { ami = data.aws_ami.ubuntu.id instance_type = var.instance_type subnet_id = data.aws_subnets.default.ids[0] vpc_security_group_ids = [aws_security_group.lab02_sg.id]
tags = { Name = var.instance_name }}Points clés :
subnet_id: place l'instance dans le subnet sélectionnévpc_security_group_ids: associe le security group à l'instance[aws_security_group.lab02_sg.id]: c'est une liste → contrairement àsecurity_groups, qui s'utilise dans le VPC par défaut,vpc_security_group_idsest explicite et recommandé
Étape 7, Déclarer les outputs
Section intitulée « Étape 7, Déclarer les outputs »Créez outputs.tf :
output "vpc_id" { description = "VPC ID" value = data.aws_vpc.default.id}
output "subnet_id" { description = "Subnet ID where instance is running" value = aws_instance.lab02_vm.subnet_id}
output "security_group_id" { description = "Security Group ID" value = aws_security_group.lab02_sg.id}
output "instance_id" { description = "EC2 Instance ID" value = aws_instance.lab02_vm.id}
output "instance_public_ip" { description = "Public IP address of the instance" value = aws_instance.lab02_vm.public_ip}
output "instance_private_ip" { description = "Private IP address of the instance" value = aws_instance.lab02_vm.private_ip}
output "ubuntu_ami_id" { description = "Ubuntu AMI ID" value = data.aws_ami.ubuntu.id}Étape 8, Créer le fichier tfvars
Section intitulée « Étape 8, Créer le fichier tfvars »Créez terraform.tfvars :
aws_region = "us-east-1"instance_name = "lab02-instance-with-sg"instance_type = "t2.micro"Vérification
Section intitulée « Vérification »Les quatre commandes ci-dessous s'enchaînent toujours dans cet ordre. terraform validate ne contacte pas AWS, il ne vérifie que la syntaxe et la cohérence des références. C'est terraform plan qui interroge réellement l'API pour résoudre les data sources, d'où les valeurs de subnet_id et ubuntu_ami_id déjà connues avant l'apply.
-
Initialiser Terraform :
Fenêtre de terminal terraform initSortie attendue :
Initializing the backend...Initializing provider plugins...- Finding hashicorp/aws versions matching "~> 5.0"...- Installing hashicorp/aws v5.100.0...Terraform has been successfully initialized! -
Valider la configuration :
Fenêtre de terminal terraform validateSortie attendue :
Success! The configuration is valid. -
Afficher le plan :
Fenêtre de terminal terraform planSortie attendue (résumé) :
Plan: 4 to add, 0 to change, 0 to destroy.Changes to Outputs:+ instance_id = (known after apply)+ instance_private_ip = (known after apply)+ instance_public_ip = (known after apply)+ security_group_id = (known after apply)+ subnet_id = "subnet-xxxxxxxxx"+ ubuntu_ami_id = "ami-xxxxxxxx"+ vpc_id = "vpc-xxxxxxxx" -
Appliquer la configuration :
Fenêtre de terminal terraform apply -auto-approveSortie attendue (fin) :
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.Outputs:instance_id = "i-0e43f24bc8817d50d"instance_private_ip = "172.31.69.130"instance_public_ip = "32.195.205.231"security_group_id = "sg-07b0de0b4eca49e47"subnet_id = "subnet-ef519de1"ubuntu_ami_id = "ami-00de3875b03809ec5"vpc_id = "vpc-801432fa" -
Vérifier l'accès SSH (optionnel, la clé par défaut n'est pas disponible) :
Fenêtre de terminal aws ec2 describe-instances --instance-ids i-0e43f24bc8817d50d
Anatomie
Section intitulée « Anatomie »Maintenant que l'infrastructure est en place, revenons sur les deux décisions structurantes de cette configuration : l'emploi des data sources plutôt que de nouvelles ressources, et la séparation des règles d'ingress hors du bloc aws_security_group. Ce sont deux choix qui reviendront dans toutes vos configurations AWS.
Data sources vs Resources
Section intitulée « Data sources vs Resources »La distinction se voit dans le plan : les data sources n'apparaissent jamais dans le décompte « to add », seules les resource y figurent. Ici, quatre ressources sont créées alors que sept objets sont manipulés.
| Type | Utilité | Exemple |
|---|---|---|
| Data source | Lire de l'infrastructure existante | data "aws_vpc", data "aws_ami" |
| Resource | Créer nouvelle infrastructure | resource "aws_security_group", resource "aws_instance" |
Les data sources ne créent rien, elles lisent uniquement. Cela vous permet de réutiliser ce qui existe déjà (VPC par défaut, AMI) sans duplication.
Security Group vs VPC Security Group Ingress Rule
Section intitulée « Security Group vs VPC Security Group Ingress Rule »Deux approches pour les règles :
# Approche 1 : Règles DANS la ressource (inline), dépréciéresource "aws_security_group" "example" { ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] }}
# Approche 2 : Ressources SÉPARÉES (recommended) ✅resource "aws_security_group" "example" { name = "example"}
resource "aws_vpc_security_group_ingress_rule" "ssh" { security_group_id = aws_security_group.example.id from_port = 22 to_port = 22 ip_protocol = "tcp" cidr_ipv4 = "0.0.0.0/0"}Pourquoi séparer ?
- Plus granulaire : vous pouvez ajouter/modifier/supprimer une seule règle sans refactoriser tout le security group
- Plus lisible : chaque règle est explicite
- Meilleure gestion en équipe : moins de conflits lors de merges
Bonnes pratiques
Section intitulée « Bonnes pratiques »Un security group est l'un des rares objets AWS dont une erreur de configuration est directement exploitable depuis Internet. Les quatre règles ci-dessous portent donc sur deux axes : rendre les groupes identifiables pour pouvoir les auditer, et réduire la surface exposée.
1. Nommer clairement les security groups
Section intitulée « 1. Nommer clairement les security groups »Le champ name est celui qu'affiche la console AWS et il est unique par VPC : un groupe nommé sg devient impossible à distinguer dès le troisième projet.
# ❌ Mauvaisresource "aws_security_group" "main" { name = "sg"}
# ✅ Bonresource "aws_security_group" "web_access" { name = "web-access-sg"}2. Restreindre 0.0.0.0/0 en production
Section intitulée « 2. Restreindre 0.0.0.0/0 en production »Un port 22 ouvert sur 0.0.0.0/0 est scanné et attaqué en quelques minutes après création de l'instance. Remplacez-le par le préfixe de votre VPN ou de votre sortie Internet ; pour un accès nomade, préférez une solution sans port exposé comme AWS Systems Manager Session Manager.
# ❌ Risquéecidr_ipv4 = "0.0.0.0/0"
# ✅ Produitcidr_ipv4 = "203.0.113.0/24" # Votre bureau/VPN3. Utiliser des tags pour tracer qui a créé quoi
Section intitulée « 3. Utiliser des tags pour tracer qui a créé quoi »Sans tag ManagedBy, rien ne distingue dans la console une ressource pilotée par Terraform d'une ressource créée à la main : la modifier depuis la console provoque une dérive que le prochain apply écrasera sans prévenir.
tags = { Name = "lab02-sg" Environment = "lab" ManagedBy = "terraform"}4. Minimiser les ports ouverts
Section intitulée « 4. Minimiser les ports ouverts »La valeur ip_protocol = "-1" signifie tous les protocoles, et elle rend les champs from_port et to_port sans effet : la règle ouvre alors la totalité du trafic entrant, pas seulement les ports listés.
# ❌ Trop permissiffrom_port = 0to_port = 65535ip_protocol = "-1" # Tous les protocoles
# ✅ Juste le nécessairefrom_port = 22to_port = 22ip_protocol = "tcp"Dépannage
Section intitulée « Dépannage »Les erreurs de ce lab se répartissent en deux familles : celles remontées par Terraform pendant l'apply, qui portent un message AWS explicite, et celles qui n'apparaissent qu'après coup, quand l'instance est créée mais injoignable. Pour la seconde famille, vérifiez toujours l'association du security group avant de soupçonner les règles elles-mêmes.
| Symptôme | Cause probable | Solution |
|---|---|---|
Error: No aws_subnet could be selected | Pas de subnet dans le VPC par défaut | Créer un VPC/subnet manuellement ou utiliser data "aws_availab availability_zones" |
Error: creating Security Group: ... InvalidParameterValue | Caractères spéciaux dans la description | Utiliser uniquement ASCII (pas d'accents, tirets longs) |
| Instance créée mais pas accessible SSH | La clé .pem n'est pas attachée | Ajouter key_name = aws_key_pair.xxx.key_name à la ressource |
| Port 22 ou 80 fermé | Security group non attachée ou règle manquante | Vérifier vpc_security_group_ids et les ingress rules |
Nettoyage
Section intitulée « Nettoyage »Après le lab, détruisez l'infrastructure pour ne pas être facturé :
terraform destroy -auto-approveSortie attendue :
Destroy complete! Resources: 4 destroyed.Nettoyez les fichiers Terraform générés :
rm -rf .terraform* terraform.tfstate*À retenir
Section intitulée « À retenir »- Les data sources permettent de lire l'infrastructure existante (VPC, subnet, AMI) sans la recréer
- aws_vpc_security_group_ingress_rule est l'approche recommandée pour les règles (séparation des préoccupations)
- Les security groups sont des pare-feu virtuels, ils contrôlent qui peut accéder à vos instances
- vpc_security_group_ids (liste) est plus robuste que
security_groups(approche legacy) - Les outputs vous permettent de voir les IDs importants après l'apply
- Testez le plan avant l'apply pour voir exactement quoi Terraform créera
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Backend S3 et terraform_remote_state : Déplace le state local vers un bucket partagé, étape obligatoire dès que l'on travaille à plusieurs.
- Launch template et autoscaling group : Passe d'une instance unique à un groupe d'instances géré par AWS.
- Import, moved et drift : Reprend dans le code une ressource créée à la main, puis mesure les écarts.