# ๐ŸŒ Module Terraform โ€” VPC AWS multi-AZ **Format** : Terraform (.tf) ยท production-ready **Auteur** : ร‰quipe pรฉdagogique ITAG ยท DevOps Pack **Mise ร  jour** : 2026 (provider AWS 5.x ยท Terraform 1.7+) --- ## ๐ŸŽฏ Description Module Terraform rรฉutilisable pour provisionner un VPC AWS production-grade : 3 AZ, subnets publics + privรฉs + database, NAT gateway, bastion host optionnel, flow logs, network ACLs. ## ๐Ÿ“‹ Ressources crรฉรฉes ### Networking - `aws_vpc` (CIDR configurable, DNS hostnames + support) - 3 ร— `aws_subnet` publics (un par AZ) - 3 ร— `aws_subnet` privรฉs (compute) - 3 ร— `aws_subnet` database (isolรฉs) - `aws_internet_gateway` - 3 ร— `aws_nat_gateway` (HA โ€” un par AZ) ### Routing - `aws_route_table` public (vers IGW) - 3 ร— `aws_route_table` private (vers NAT) - `aws_route_table` database (sans Internet) ### Sรฉcuritรฉ - `aws_network_acl` strict pour DB - `aws_security_group` bastion (port 22 from your-ip) - `aws_flow_log` vers CloudWatch ### Optional - Bastion EC2 t3.nano (toggle var.enable_bastion) - VPC peering (toggle var.enable_peering) ## ๐Ÿ› ๏ธ Variables clรฉs - `vpc_cidr` (default `10.0.0.0/16`) - `azs` (default 3 premiรจres AZ de la rรฉgion) - `environment` (dev / staging / prod) - `enable_bastion` (bool) - `tags_default` ## ๐Ÿ’พ Code Terraform complet ### provider.tf ```hcl terraform { required_version = ">= 1.6" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } provider "aws" { region = var.region default_tags { tags = { Project = var.project_name Environment = var.environment ManagedBy = "Terraform" } } } ``` ### main.tf ```hcl # โ”€โ”€ VPC โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_hostnames = true enable_dns_support = true tags = { Name = "${var.project_name}-${var.environment}-vpc" } } # โ”€โ”€ SUBNETS PUBLICS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ resource "aws_subnet" "public_a" { vpc_id = aws_vpc.main.id cidr_block = "10.0.1.0/24" availability_zone = "${var.region}a" map_public_ip_on_launch = true tags = { Name = "${var.project_name}-${var.environment}-public-a" Tier = "public" } } resource "aws_subnet" "public_b" { vpc_id = aws_vpc.main.id cidr_block = "10.0.2.0/24" availability_zone = "${var.region}b" map_public_ip_on_launch = true tags = { Name = "${var.project_name}-${var.environment}-public-b" Tier = "public" } } # โ”€โ”€ SUBNETS PRIVร‰S โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ resource "aws_subnet" "private_a" { vpc_id = aws_vpc.main.id cidr_block = "10.0.10.0/24" availability_zone = "${var.region}a" tags = { Name = "${var.project_name}-${var.environment}-private-a" Tier = "private" } } resource "aws_subnet" "private_b" { vpc_id = aws_vpc.main.id cidr_block = "10.0.11.0/24" availability_zone = "${var.region}b" tags = { Name = "${var.project_name}-${var.environment}-private-b" Tier = "private" } } # โ”€โ”€ INTERNET GATEWAY โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id tags = { Name = "${var.project_name}-${var.environment}-igw" } } # โ”€โ”€ ROUTE TABLE PUBLIQUE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 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 } tags = { Name = "${var.project_name}-${var.environment}-rt-public" } } resource "aws_route_table_association" "public_a" { subnet_id = aws_subnet.public_a.id route_table_id = aws_route_table.public.id } resource "aws_route_table_association" "public_b" { subnet_id = aws_subnet.public_b.id route_table_id = aws_route_table.public.id } # โ”€โ”€ NAT GATEWAYS (HA โ€” un par AZ) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ resource "aws_eip" "nat_a" { count = var.enable_nat_gateway ? 1 : 0 domain = "vpc" tags = { Name = "${var.project_name}-${var.environment}-eip-nat-a" } } resource "aws_eip" "nat_b" { count = var.enable_nat_gateway ? 1 : 0 domain = "vpc" tags = { Name = "${var.project_name}-${var.environment}-eip-nat-b" } } resource "aws_nat_gateway" "nat_a" { count = var.enable_nat_gateway ? 1 : 0 allocation_id = aws_eip.nat_a[0].id subnet_id = aws_subnet.public_a.id tags = { Name = "${var.project_name}-${var.environment}-nat-a" } depends_on = [aws_internet_gateway.main] } resource "aws_nat_gateway" "nat_b" { count = var.enable_nat_gateway ? 1 : 0 allocation_id = aws_eip.nat_b[0].id subnet_id = aws_subnet.public_b.id tags = { Name = "${var.project_name}-${var.environment}-nat-b" } depends_on = [aws_internet_gateway.main] } # โ”€โ”€ ROUTE TABLES PRIVร‰ES (via NAT) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ resource "aws_route_table" "private_a" { count = var.enable_nat_gateway ? 1 : 0 vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" nat_gateway_id = aws_nat_gateway.nat_a[0].id } tags = { Name = "${var.project_name}-${var.environment}-rt-private-a" } } resource "aws_route_table" "private_b" { count = var.enable_nat_gateway ? 1 : 0 vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" nat_gateway_id = aws_nat_gateway.nat_b[0].id } tags = { Name = "${var.project_name}-${var.environment}-rt-private-b" } } resource "aws_route_table_association" "private_a" { count = var.enable_nat_gateway ? 1 : 0 subnet_id = aws_subnet.private_a.id route_table_id = aws_route_table.private_a[0].id } resource "aws_route_table_association" "private_b" { count = var.enable_nat_gateway ? 1 : 0 subnet_id = aws_subnet.private_b.id route_table_id = aws_route_table.private_b[0].id } # โ”€โ”€ SECURITY GROUP BASTION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ resource "aws_security_group" "bastion" { name = "${var.project_name}-${var.environment}-bastion-sg" description = "Security group for bastion host โ€” SSH access" vpc_id = aws_vpc.main.id ingress { description = "SSH from allowed CIDR" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = [var.allowed_cidr] } egress { description = "All outbound traffic" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "${var.project_name}-${var.environment}-bastion-sg" } } ``` ### variables.tf ```hcl variable "region" { description = "AWS region to deploy resources" type = string default = "eu-west-1" } variable "environment" { description = "Deployment environment (dev / staging / prod)" type = string default = "dev" validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "environment must be one of: dev, staging, prod." } } variable "project_name" { description = "Name used as prefix for all resource names" type = string default = "myapp" } variable "vpc_cidr" { description = "CIDR block for the VPC" type = string default = "10.0.0.0/16" } variable "allowed_cidr" { description = "CIDR allowed to SSH into the bastion host" type = string default = "0.0.0.0/0" # Restreindre en production ! } variable "enable_nat_gateway" { description = "Provision NAT Gateways for private subnets (adds cost)" type = bool default = true } ``` ### outputs.tf ```hcl output "vpc_id" { description = "ID of the created VPC" value = aws_vpc.main.id } output "public_subnet_ids" { description = "List of public subnet IDs" value = [aws_subnet.public_a.id, aws_subnet.public_b.id] } output "private_subnet_ids" { description = "List of private subnet IDs" value = [aws_subnet.private_a.id, aws_subnet.private_b.id] } output "nat_gateway_ids" { description = "IDs of NAT Gateways (empty if enable_nat_gateway=false)" value = var.enable_nat_gateway ? [ aws_nat_gateway.nat_a[0].id, aws_nat_gateway.nat_b[0].id ] : [] } output "bastion_sg_id" { description = "ID of the bastion security group" value = aws_security_group.bastion.id } ``` ## ๐Ÿ’ผ Cas d'usage - Dรฉmarrer une infra AWS from scratch - Prรฉparation Terraform Associate / AWS SAA - Mise en place CI/CD multi-environnements - Modรจle pour SaaS B2B ## ๐Ÿ“ฅ Tรฉlรฉcharger ce template [Tรฉlรฉcharger le fichier .md](/templates/view.php?file=terraform-aws-vpc&dl=1) ยท [โ† Catalogue Terraform](/templates.php?cat=terraform) ยท [Parcours DevOps โ†’](/parcours/tech-devops.php) --- *ITAG ยท Non-affiliรฉs HashiCorp / AWS. Terraformยฎ et AWSยฎ sont des marques dรฉposรฉes.*