Introduction
L'Infrastructure as Code (IaC) révolutionne la gestion d'infrastructures en traitant les ressources cloud comme du code source : versionnable, testable et reproductible. En 2026, avec la complexité croissante des clouds hybrides, Terraform s'impose comme l'outil open-source leader grâce à son langage HCL déclaratif et sa compatibilité avec 2500+ providers (AWS, Azure, GCP).
Ce tutoriel intermédiaire vous guide pour déployer un bucket S3 sécurisé avec versioning et politique IAM sur AWS. Pourquoi c'est crucial ? Une infra manuelle génère des dérives (drift), des coûts imprévus et des downtimes. Avec IaC, vous appliquez des idempotences : terraform apply reconstruit exactement l'état désiré.
Analogie : comme Git pour le code, Terraform versionne votre infra via un state file chiffré (optionnel S3 backend). Résultat : CI/CD fluide, audits conformes GDPR/SOC2. Prêt à transformer vos déploiements en code ? (128 mots)
Prérequis
- Terraform CLI ≥ 1.9.0 installé (téléchargement)
- Compte AWS avec droits IAM (AdministratorAccess pour tests)
- AWS CLI configurée (
aws configureavec access key/secret) - Connaissances de base en HCL et AWS S3
- Git pour versionner le projet
- Éditeur comme VS Code avec extension HashiCorp Terraform
Initialiser le projet Terraform
mkdir terraform-iac-s3 && cd terraform-iac-s3
git init
echo '# IaC S3 Bucket Terraform' > README.md
terraform initCette commande crée un répertoire projet, l'initialise en Git pour le versionning, et exécute terraform init qui télécharge les providers et modules nécessaires. Piège : sans Git, pas de historique ; toujours exécuter dans un dossier vide pour éviter les conflits de state.
Configurer le provider AWS
Le provider définit comment Terraform interagit avec AWS. Il utilise vos credentials CLI par défaut, mais supporte variables d'env ou rôles IAM pour la sécurité. Créez main.tf avec version pinning pour reproductibilité.
Définir le provider et variables
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
}
required_version = ">= 1.5"
}
provider "aws" {
region = var.aws_region
}
variable "aws_region" {
description = "Région AWS"
type = string
default = "eu-west-1"
}
variable "bucket_name" {
description = "Nom unique du bucket S3"
type = string
}Ce fichier fixe la version Terraform/providers pour éviter les breaking changes, configure le provider avec une variable région, et prépare bucket_name. Utilisez ~> 5.40 pour minor updates sécurisés ; piège : noms buckets globaux uniques, préfixez avec mon-app-2026-.
Créer le bucket S3 de base
Définissez la ressource S3 avec versioning activé pour récupération données. Terraform gère l'idempotence : apply répété n'ajoute pas de duplicatas.
Ressource S3 bucket avec versioning
resource "aws_s3_bucket" "secure_bucket" {
bucket = var.bucket_name
tags = {
Name = "Secure IaC Bucket"
Environment = "prod"
ManagedBy = "Terraform"
}
}
resource "aws_s3_bucket_versioning" "secure_bucket_versioning" {
bucket = aws_s3_bucket.secure_bucket.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "secure_bucket_encryption" {
bucket = aws_s3_bucket.secure_bucket.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}Crée un bucket tagué, active versioning (rétention 128 versions) et chiffrement AES256 at-rest. Références comme aws_s3_bucket.secure_bucket.id créent des dépendances implicites. Piège : sans tags, traçabilité nulle ; toujours activer encryption pour conformité.
Ajouter une politique de sécurité
Bloquez l'accès public et limitez uploads à un rôle IAM spécifique. Les policies JSON sont parsées via aws_iam_policy_document.
Politique bucket et IAM role
data "aws_iam_policy_document" "secure_bucket_policy" {
statement {
sid = "DenyUnEncryptedObjectUploads"
effect = "Deny"
principals {
type = "*"
identifiers = ["*"]
}
actions = ["s3:PutObject*"]
resources = ["${aws_s3_bucket.secure_bucket.arn}/*"]
condition {
test = "StringNotEquals"
variable = "s3:x-amz-server-side-encryption"
values = ["AES256"]
}
}
}
resource "aws_s3_bucket_policy" "secure_bucket_policy" {
bucket = aws_s3_bucket.secure_bucket.id
policy = data.aws_iam_policy_document.secure_bucket_policy.json
}
resource "aws_iam_role" "s3_uploader" {
name = "s3-uploader-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "ec2.amazonaws.com" }
}]
})
}La data source génère une policy JSON denyant uploads non-chiffrés ; attachée au bucket. Crée un rôle IAM pour EC2 uploads. Piège : policies malformées causent 403 ; validez avec terraform plan et AWS Policy Simulator.
Définir les outputs et state remote
Outputs exposent des valeurs (ex: bucket ARN pour apps). Pour prod, stockez state en S3 + DynamoDB lock.
Outputs et backend S3
output "bucket_arn" {
description = "ARN du bucket S3"
value = aws_s3_bucket.secure_bucket.arn
}
output "bucket_name_output" {
description = "Nom du bucket"
value = aws_s3_bucket.secure_bucket.bucket
}
terraform {
backend "s3" {
bucket = "mon-terraform-state-2026"
key = "iac-s3/terraform.tfstate"
region = "eu-west-1"
dynamodb_table = "terraform-locks"
}
}Outputs pour intégration CI/CD ; backend S3 collabore multi-équipes avec locks DynamoDB (créez-les avant). Piège : state local perdu en delete dossier ; migrez vers remote avec terraform init -migrate-state.
Planifier et appliquer l'infrastructure
terraform validate
tfenv install 1.9.2
tfenv use 1.9.2
terraform fmt -recursive
terraform plan -var="bucket_name=mon-iac-s3-bucket-$(date +%s)-unique"
terraform apply -auto-approve -var="bucket_name=mon-iac-s3-bucket-$(date +%s)-unique"
terraform output -jsonvalidate/fmt/plan vérifient syntaxe/drift ; apply déploie. Utilisez timestamp pour unicité bucket. Piège : sans -var, default échoue si non-unique ; output -json pour pipelines.
Nettoyage avec destroy
terraform plan -destroy
terraform destroy -auto-approve
terraform state rm aws_s3_bucket.secure_bucket # Optionnel pour cleanup sélectifdestroy supprime tout idempotemment ; state rm pour ressources orphelines. Piège : oublie destroy = coûts AWS ; toujours plan -destroy preview.
Bonnes pratiques
- Modularisez : Séparez en modules réutilisables (
module "vpc" { source = "./modules/vpc" }). - State sécurisé : Toujours backend S3 + OIDC pour GitHub Actions.
- Variables & secrets :
terraform.tfvarsignoré Git ; utilisez Vault/Terraform Cloud. - Tests : Intégrez
terratestoucheckoven CI pour scans IaC. - Versions pinning :
~>providers,tfenvpour lock Terraform version.
Erreurs courantes à éviter
- Bucket name non-unique : Ajoutez UUID/timestamp ; erreur
BucketAlreadyExists. - State drift : Exécutez
terraform refreshpost-changes manuels. - Provider version drift : Sans pinning,
initupgrade casse ; utilisezterraform providers lock. - Cycles de dépendances : Évitez refs circulaires avec
depends_onexplicite.
Pour aller plus loin
- Docs officielles : Terraform AWS Provider
- Outils avancés : Terragrunt pour DRY, Atlantis pour PR reviews.
- CI/CD : Intégrez GitHub Actions avec OIDC.
- Découvrez nos formations Learni DevOps pour maîtriser Kubernetes as Code et advanced IaC.