📖 Manuel
Infrastructure as Code
Critères de choix d'outil
| Critère | Terraform | Bicep | Pulumi |
|---|---|---|---|
| Multi-cloud | ✅ | ❌ Azure only | ✅ |
| Langage | HCL (DSL) | Bicep DSL | TS / Python / Go / .NET |
| Maturité état | Élevée | Élevée (Azure) | Moyenne |
| Courbe apprentissage | Faible | Très faible (Azure) | Nécessite dev skills |
| Meilleur pour | Tout cloud, équipes mixtes | Azure full-native, Azure Devs | Teams avec devs, logique complexe |
Règle : si Azure only → Bicep. Si multi-cloud ou équipes mixtes → Terraform. Si la logique dépasse les templates → Pulumi.
Workflow en étapes
1. Analyse du contexte
- Cloud provider(s) cibles, régions
- Services requis : compute, réseau, storage, DB, IAM
- Environnements : dev / staging / prod (séparés ?)
- Contraintes : conformité, coûts, équipe existante
2. Structure du projet
Terraform (recommandé)
infra/
├── modules/ # modules réutilisables (vnet, aks, sql…)
│ └── vnet/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ └── prod/
│ ├── main.tf
│ └── terraform.tfvars
└── .terraform-version # ex: 1.9.0
Bicep
infra/
├── modules/
│ └── storage.bicep
├── main.bicep
└── parameters/
├── dev.bicepparam
└── prod.bicepparam
3. Backend de state (Terraform)
# backend.tf — à créer AVANT terraform init
terraform {
backend "azurerm" {
resource_group_name = "rg-tfstate-prod"
storage_account_name = "sttfstateprod001"
container_name = "tfstate"
key = "myapp/prod.terraform.tfstate"
}
}
Commandes de bootstrap du backend :
az group create -n rg-tfstate-prod -l westeurope
az storage account create -n sttfstateprod001 -g rg-tfstate-prod --sku Standard_LRS
az storage container create -n tfstate --account-name sttfstateprod001
4. Variables et secrets
# variables.tf
variable "db_password" {
description = "Mot de passe de la base de données"
type = string
sensitive = true # masqué dans les logs
}
# Référence Key Vault (Azure)
data "azurerm_key_vault_secret" "db_password" {
name = "db-password"
key_vault_id = azurerm_key_vault.kv.id
}
Passer les secrets sans les coder en dur :
# Via variable d'environnement (CI/CD)
export TF_VAR_db_password="$(az keyvault secret show --name db-password --vault-name mykv --query value -o tsv)"
terraform apply
5. Cycle plan / apply
# Initialiser
terraform init
# Valider la syntaxe
terraform validate
# Voir les changements avant apply
terraform plan -var-file=environments/prod/terraform.tfvars -out=tfplan
# Appliquer (toujours à partir du plan généré)
terraform apply tfplan
# Vérifier le drift (détection manuelle)
terraform plan -detailed-exitcode # exit 2 = drift détecté
Bicep :
az deployment group what-if \
--resource-group rg-myapp-prod \
--template-file main.bicep \
--parameters parameters/prod.bicepparam
az deployment group create \
--resource-group rg-myapp-prod \
--template-file main.bicep \
--parameters parameters/prod.bicepparam
6. Import de ressources existantes
# Terraform : importer une ressource créée manuellement
terraform import azurerm_resource_group.main /subscriptions/<sub>/resourceGroups/rg-existing
# Générer la config depuis une ressource existante (Terraform 1.5+)
terraform plan -generate-config-out=generated.tf
7. CI/CD (GitHub Actions, Azure DevOps)
# .github/workflows/terraform.yml (extrait)
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.9.0"
- run: terraform init
- run: terraform plan -out=tfplan
- uses: actions/upload-artifact@v4
with:
name: tfplan
path: tfplan
apply:
needs: plan
environment: production # gate d'approbation manuelle
steps:
- uses: actions/download-artifact@v4
with: { name: tfplan }
- run: terraform apply tfplan
Bonnes pratiques 2026
- Versions épinglées : always pin provider et terraform version dans
required_providers - Tagging systématique :
environment,owner,cost-center,managed-by=terraformsur toutes les ressources - Outputs documentés : exposer les
outpututiles aux autres modules (IDs, endpoints) movedblocks : utilisermoved {}pour renommer des ressources sans destroy/recreate- Drift detection : planifier
terraform planen cron (daily) et alerter sur exit code 2 - Workspaces : éviter les workspaces Terraform pour les environnements — préférer des dossiers séparés (state isolation plus claire)
- Checkov / tfsec : scanner IaC avant merge (secrets, ports ouverts, chiffrement désactivé)
Anti-patterns et pièges
| Piège | Conséquence | Remède |
|---|---|---|
terraform apply sans plan préalable | Surprises en prod | Toujours -out=tfplan + apply depuis le plan |
Secrets dans .tfvars commités | Fuite de credentials | sensitive = true + .gitignore sur *.tfvars |
| State en local | Pas de collaboration, perte | Backend remote dès le début |
| Trop de ressources dans un seul state | plan lent, blast radius élevé | Découper par domaine (réseau / app / data) |
count pour les environnements | terraform state difficile à lire | for_each sur une map + workspaces séparés |
| Hardcoder la région | Pas réutilisable | Variable location systématique |
Ignorer les lifecycle | Recreations non voulues | lifecycle { prevent_destroy = true } sur les bases de données |
Bicep sans what-if | Changements destructifs silencieux | Toujours what-if avant deployment |