Exercices et projets
Objectifs
- Consolider vos connaissances Terraform
- Mettre en pratique tous les concepts
- Développer des compétences opérationnelles
Exercice 1 : Premier déploiement
Objectif
Créer une instance EC2 simple avec Terraform.
Fichiers à créer
# main.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = {
Name = "web-server"
Environment = var.environment
}
}
# variables.tf
variable "region" {
description = "AWS region"
type = string
default = "eu-west-1"
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "environment" {
description = "Environment name"
type = string
default = "development"
}
# outputs.tf
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.web.id
}
output "public_ip" {
description = "Public IP of the EC2 instance"
value = aws_instance.web.public_ip
}
Commandes
terraform init
terraform plan
terraform apply
terraform destroy
Exercice 2 : VPC complet
Objectif
Créer un VPC avec subnets publics et privés.
Architecture
Code
# vpc.tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project}-vpc"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project}-igw"
}
}
resource "aws_subnet" "public" {
count = length(var.public_subnets)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnets[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project}-public-${count.index + 1}"
Type = "public"
}
}
resource "aws_subnet" "private" {
count = length(var.private_subnets)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnets[count.index]
availability_zone = var.availability_zones[count.index]
tags = {
Name = "${var.project}-private-${count.index + 1}"
Type = "private"
}
}
resource "aws_eip" "nat" {
domain = "vpc"
tags = {
Name = "${var.project}-nat-eip"
}
}
resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public[0].id
tags = {
Name = "${var.project}-nat"
}
depends_on = [aws_internet_gateway.main]
}
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}-public-rt"
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
tags = {
Name = "${var.project}-private-rt"
}
}
resource "aws_route_table_association" "public" {
count = length(var.public_subnets)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
count = length(var.private_subnets)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private.id
}
# variables.tf
variable "project" {
default = "myproject"
}
variable "vpc_cidr" {
default = "10.0.0.0/16"
}
variable "availability_zones" {
default = ["eu-west-1a", "eu-west-1b"]
}
variable "public_subnets" {
default = ["10.0.1.0/24", "10.0.2.0/24"]
}
variable "private_subnets" {
default = ["10.0.10.0/24", "10.0.11.0/24"]
}
Exercice 3 : Module réutilisable
Objectif
Créer un module EC2 réutilisable.
Structure
modules/
└── ec2-instance/
├── main.tf
├── variables.tf
└── outputs.tf
Code du module
# modules/ec2-instance/variables.tf
variable "name" {
description = "Name of the instance"
type = string
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
variable "subnet_id" {
description = "Subnet ID"
type = string
}
variable "security_group_ids" {
description = "List of security group IDs"
type = list(string)
default = []
}
variable "tags" {
description = "Additional tags"
type = map(string)
default = {}
}
# modules/ec2-instance/main.tf
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
resource "aws_instance" "this" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
subnet_id = var.subnet_id
vpc_security_group_ids = var.security_group_ids
tags = merge(var.tags, {
Name = var.name
})
}
# modules/ec2-instance/outputs.tf
output "id" {
description = "Instance ID"
value = aws_instance.this.id
}
output "private_ip" {
description = "Private IP"
value = aws_instance.this.private_ip
}
output "public_ip" {
description = "Public IP"
value = aws_instance.this.public_ip
}
Utilisation
# main.tf
module "web_server" {
source = "./modules/ec2-instance"
name = "web-server"
instance_type = "t3.small"
subnet_id = module.vpc.public_subnet_ids[0]
security_group_ids = [aws_security_group.web.id]
tags = {
Environment = "production"
Role = "webserver"
}
}
module "api_server" {
source = "./modules/ec2-instance"
name = "api-server"
instance_type = "t3.medium"
subnet_id = module.vpc.private_subnet_ids[0]
security_group_ids = [aws_security_group.api.id]
tags = {
Environment = "production"
Role = "api"
}
}
Projet 1 : Application 3-tiers
Architecture
Structure
project-3tier/
├── main.tf
├── variables.tf
├── outputs.tf
├── backend.tf
├── vpc.tf
├── security.tf
├── alb.tf
├── ec2.tf
├── rds.tf
└── terraform.tfvars
Fichiers principaux
# main.tf
terraform {
required_version = ">= 1.5.0"
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"
}
}
}
locals {
name_prefix = "${var.project_name}-${var.environment}"
}
# ec2.tf
resource "aws_launch_template" "app" {
name_prefix = "${local.name_prefix}-app-"
image_id = data.aws_ami.ubuntu.id
instance_type = var.app_instance_type
vpc_security_group_ids = [aws_security_group.app.id]
user_data = base64encode(<<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl start nginx
EOF
)
tag_specifications {
resource_type = "instance"
tags = {
Name = "${local.name_prefix}-app"
}
}
}
resource "aws_autoscaling_group" "app" {
name = "${local.name_prefix}-app-asg"
desired_capacity = var.app_desired_count
max_size = var.app_max_count
min_size = var.app_min_count
vpc_zone_identifier = aws_subnet.private[*].id
target_group_arns = [aws_lb_target_group.app.arn]
launch_template {
id = aws_launch_template.app.id
version = "$Latest"
}
tag {
key = "Name"
value = "${local.name_prefix}-app"
propagate_at_launch = true
}
}
# rds.tf
resource "aws_db_subnet_group" "main" {
name = "${local.name_prefix}-db-subnet"
subnet_ids = aws_subnet.private[*].id
}
resource "aws_db_instance" "main" {
identifier = "${local.name_prefix}-db"
engine = "postgres"
engine_version = "15"
instance_class = var.db_instance_class
allocated_storage = 20
storage_encrypted = true
db_name = var.db_name
username = var.db_username
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.db.id]
skip_final_snapshot = true
tags = {
Name = "${local.name_prefix}-db"
}
}
Projet 2 : Multi-environnements
Structure
terraform-multi-env/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── backend.tf
│ │ └── terraform.tfvars
│ ├── staging/
│ │ ├── main.tf
│ │ ├── backend.tf
│ │ └── terraform.tfvars
│ └── production/
│ ├── main.tf
│ ├── backend.tf
│ └── terraform.tfvars
└── modules/
├── vpc/
├── compute/
└── database/
Configuration par environnement
# environments/dev/terraform.tfvars
environment = "dev"
instance_type = "t3.micro"
instance_count = 1
db_instance_class = "db.t3.micro"
# environments/production/terraform.tfvars
environment = "production"
instance_type = "t3.large"
instance_count = 3
db_instance_class = "db.r6g.large"
Quiz de validation
Questions
-
Quel fichier stocke l'état de l'infrastructure ?
- a) main.tf
- b) terraform.tfstate
- c) variables.tf
- d) providers.tf
-
Quelle commande prévisualise les changements ?
- a) terraform init
- b) terraform apply
- c) terraform plan
- d) terraform validate
-
Comment créer plusieurs ressources similaires ?
- a) Copier-coller le bloc resource
- b) Utiliser count ou for_each
- c) Créer plusieurs fichiers
- d) Utiliser des boucles while
-
Où stocker le state en production ?
- a) Localement
- b) Dans Git
- c) Dans un backend distant (S3, GCS)
- d) Dans le répertoire /tmp
-
Comment r éutiliser du code Terraform ?
- a) Avec des scripts bash
- b) Avec des modules
- c) Avec des templates
- d) Avec des includes
Réponses
- b) terraform.tfstate
- c) terraform plan
- b) Utiliser count ou for_each
- c) Dans un backend distant
- b) Avec des modules
Ressources supplémentaires
Documentation
Outils
Communauté
Conclusion
Félicitations ! Vous avez terminé le cours Terraform.
Vous maîtrisez maintenant :
- La syntaxe HCL et les providers
- La création et gestion de ressources
- Les variables, outputs et state
- Les modules réutilisables
- Les bonnes pratiques de production
Continuez à pratiquer avec de vrais projets cloud !