Infrastructure as Code

Terraform

Cheatsheet Terraform — workflow, ressources, modules, state et bonnes pratiques

terraformiachclinfrastructureprovisioning

Workflow de base

terraform init → terraform plan → terraform apply → terraform destroy
$terraform init
Initialise le backend, télécharge les providers et modules
$terraform plan
Affiche les changements prévus sans les appliquer
$terraform apply
Applique les changements (demande confirmation)
$terraform apply -auto-approve
Applique sans demander confirmation (CI/CD)
$terraform destroy
Supprime toutes les ressources gérées

Structure de projet

infra/
├── main.tf           # Ressources principales
├── variables.tf      # Déclaration des variables
├── outputs.tf        # Valeurs de sortie
├── providers.tf      # Configuration des providers
├── terraform.tfvars  # Valeurs des variables (non commit)
├── backend.tf        # Configuration du state backend
└── modules/
    ├── vpc/
    ├── eks/
    └── rds/

Ressources

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
 
  tags = {
    Name        = "web-server"
    Environment = "production"
  }
}

Variables

# variables.tf
variable "region" {
  description = "AWS region"
  type        = string
  default     = "eu-west-3"
}
 
variable "instance_count" {
  description = "Number of instances"
  type        = number
  default     = 2
}
 
variable "allowed_cidrs" {
  description = "Allowed CIDR blocks"
  type        = list(string)
  default     = ["10.0.0.0/8"]
}
 
variable "tags" {
  description = "Resource tags"
  type        = map(string)
  default     = {}
}
# terraform.tfvars
region         = "eu-west-3"
instance_count = 3
allowed_cidrs  = ["10.0.0.0/8", "172.16.0.0/12"]

Outputs

output "instance_ip" {
  description = "Public IP of the web server"
  value       = aws_instance.web.public_ip
}
 
output "db_endpoint" {
  description = "RDS endpoint"
  value       = aws_db_instance.main.endpoint
  sensitive   = true
}
$terraform output
$terraform output -json

State

$terraform state list
Liste toutes les ressources dans le state
$terraform state show aws_instance.web
Details d'une ressource
$terraform state mv aws_instance.web aws_instance.api
Renommer une ressource dans le state
$terraform import aws_instance.web i-1234567890abcdef0
Importer une ressource existante

Backend S3 (recommandé)

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "eu-west-3"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

Modules

module "vpc" {
  source = "./modules/vpc"
 
  cidr_block   = "10.0.0.0/16"
  environment  = "production"
  project_name = "mon-app"
}
 
# Utiliser un module du registry
module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"
 
  cluster_name    = "mon-cluster"
  cluster_version = "1.31"
}

Expressions utiles

# Boucle for_each
resource "aws_subnet" "private" {
  for_each = toset(["a", "b", "c"])
 
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(aws_vpc.main.cidr_block, 8, index(["a", "b", "c"], each.key))
  availability_zone = "${var.region}${each.key}"
}
 
# Condition
resource "aws_eip" "nat" {
  count = var.enable_nat_gateway ? 1 : 0
}
 
# Data source
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]
 
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
  }
}

Bonnes pratiques

  • Remote state : S3 + DynamoDB pour le locking
  • Modules : réutiliser et versionner l'infrastructure
  • Workspaces : séparer dev/staging/prod
  • Plan avant apply : toujours reviewer le plan
  • Versionner les providers : pinning avec ~> ou =
  • tfvars secrets : ne jamais commiter, utiliser des variables d'environnement ou un vault