
Infrastructure as Code avec Terraform en 2026 : le guide complet pour automatiser votre cloud
Introduction
En 2026, l'Infrastructure as Code (IaC) n'est plus une option — c'est le socle de toute stratégie cloud sérieuse. Parmi les outils disponibles, Terraform de HashiCorp reste le leader incontesté grâce à son approche déclarative, son écosystème de providers et sa capacité à gérer des environnements multi-cloud complexes.
Que vous gériez une startup sur AWS ou une infrastructure hybride multi-cloud pour une grande entreprise, ce guide vous accompagne de la prise en main à l'expertise avancée.
Qu'est-ce que l'Infrastructure as Code ?
L'IaC consiste à décrire votre infrastructure sous forme de fichiers de configuration versionés, plutôt que de la configurer manuellement via des interfaces graphiques.
Les avantages fondamentaux
- Reproductibilité : un même code produit le même environnement à chaque exécution
- Versioning : historique complet des changements via Git
- Collaboration : revue de code sur l'infrastructure comme sur le logiciel
- Automatisation : déploiements intégrés aux pipelines CI/CD
- Documentation vivante : le code EST la documentation de l'infrastructure
- Disaster Recovery : reconstruction rapide à partir du code source
IaC déclaratif vs impératif
| Approche | Exemple | Principe |
|---|---|---|
| Déclaratif | Terraform, CloudFormation | Décrire l'état désiré |
| Impératif | Scripts Bash, Pulumi | Décrire les étapes à suivre |
Terraform en 2026 : quoi de neuf ?
Terraform Stacks (GA)
Les Stacks permettent de coordonner plusieurs configurations Terraform comme une unité logique :
```hcl # stack.tfstack.hcl component "networking" { source = "./modules/networking" inputs = { environment = var.environment cidr_block = "10.0.0.0/16" } }
component "kubernetes" { source = "./modules/eks-cluster" inputs = { vpc_id = component.networking.vpc_id subnet_ids = component.networking.private_subnet_ids } }
component "monitoring" { source = "./modules/observability" inputs = { cluster_endpoint = component.kubernetes.endpoint } } ```
Les Stacks gèrent automatiquement les dépendances entre composants et permettent des déploiements orchestrés.
Provider Ephemeral Resources
Terraform 1.10+ introduit les ressources éphémères pour les secrets et données temporaires :
```hcl ephemeral "aws_secretsmanager_secret_version" "db_password" { secret_id = aws_secretsmanager_secret.db.id }
resource "aws_db_instance" "main" { engine = "postgresql" password = ephemeral.aws_secretsmanager_secret_version.db_password.secret_string } ```
Le mot de passe n'est jamais stocké dans le state, réduisant considérablement la surface d'attaque.
Terraform Cloud avec HCP
HashiCorp Cloud Platform offre désormais :- Drift Detection automatique : alerte quand l'infrastructure réelle diverge du code
- Policy as Code avec Sentinel et OPA intégrés
- Cost Estimation avant chaque apply
- Private Registry pour vos modules internes
Prise en main : votre première infrastructure
Structure de projet recommandée
``` infrastructure/ ├── environments/ │ ├── dev/ │ │ ├── main.tf │ │ ├── variables.tf │ │ └── terraform.tfvars │ ├── staging/ │ └── production/ ├── modules/ │ ├── networking/ │ ├── compute/ │ ├── database/ │ └── monitoring/ ├── stacks/ │ └── full-environment/ └── tests/ ├── unit/ └── integration/ ```
Exemple concret : infrastructure web sur AWS
```hcl # environments/production/main.tf
terraform { required_version = ">= 1.10"
required_providers { aws = { source = "hashicorp/aws" version = "~> 5.80" } }
backend "s3" { bucket = "mycompany-terraform-state" key = "production/terraform.tfstate" region = "eu-west-3" encrypt = true dynamodb_table = "terraform-locks" } }
provider "aws" { region = var.aws_region
default_tags { tags = { Environment = var.environment ManagedBy = "terraform" Project = var.project_name } } }
module "networking" { source = "../../modules/networking"
environment = var.environment vpc_cidr = "10.0.0.0/16" azs = ["eu-west-3a", "eu-west-3b", "eu-west-3c"] public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] private_subnets = ["10.0.10.0/24", "10.0.11.0/24", "10.0.12.0/24"] }
module "compute" { source = "../../modules/compute"
environment = var.environment vpc_id = module.networking.vpc_id subnet_ids = module.networking.private_subnet_ids instance_type = "t3.large" min_size = 2 max_size = 10 }
module "database" { source = "../../modules/database"
environment = var.environment vpc_id = module.networking.vpc_id subnet_ids = module.networking.database_subnet_ids instance_class = "db.r6g.xlarge" multi_az = true backup_retention = 30 } ```
Modules avancés : écrire du Terraform réutilisable
Anatomie d'un module production-ready
```hcl # modules/networking/main.tf
resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_hostnames = true enable_dns_support = true
tags = { Name = "${var.environment}-vpc" } }
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.azs[count.index]
map_public_ip_on_launch = true
tags = { Name = "${var.environment}-public-${count.index + 1}" Tier = "public" } }
resource "aws_nat_gateway" "main" { count = var.enable_nat ? length(var.azs) : 0 allocation_id = aws_eip.nat[count.index].id subnet_id = aws_subnet.public[count.index].id
tags = { Name = "${var.environment}-nat-${count.index + 1}" } } ```
Variables avec validation
```hcl # modules/networking/variables.tf
variable "vpc_cidr" { description = "CIDR block pour le VPC" type = string
validation { condition = can(cidrhost(var.vpc_cidr, 0)) error_message = "Le vpc_cidr doit être un bloc CIDR valide." } }
variable "environment" { description = "Nom de l'environnement" type = string
validation { condition = contains(["dev", "staging", "production"], var.environment) error_message = "L'environnement doit être dev, staging ou production." } } ```
Outputs structurés
```hcl # modules/networking/outputs.tf
output "vpc_id" { description = "ID du VPC créé" value = aws_vpc.main.id }
output "private_subnet_ids" { description = "IDs des sous-réseaux privés" value = aws_subnet.private[].id }
output "summary" { description = "Résumé de l'infrastructure réseau" value = { vpc_id = aws_vpc.main.id vpc_cidr = aws_vpc.main.cidr_block public_subnets = aws_subnet.public[].id private_subnets = aws_subnet.private[].id nat_gateway_ips = aws_eip.nat[].public_ip } } ```
Multi-cloud avec Terraform
Gérer AWS et Azure dans le même projet
```hcl # multi-cloud/main.tf
provider "aws" { region = "eu-west-3" alias = "paris" }
provider "azurerm" { features {} alias = "westeurope" }
# Application principale sur AWS module "aws_app" { source = "./modules/aws-app" providers = { aws = aws.paris } environment = var.environment }
# Backup et DR sur Azure module "azure_dr" { source = "./modules/azure-dr" providers = { azurerm = azurerm.westeurope } primary_db_endpoint = module.aws_app.db_endpoint } ```
Cette approche vous protège du vendor lock-in et permet des stratégies de disaster recovery multi-cloud.
Tests automatisés pour l'infrastructure
Tests unitaires avec Terraform Test (natif)
```hcl # tests/unit/networking.tftest.hcl
run "vpc_creation" { command = plan
variables { environment = "test" vpc_cidr = "10.99.0.0/16" azs = ["eu-west-3a"] public_subnets = ["10.99.1.0/24"] }
assert { condition = aws_vpc.main.cidr_block == "10.99.0.0/16" error_message = "Le CIDR du VPC ne correspond pas" }
assert { condition = aws_vpc.main.enable_dns_hostnames == true error_message = "Le DNS hostnames devrait être activé" } } ```
Tests d'intégration
```hcl run "full_stack_deployment" { command = apply
variables { environment = "test" vpc_cidr = "10.99.0.0/16" }
assert { condition = output.vpc_id != "" error_message = "Le VPC ID ne devrait pas être vide" }
assert { condition = length(output.private_subnet_ids) > 0 error_message = "Au moins un sous-réseau privé doit exister" } } ```
Intégration CI/CD
```yaml # .github/workflows/terraform.yml name: Terraform CI/CD
on: pull_request: paths: ['infrastructure/**'] push: branches: [main]
jobs: validate: runs-on: ubuntu-latest steps: - uses: hashicorp/setup-terraform@v3 - run: terraform fmt -check -recursive - run: terraform validate - run: terraform test
plan: needs: validate runs-on: ubuntu-latest steps: - run: terraform plan -out=tfplan - uses: actions/upload-artifact@v4 with: name: tfplan path: tfplan
apply: needs: plan if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: production steps: - run: terraform apply -auto-approve tfplan ```
Sécurité et bonnes pratiques
Gestion du State
Le state Terraform contient des données sensibles. Protégez-le :
```hcl terraform { backend "s3" { bucket = "terraform-state-prod" key = "infra/terraform.tfstate" region = "eu-west-3" encrypt = true kms_key_id = "arn:aws:kms:eu-west-3:123456:key/abc-123" dynamodb_table = "terraform-locks" } } ```
Règles essentielles :- ✅ Chiffrement du state au repos (KMS/SSE)
- ✅ Verrouillage avec DynamoDB pour éviter les conflits
- ✅ Versioning du bucket S3 pour rollback
- ❌ Jamais de state en local pour la production
- ❌ Jamais de secrets en dur dans les fichiers .tf
Policy as Code avec OPA
```rego # policies/cost-control.rego package terraform.policies
deny[msg] { resource := input.planned_values.root_module.resources[_] resource.type == "aws_instance" not resource.values.instance_type in allowed_instance_types
msg := sprintf( "Instance type %s non autorisé. Utilisez: %v", [resource.values.instance_type, allowed_instance_types] ) }
allowed_instance_types := [ "t3.micro", "t3.small", "t3.medium", "t3.large" ] ```
Checklist sécurité Terraform
| Pratique | Priorité | Statut |
|---|---|---|
| State chiffré et distant | Critique | ✅ |
| Variables sensibles marquées `sensitive` | Critique | ✅ |
| Modules versionnés avec tags | Haute | ✅ |
| Tests automatisés en CI | Haute | ✅ |
| Policy as Code (OPA/Sentinel) | Moyenne | ✅ |
| Drift Detection activé | Moyenne | ✅ |
| Revue de code sur les plans | Haute | ✅ |
Terraform vs la concurrence en 2026
| Critère | Terraform | Pulumi | OpenTofu | CloudFormation |
|---|---|---|---|---|
| Langage | HCL | Python, Go, TS | HCL | JSON/YAML |
| Multi-cloud | ✅ Natif | ✅ Natif | ✅ Natif | ❌ AWS only |
| Communauté | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| Écosystème | 4000+ providers | 150+ providers | 4000+ providers | AWS natif |
| Licence | BSL 1.1 | Apache 2.0 | MPL 2.0 | Propriétaire |
| Tests natifs | ✅ tftest | ✅ Unit tests | ✅ tftest | ❌ |
| Courbe d'apprentissage | Moyenne | Faible (si dev) | Moyenne | Faible (si AWS) |
Cas d'usage concret : déployer un cluster Kubernetes
```hcl module "eks" { source = "terraform-aws-modules/eks/aws" version = "~> 20.0"
cluster_name = "${var.environment}-cluster" cluster_version = "1.31"
vpc_id = module.networking.vpc_id subnet_ids = module.networking.private_subnet_ids
cluster_endpoint_public_access = false
eks_managed_node_groups = { general = { instance_types = ["m6i.xlarge"] min_size = 2 max_size = 8 desired_size = 3
labels = { role = "general" } }
spot = { instance_types = ["m6i.xlarge", "m5.xlarge", "m5a.xlarge"] capacity_type = "SPOT" min_size = 0 max_size = 10 desired_size = 2
labels = { role = "spot-workloads" } } }
tags = { Environment = var.environment ManagedBy = "terraform" } } ```
Conclusion
Terraform en 2026 est un outil mature, puissant et incontournable pour toute équipe DevOps ou SRE. Avec les Stacks, les ressources éphémères et les tests natifs, il n'a jamais été aussi complet.
Les clés du succès avec Terraform :
- Modularisez : des modules réutilisables pour chaque composant
- Testez : tests unitaires et d'intégration automatisés
- Sécurisez : state chiffré, Policy as Code, secrets gérés
- Collaborez : revue de code, CI/CD, documentation dans le code
- Itérez : commencez simple, complexifiez progressivement
Envie d'automatiser votre infrastructure ? Utilisez notre assistant IT intelligent pour poser vos questions sur Terraform, AWS, Azure ou toute autre technologie cloud.