<< All versions

Skill v1.0.0

currentAutomated scan100/100
whitemuush/your-claude-devops/terraform-guide
──Details
PublishedSeptember 27, 2026 at 09:50 PM
Content Hashsha256:9c21ec04f44ff12e...
Git SHA
──Files
Files (1 file, 9.1 KB)
SKILL.md9.1 KBactive
SKILL.md · 274 lines · 9.1 KB

version: "1.0.0" 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 ``

  1. Valider, vérifier la syntaxe avant de planifier.

``bash terraform validate terraform fmt -recursive # formater tout le répertoire ``

  1. 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"])' ``

  1. Appliquer, uniquement depuis le plan sauvegardé.

``bash terraform apply tfplan.bin ``

  1. 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 ``

  1. 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ègeSymptômeRemède
State en local / commité dans GitConflits d'équipe, secrets exposésBackend distant + .gitignore sur *.tfstate*
terraform apply sans plan sauvegardéApply incohérent si state a changé entre-tempsToujours -out=tfplan.bin + apply tfplan.bin
Hard-coding de secrets dans HCLSecrets dans Gitvar + Key Vault / Secrets Manager ou sensitive = true
Modules trop fins (1 ressource)Overhead de composition, appels imbriquésRegrouper par domaine fonctionnel
terraform destroy sans -targetDestruction de toute l'infraToujours cibler ou utiliser des workspaces isolés
Drift ignoréÉtat réel diverge du planplan -refresh-only en CI hebdomadaire
Pas de lifecycle.prevent_destroy sur ressources critiquesSuppression accidentelle BDD/stockageAjouter 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
All versions