Guide Terraform pour l'Infrastructure as Code, modules, state management, workspaces et bonnes pratiques. À utiliser quand l'utilisateur écrit du Terraform, conçoit des modules ou gère de l'infrastructure cloud. Se déclenche aussi avec "terraform", "infrastructure as code", "terraform plan", "terraform apply", "module terraform", "tfstate", "HCL".
Installs into .claude/skills of the current project.
Are you the author of Terraform Guide?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/whitemuush-terraform-guide)
---
name: terraform-guide
description: Guide Terraform pour l'Infrastructure as Code, modules, state management, workspaces et bonnes pratiques. À utiliser quand l'utilisateur écrit du Terraform, conçoit des modules ou gère de l'infrastructure cloud. Se déclenche aussi avec "terraform", "infrastructure as code", "terraform plan", "terraform apply", "module terraform", "tfstate", "HCL".
---
# Guide Terraform
## 1. Workflow opérationnel (étapes numérotées)
1. **Initialiser**, configurer le backend et télécharger les providers.
```bash
terraform init -upgrade # première fois ou mise à jour providers
terraform init -reconfigure # changer de backend sans migrer l'état
```
2. **Valider**, vérifier la syntaxe avant de planifier.
```bash
terraform validate
terraform fmt -recursive # formater tout le répertoire
```
3. **Planifier**, toujours sauvegarder le plan pour un apply déterministe.
```bash
terraform plan -out=tfplan.bin
terraform show -json tfplan.bin | jq '.resource_changes[] | select(.change.actions != ["no-op"])'
```
4. **Appliquer**, uniquement depuis le plan sauvegardé.
```bash
terraform apply tfplan.bin
```
5. **Vérifier le drift**, détecter les divergences entre state et réalité.
```bash
terraform plan -refresh-only # voir ce qui a changé hors Terraform
terraform apply -refresh-only # re-synchroniser le state sans modifier les ressources
```
6. **Détruire proprement**, cibler d'abord, jamais en masse sans review.
```bash
terraform destroy -target=module.networking.azurerm_subnet.main
```
---
## 2. Structure de projet recommandée
```
infrastructure/
├── environments/
│ ├── dev/
│ │ ├── main.tf # appels aux modules
│ │ ├── variables.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ ├── staging/
│ └── production/
├── modules/
│ ├── networking/ # un module = une responsabilité
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── database/
│ └── app-service/
└── shared/
└── versions.tf # contraintes de version providers
```
**Critère de découpage** : un module par "domaine fonctionnel" (réseau, stockage, compute). Éviter les modules à 1 ressource (sur-découpage) et les modules "tout en un" (couplage fort).
---
## 3. Module réutilisable, exemple complet
```hcl
# modules/app-service/variables.tf
variable "app_name" { type = string }
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "Valeurs acceptées : dev, staging, production."
}
}
variable "sku" { type = string; default = "B1" }
variable "location" { type = string }
variable "resource_group_name"{ type = string }
variable "app_settings" { type = map(string); default = {} }
# modules/app-service/main.tf
locals {
common_tags = {
Environment = var.environment
ManagedBy = "terraform"
Application = var.app_name
}
}
resource "azurerm_service_plan" "this" {
name = "plan-${var.app_name}-${var.environment}"
location = var.location
resource_group_name = var.resource_group_name
os_type = "Linux"
sku_name = var.sku
tags = local.common_tags
}
resource "azurerm_linux_web_app" "this" {
name = "app-${var.app_name}-${var.environment}"
location = var.location
resource_group_name = var.resource_group_name
service_plan_id = azurerm_service_plan.this.id
tags = local.common_tags
site_config {
always_on = var.environment == "production"
application_stack { dotnet_version = "8.0" }
}
app_settings = var.app_settings
}
# modules/app-service/outputs.tf
output "app_url" { value = "https://${azurerm_linux_web_app.this.default_hostname}" }
output "app_id" { value = azurerm_linux_web_app.this.id }
output "plan_id" { value = azurerm_service_plan.this.id }
```
Appel depuis un environnement :
```hcl
module "api" {
source = "../../modules/app-service"
app_name = "myapi"
environment = "production"
sku = "P1v3"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
```
---
## 4. State management
### Backend distant avec locking (Azure)
```hcl
# environments/production/backend.tf
terraform {
backend "azurerm" {
resource_group_name = "rg-terraform-state"
storage_account_name = "stterraformstprod"
container_name = "tfstate"
key = "myapp.production.tfstate"
use_azuread_auth = true # évite les clés de compte (2025+)
}
}
```
### Backend S3 + DynamoDB (AWS)
```hcl
terraform {
backend "s3" {
bucket = "myco-tfstate-prod"
key = "myapp/production/terraform.tfstate"
region = "eu-west-1"
encrypt = true
use_lockfile = true # verrouillage natif S3 (Terraform 1.10+)
}
}
```
> `dynamodb_table` est déprécié depuis Terraform 1.11 : le verrouillage passe désormais par un
> fichier de lock dans le bucket lui-même. Une table DynamoDB de moins à provisionner et à payer.
### Commandes de gestion du state
```bash
terraform state list # toutes les ressources
terraform state show azurerm_linux_web_app.this # détail d'une ressource
terraform state mv module.old.res module.new.res # renommer sans recréer
terraform state rm azurerm_resource_group.legacy # retirer du state sans détruire
terraform import azurerm_resource_group.legacy /subscriptions/.../rg-name
```
---
## 5. Versions et contraintes providers
```hcl
# shared/versions.tf, à copier dans chaque environnement
terraform {
required_version = ">= 1.7, < 2.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 5.1"
}
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
}
}
```
Verrouiller le `.terraform.lock.hcl` dans Git, il garantit la reproductibilité des builds.
> **Migration 3.x → 5.x** : azurerm a passé deux majeures depuis la 3. La 4.0 rend `subscription_id`
> obligatoire dans le bloc provider et remplace `skip_provider_registration` par
> `resource_provider_registrations`. Ne pas sauter ces deux marches sans lire les guides d'upgrade.
---
## 6. Garde-fous et pièges fréquents
| Piège | Symptôme | Remède |
|-------|----------|--------|
| State en local / commité dans Git | Conflits d'équipe, secrets exposés | Backend distant + `.gitignore` sur `*.tfstate*` |
| `terraform apply` sans plan sauvegardé | Apply incohérent si state a changé entre-temps | Toujours `-out=tfplan.bin` + `apply tfplan.bin` |
| Hard-coding de secrets dans HCL | Secrets dans Git | `var` + Key Vault / Secrets Manager ou `sensitive = true` |
| Modules trop fins (1 ressource) | Overhead de composition, appels imbriqués | Regrouper par domaine fonctionnel |
| `terraform destroy` sans `-target` | Destruction de toute l'infra | Toujours cibler ou utiliser des workspaces isolés |
| Drift ignoré | État réel diverge du plan | `plan -refresh-only` en CI hebdomadaire |
| Pas de `lifecycle.prevent_destroy` sur ressources critiques | Suppression accidentelle BDD/stockage | Ajouter `prevent_destroy = true` sur les ressources stateful |
```hcl
# Protéger une base de données critique
resource "azurerm_postgresql_flexible_server" "main" {
# ...
lifecycle {
prevent_destroy = true
}
}
```
---
## 7. Bonnes pratiques 2026
- **Épingler les versions** providers via `.terraform.lock.hcl` (commité dans Git).
- **Secrets** : ne jamais les mettre dans `.tfvars`; utiliser `sensitive = true` + injection CI (env vars `TF_VAR_*`).
- **Tagging systématique** : module `locals` avec `common_tags` hérité par toutes les ressources.
- **Outputs sensibles** : marquer `sensitive = true` pour éviter les logs en clair.
- **CI/CD** : `terraform plan` en PR (commentaire automatique), `terraform apply` uniquement sur merge main.
- **Workspaces** : réservés aux environnements éphémères (feature branches), ne pas les utiliser pour prod/staging (préférer des dossiers séparés avec leur propre state).
- **Tflint + Checkov** : linting et scan de sécurité avant le plan.
```bash
tflint --recursive
checkov -d . --framework terraform
```
---
## 8. Commandes de référence rapide
```bash
# Init & format
terraform init -upgrade && terraform fmt -recursive && terraform validate
# Plan sécurisé
terraform plan -var-file=environments/prod.tfvars -out=tfplan.bin
# Inspection du plan
terraform show tfplan.bin # lisible humain
terraform show -json tfplan.bin | jq '.' # JSON pour scripts
# State ops
terraform state list
terraform state show <resource_address>
terraform state mv <src> <dst>
terraform state rm <resource_address>
# Import ressource existante
terraform import <resource_address> <cloud_id>
# Drift
terraform plan -refresh-only
```