AWS avec Terraform : construire une infrastructure reproductible – Partie 1/2

Objectif
Décrire une petite infrastructure AWS dans du code Terraform, la déployer et vérifier qu’elle correspond exactement à l’état attendu.
| Question de départ : si l’infrastructure n’existait que dans la console AWS, comment la reprendre proprement et la rendre reproductible ? |
Synopsis
Un VPC créé dans la console, un subnet ajouté plus tard, une règle réseau modifiée dans l’urgence : l’environnement peut fonctionner sans être réellement maîtrisé. Nous allons faire du code Terraform la source de vérité de cette infrastructure.
Prérequis
Le laboratoire est réalisé depuis une VM Debian 13 avec Git, AWS CLI et Terraform.

Architecture du laboratoire
Nous partons d’une architecture volontairement simple : un VPC, un subnet public, une EC2 Amazon Linux 2023 et Nginx. Le but n’est pas encore la haute disponibilité, mais la reproductibilité.

Accès AWS avec IAM Identity Center
Le lab est déployé dans le compte membre SAYOOS-LAB. L’accès passe par IAM Identity Center avec MFA, l’utilisateur sayoos-devops, le groupe devops-lab et le Permission Set SAYOOS-DevOps-PowerUser.

aws configure sso –profile sayoos-lab
aws sso login –profile sayoos-lab
aws sts get-caller-identity –profile sayoos-lab

Pour la session courante :
export AWS_PROFILE=sayoos-lab
export AWS_REGION=eu-west-3
Préparer le projet Terraform
mkdir -p ~/labs/sayoos-terraform-aws
cd ~/labs/sayoos-terraform-aws
git init
git branch -m main

Protéger le dépôt Git
Le state Terraform, les plans locaux et le dossier .terraform ne doivent pas être versionnés. Le fichier .terraform.lock.hcl reste, lui, dans Git.

Déclarer Terraform et AWS
Dans terraform.tf :
terraform {
required_version = ">= 1.16.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
Dans provider.tf :
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Project = "SAYOOS-DevOps-Lab"
Environment = "lab"
ManagedBy = "Terraform"
}
}
}
Variables du projet
variable "aws_region" {
type = string
default = "eu-west-3"
}
variable "project_name" {
type = string
default = "sayoos-devops-lab"
}
variable "vpc_cidr" {
type = string
default = "10.10.0.0/16"
}
variable "public_subnet_cidr" {
type = string
default = "10.10.10.0/24"
}
Construire le réseau AWS
Dans network.tf, nous déclarons le VPC, l’Internet Gateway, le subnet public et la route par défaut vers Internet.
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = { Name = "${var.project_name}-vpc" }
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = { Name = "${var.project_name}-igw" }
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidr
map_public_ip_on_launch = true
tags = { Name = "${var.project_name}-public-subnet" }
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
Autoriser le trafic HTTP
resource "aws_security_group" "web" {
name = "${var.project_name}-web-sg"
description = "Autorise le trafic HTTP vers le serveur web"
vpc_id = aws_vpc.main.id
}
resource "aws_vpc_security_group_ingress_rule" "http" {
security_group_id = aws_security_group.web.id
cidr_ipv4 = "0.0.0.0/0"
from_port = 80
to_port = 80
ip_protocol = "tcp"
}
Nous n’ouvrons pas SSH : la configuration initiale du serveur sera automatisée par user_data.
Déployer EC2 et Nginx
Terraform recherche une AMI Amazon Linux 2023 récente, puis crée l’instance dans le subnet public.
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-2023.*-kernel-6.1-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
associate_public_ip_address = true
user_data_replace_on_change = true
user_data = <<-EOF
#!/bin/bash
dnf install -y nginx
cat > /usr/share/nginx/html/index.html <<'HTML'
<h1>SAYOOS DevOps Lab</h1>
<p>Infrastructure AWS provisionnée avec Terraform.</p>
HTML
systemctl enable --now nginx
EOF
tags = { Name = "${var.project_name}-web" }
}
Afficher les informations utiles
output "instance_public_ip" {
value = aws_instance.web.public_ip
}
output "web_url" {
value = "http://${aws_instance.web.public_ip}"
}
Initialiser, valider et planifier
terraform init
terraform fmt
terraform validate
terraform plan -out=tfplan
Le plan du lab annonce neuf créations et aucune modification ou destruction :
Plan: 9 to add, 0 to change, 0 to destroy.

Premier déploiement
terraform apply tfplan

Valider le service
curl -I "$(terraform output -raw web_url)"
terraform output -raw web_url

Vérifier l’idempotence
terraform plan
| Résultat attendu : No changes. Your infrastructure matches the configuration. |
Fin de la partie 1/2
Notre infrastructure fonctionne et Terraform considère qu’elle correspond au code. Il reste maintenant le test qui compte vraiment : la supprimer entièrement, puis vérifier que le même dépôt permet de remettre le service en ligne.
| Dans la partie 2/2 : destruction, reconstruction, modification ciblée de l’EC2 et évolution de l’architecture vers un modèle plus proche de la production. |