Aller au contenu
Infrastructure as Code medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Terraform AWS — Security groups, subnet et instance

15 min de lecture

logo terraform

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.

  • 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

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.

Vous allez construire une infrastructure qui :

  1. Récupère le VPC par défaut d’AWS
  2. Sélectionne un subnet existant
  3. Crée un security group avec règles d’ingress SSH et HTTP
  4. 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)

Créez le répertoire du lab :

Fenêtre de terminal
mkdir -p ~/terraform-aws-sg-subnet
cd ~/terraform-aws-sg-subnet

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

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.

Créez main.tf avec les data sources :

# Récupérer le VPC par défaut
data "aws_vpc" "default" {
default = true
}
# Récupérer le premier subnet disponible du VPC par défaut
data "aws_subnets" "default" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
}
# Lire l'AMI Ubuntu la plus récente
data "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éfaut
  • data "aws_subnets" "default" : récupère tous les subnets du VPC
  • data "aws_ami" "ubuntu" : récupère la dernière Ubuntu 22.04

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.

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_ids est explicite et recommandé

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
}

Créez terraform.tfvars :

aws_region = "us-east-1"
instance_name = "lab02-instance-with-sg"
instance_type = "t2.micro"

Maintenant exécutez le lab :

  1. Initialiser Terraform :

    Fenêtre de terminal
    terraform init

    Sortie 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!
  2. Valider la configuration :

    Fenêtre de terminal
    terraform validate

    Sortie attendue :

    Success! The configuration is valid.
  3. Afficher le plan :

    Fenêtre de terminal
    terraform plan

    Sortie 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"
  4. Appliquer la configuration :

    Fenêtre de terminal
    terraform apply -auto-approve

    Sortie 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"
  5. 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
TypeUtilitéExemple
Data sourceLire de l’infrastructure existantedata "aws_vpc", data "aws_ami"
ResourceCréer nouvelle infrastructureresource "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.

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
# ❌ Mauvais
resource "aws_security_group" "main" {
name = "sg"
}
# ✅ Bon
resource "aws_security_group" "web_access" {
name = "web-access-sg"
}
# ❌ Risquée
cidr_ipv4 = "0.0.0.0/0"
# ✅ Produit
cidr_ipv4 = "203.0.113.0/24" # Votre bureau/VPN

3. Utiliser des tags pour tracer qui a créé quoi

Section intitulée « 3. Utiliser des tags pour tracer qui a créé quoi »
tags = {
Name = "lab02-sg"
Environment = "lab"
ManagedBy = "terraform"
}
# ❌ Trop permissif
from_port = 0
to_port = 65535
ip_protocol = "-1" # Tous les protocoles
# ✅ Juste le nécessaire
from_port = 22
to_port = 22
ip_protocol = "tcp"
SymptômeCause probableSolution
Error: No aws_subnet could be selectedPas de subnet dans le VPC par défautCréer un VPC/subnet manuellement ou utiliser data "aws_availab availability_zones"
Error: creating Security Group: ... InvalidParameterValueCaractères spéciaux dans la descriptionUtiliser uniquement ASCII (pas d’accents, tirets longs)
Instance créée mais pas accessible SSHLa clé .pem n’est pas attachéeAjouter key_name = aws_key_pair.xxx.key_name à la ressource
Port 22 ou 80 ferméSecurity group non attachée ou règle manquanteVérifier vpc_security_group_ids et les ingress rules

Après le lab, détruisez l’infrastructure pour ne pas être facturé :

Fenêtre de terminal
terraform destroy -auto-approve

Sortie attendue :

Destroy complete! Resources: 4 destroyed.

Nettoyez les fichiers Terraform générés :

Fenêtre de terminal
rm -rf .terraform* terraform.tfstate*
  1. Les data sources permettent de lire l’infrastructure existante (VPC, subnet, AMI) sans la recréer
  2. aws_vpc_security_group_ingress_rule est l’approche recommandée pour les règles (séparation des préoccupations)
  3. Les security groups sont des firewalls virtuel — ils contrôlent qui peut accéder à vos instances
  4. vpc_security_group_ids (liste) est plus robuste que security_groups (approche legacy)
  5. Les outputs vous permettent de voir les IDs importants après l’apply
  6. Testez le plan avant l’apply pour voir exactement quoi Terraform créera

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn