Aller au contenu principal

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

  1. Quel fichier stocke l'état de l'infrastructure ?

    • a) main.tf
    • b) terraform.tfstate
    • c) variables.tf
    • d) providers.tf
  2. Quelle commande prévisualise les changements ?

    • a) terraform init
    • b) terraform apply
    • c) terraform plan
    • d) terraform validate
  3. 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
  4. Où stocker le state en production ?

    • a) Localement
    • b) Dans Git
    • c) Dans un backend distant (S3, GCS)
    • d) Dans le répertoire /tmp
  5. 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

  1. b) terraform.tfstate
  2. c) terraform plan
  3. b) Utiliser count ou for_each
  4. c) Dans un backend distant
  5. 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 !


← Bonnes pratiques | Table des matières