Aller au contenu principal

Ressources Terraform


Objectifs du chapitre

  • Comprendre le cycle de vie des ressources
  • Créer différents types de ressources
  • Utiliser les data sources
  • Gérer les dépendances

1 - Anatomie d'une ressource

Structure de base

resource "type" "name" {
argument1 = "value1"
argument2 = "value2"

nested_block {
nested_argument = "value"
}
}

Exemple concret

resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"

tags = {
Name = "WebServer"
}
}

# Accès aux attributs
output "instance_id" {
value = aws_instance.web.id
}

output "public_ip" {
value = aws_instance.web.public_ip
}

2 - Cycle de vie des ressources

Commandes du cycle de vie

États d'une ressource

ÉtatDescription
CreateNouvelle ressource à créer
UpdateRessource existante modifiée
DestroyRessource à supprimer
ReplaceDétruire puis recréer
No-opAucun changement

3 - Meta-arguments

count

resource "aws_instance" "web" {
count = 3

ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"

tags = {
Name = "web-${count.index}"
}
}

# Accès: aws_instance.web[0], aws_instance.web[1], etc.
output "instance_ids" {
value = aws_instance.web[*].id
}

for_each

resource "aws_instance" "servers" {
for_each = {
web = { type = "t2.micro", ami = "ami-web" }
api = { type = "t2.small", ami = "ami-api" }
db = { type = "t2.medium", ami = "ami-db" }
}

ami = each.value.ami
instance_type = each.value.type

tags = {
Name = each.key
}
}

# Accès: aws_instance.servers["web"], aws_instance.servers["api"]

depends_on

resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"

# Dépendance explicite
depends_on = [
aws_iam_role_policy.example
]
}

lifecycle

resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"

lifecycle {
# Créer le nouveau avant de détruire l'ancien
create_before_destroy = true

# Empêcher la destruction
prevent_destroy = true

# Ignorer certains changements
ignore_changes = [
tags,
user_data,
]

# Conditions de remplacement
replace_triggered_by = [
null_resource.trigger.id
]
}
}

provider

resource "aws_instance" "us_server" {
provider = aws.us_east

ami = "ami-87654321"
instance_type = "t2.micro"
}

4 - Ressources AWS courantes

EC2 Instance

resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
key_name = aws_key_pair.deployer.key_name
vpc_security_group_ids = [aws_security_group.web.id]
subnet_id = aws_subnet.public.id

root_block_device {
volume_size = 20
volume_type = "gp3"
encrypted = true
}

user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl start nginx
EOF

tags = {
Name = "WebServer"
}
}

S3 Bucket

resource "aws_s3_bucket" "data" {
bucket = "my-unique-bucket-name"
}

resource "aws_s3_bucket_versioning" "data" {
bucket = aws_s3_bucket.data.id

versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
bucket = aws_s3_bucket.data.id

rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

VPC

resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true

tags = {
Name = "main-vpc"
}
}

resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "eu-west-1a"
map_public_ip_on_launch = true

tags = {
Name = "public-subnet"
}
}

resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
}

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
}
}

Security Group

resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id

ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}

5 - Data Sources

Concept

Les data sources permettent de lire des données existantes.

Exemples

# Trouver la dernière AMI Ubuntu
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical

filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}

# Utilisation
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
# ...
}
# Lire les zones de disponibilité
data "aws_availability_zones" "available" {
state = "available"
}

# Utilisation
resource "aws_subnet" "private" {
count = length(data.aws_availability_zones.available.names)
availability_zone = data.aws_availability_zones.available.names[count.index]
# ...
}
# Lire le compte AWS actuel
data "aws_caller_identity" "current" {}

output "account_id" {
value = data.aws_caller_identity.current.account_id
}

6 - Dépendances

Dépendances implicites

# Terraform détecte automatiquement la dépendance
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id # Dépendance implicite
cidr_block = "10.0.1.0/24"
}

Dépendances explicites

resource "aws_iam_role" "example" {
name = "example-role"
# ...
}

resource "aws_iam_role_policy" "example" {
role = aws_iam_role.example.name
# ...
}

resource "aws_instance" "web" {
# ...

# Dépendance explicite nécessaire
depends_on = [
aws_iam_role_policy.example
]
}

7 - Provisioners

Avertissement

attention

Les provisioners sont considérés comme un dernier recours. Préférez les alternatives cloud-native (user_data, cloud-init).

Types de provisioners

resource "aws_instance" "web" {
ami = "ami-12345678"
instance_type = "t2.micro"

# Exécute sur la machine locale
provisioner "local-exec" {
command = "echo ${self.public_ip} >> inventory.txt"
}

# Exécute sur la ressource créée
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx"
]

connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}

# Copie des fichiers
provisioner "file" {
source = "config/"
destination = "/tmp/config"

connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
}

8 - Import de ressources existantes

Workflow

Commande import

# Syntaxe
terraform import <resource_type>.<name> <id>

# Exemples
terraform import aws_instance.web i-1234567890abcdef0
terraform import aws_s3_bucket.data my-bucket-name
terraform import aws_vpc.main vpc-12345678

Générer la configuration

# Terraform 1.5+
terraform plan -generate-config-out=generated.tf

9 - Moved blocks

Renommer des ressources

# Avant: resource "aws_instance" "web" {}
# Après: resource "aws_instance" "web_server" {}

moved {
from = aws_instance.web
to = aws_instance.web_server
}

Déplacer dans un module

moved {
from = aws_instance.web
to = module.compute.aws_instance.web
}

10 - Bonnes pratiques

Nommage

# ✅ Noms descriptifs
resource "aws_instance" "web_server_production" {}
resource "aws_security_group" "web_inbound" {}

# ❌ Noms génériques
resource "aws_instance" "instance1" {}
resource "aws_security_group" "sg" {}

Organisation

# ✅ Grouper les ressources liées
# networking.tf - VPC, subnets, routes
# compute.tf - EC2, ASG, ALB
# database.tf - RDS, ElastiCache
# security.tf - Security groups, IAM

Résumé

Points clés
  • Les ressources sont l'élément central de Terraform
  • count et for_each pour créer plusieurs instances
  • depends_on pour les dépendances explicites
  • lifecycle pour contrôler le comportement
  • Data sources pour lire sans créer

Exercices pratiques

  1. Créez une instance EC2 avec security group
  2. Utilisez for_each pour créer plusieurs subnets
  3. Importez une ressource existante
  4. Utilisez un data source pour trouver une AMI

← Providers | Variables et Outputs →