Guide technique

Infrastructure as Code avec 200 ressources : ce que les tutoriels Terraform ne te disent pas

Patterns IaC de production pour des systèmes réels. Gestion d'état à grande échelle, conception de modules, hybride CDK + Terraform, détection de drift, GitOps avec Flux.

20 avril 202616 min de lectureÉquipe d'Ingénierie Oronts

L'IaC, ce n'est pas "terraform init"

Tous les tutoriels Terraform commencent de la même façon : tu écris un fichier .tf, tu lances terraform init, tu lances terraform apply, et tu regardes ton instance EC2 apparaître. Ça te fait passer de zéro à une ressource. Ça ne te prépare pas à gérer 200+ ressources réparties sur 30 services AWS avec une équipe d'ingénieurs qui doivent tous pouvoir modifier l'infrastructure en toute sécurité.

Nous gérons l'infrastructure de plusieurs systèmes en production, des clusters Kubernetes avec Pimcore et OpenSearch aux architectures serverless avec Lambda, DynamoDB et API Gateway. Les patterns de cet article sont ceux qui ont survécu à la production. Pour savoir comment nous déployons les applications sur cette infrastructure, consulte notre page services cloud.

Gestion de l'état à grande échelle

L'état Terraform est le fichier le plus critique de ton infrastructure. Il fait le lien entre tes fichiers .tf et les ressources réelles. Si tu le perds, Terraform ne sait plus ce qui existe. Si tu le corrompus, Terraform risque de détruire des ressources en production.

État distant (non négociable)

# backend.tf
terraform {
    backend "s3" {
        bucket         = "company-terraform-state"
        key            = "prod/platform/terraform.tfstate"
        region         = "eu-central-1"
        encrypt        = true
        dynamodb_table = "terraform-locks"
    }
}
RèglePourquoi
État distant dans S3 (ou équivalent)Les fichiers d'état locaux se perdent, impossible de les partager
Chiffrement au reposL'état contient des secrets (mots de passe de bases de données, clés API)
Verrouillage DynamoDBEmpêche deux ingénieurs de lancer apply simultanément
Versioning sur le bucket S3Récupération en cas de corruption de l'état

Organisation de l'état

Un gros fichier d'état pour tout, c'est un cauchemar de maintenance. Découpe par environnement et domaine :

terraform/
├── environments/
│   ├── prod/
│   │   ├── platform/       # EKS, VPC, réseau
│   │   ├── databases/      # RDS, ElastiCache, OpenSearch
│   │   ├── compute/        # Lambda, ECS, Fargate
│   │   ├── storage/        # Buckets S3, CloudFront
│   │   └── monitoring/     # CloudWatch, alertes
│   ├── staging/
│   │   └── (même structure)
│   └── dev/
│       └── (même structure)
├── modules/
│   ├── vpc/
│   ├── eks-cluster/
│   ├── rds-postgres/
│   ├── opensearch/
│   ├── redis/
│   └── lambda-function/
└── global/
    ├── iam/                 # Rôles IAM, politiques
    ├── route53/             # Zones DNS
    └── ecr/                 # Registres de conteneurs

Chaque répertoire est un workspace Terraform séparé avec son propre fichier d'état. Les modifications du réseau ne risquent pas de casser la base de données. Les modifications du monitoring ne nécessitent pas un plan qui touche à toutes les ressources.

Références inter-états

Les workspaces doivent se référencer entre eux. Le workspace VPC exporte l'ID du VPC. Le workspace base de données le lit :

# Dans databases/main.tf
data "terraform_remote_state" "platform" {
    backend = "s3"
    config = {
        bucket = "company-terraform-state"
        key    = "prod/platform/terraform.tfstate"
        region = "eu-central-1"
    }
}

resource "aws_db_instance" "main" {
    vpc_security_group_ids = [data.terraform_remote_state.platform.outputs.db_security_group_id]
    db_subnet_group_name   = data.terraform_remote_state.platform.outputs.db_subnet_group_name
}

Conception de modules

Quand extraire un module

Pas chaque ressource n'a besoin d'un module. Extrais quand :

  • Le même pattern est utilisé dans 3+ endroits (DRY)
  • Le groupe de ressources a une frontière claire (VPC, cluster de base de données)
  • La configuration a des valeurs par défaut sensées qui réduisent la duplication

N'extrais pas quand :

  • C'est utilisé une seule fois (abstraction prématurée)
  • Le module aurait 20+ variables (trop de boutons à tourner)
  • L'abstraction cache des détails importants (réseau, sécurité)

Conception de l'interface du module

Un bon module a peu de variables requises, des valeurs par défaut sensées et des outputs clairs :

# modules/rds-postgres/variables.tf
variable "name" {
    description = "Nom de l'instance de base de données"
    type        = string
}

variable "vpc_id" {
    description = "VPC dans lequel déployer"
    type        = string
}

variable "subnet_ids" {
    description = "Sous-réseaux pour le groupe de sous-réseaux DB"
    type        = list(string)
}

variable "instance_class" {
    description = "Type d'instance RDS"
    type        = string
    default     = "db.t3.medium"
}

variable "engine_version" {
    description = "Version PostgreSQL"
    type        = string
    default     = "15.4"
}

variable "allocated_storage" {
    description = "Stockage en Go"
    type        = number
    default     = 50
}

variable "multi_az" {
    description = "Activer le déploiement multi-AZ"
    type        = bool
    default     = false  # true pour prod, false pour staging/dev
}

Le consommateur du module écrit :

module "database" {
    source     = "../../modules/rds-postgres"
    name       = "pimcore-prod"
    vpc_id     = module.vpc.vpc_id
    subnet_ids = module.vpc.private_subnet_ids
    multi_az   = true
}

Cinq lignes au lieu de cinquante. Le module gère les security groups, les parameter groups, les subnet groups, le chiffrement, la rétention des sauvegardes et le monitoring.

CDK + Terraform : l'approche hybride pragmatique

Certaines équipes misent tout sur CDK. D'autres tout sur Terraform. Nous utilisons les deux, et ça fonctionne.

Cas d'usageOutilPourquoi
Réseau, bases de données, clustersTerraformDéclaratif, plan-before-apply, gestion d'état
Fonctions Lambda + API GatewayCDKMeilleur bundling Lambda, constructs API Gateway
Politiques IAM complexesCDKLogique TypeScript pour les politiques conditionnelles
Ressources KubernetesKustomize + FluxGitOps, boucles de réconciliation
Infrastructure statiqueTerraformSimple, lisible, bien compris

La frontière est claire : Terraform gère l'infrastructure qui change rarement (VPC, RDS, cluster EKS). CDK gère l'infrastructure qui change avec les déploiements applicatifs (fonctions Lambda, routes API). Kustomize + Flux gère les workloads Kubernetes.

Ils coexistent grâce aux outputs. Terraform exporte l'ID du VPC, l'endpoint du cluster et la chaîne de connexion à la base de données. CDK les lit depuis SSM Parameter Store ou le remote state Terraform.

Le problème du drift

Le drift se produit quand quelqu'un modifie l'infrastructure via la console (ClickOps), via une commande CLI, ou via un autre outil. L'état réel diverge de l'état Terraform.

Détecter le drift

# Lancer le plan régulièrement (CI, job planifié)
terraform plan -detailed-exitcode

# Codes de sortie :
# 0 = pas de changements (l'état correspond à la réalité)
# 1 = erreur
# 2 = changements détectés (drift !)

Lance la détection de drift dans la CI sur un planning (quotidien pour la production, hebdomadaire pour le staging). Alerte quand un drift est détecté. Ne fais pas de remédiation automatique. Investigue d'abord.

Causes courantes du drift

CausePrévention
Changements via la console (ClickOps)Appliquer la politique "pas de changements console". Utiliser les SCPs pour restreindre.
Changements d'auto-scalingIgnorer les attributs d'auto-scaling dans Terraform (lifecycle { ignore_changes })
Mises à jour des services AWSFixer les versions des providers. Mettre à jour délibérément.
Terraform d'une autre équipeFichiers d'état séparés par équipe/domaine.
Hotfix manuel pendant un incidentDocumenter le changement. L'appliquer dans Terraform après l'incident.
# Ignorer les changements d'auto-scaling (drift attendu)
resource "aws_ecs_service" "app" {
    desired_count = 2

    lifecycle {
        ignore_changes = [desired_count]  # L'auto-scaling modifie cette valeur
    }
}

GitOps avec Flux

Pour les workloads Kubernetes, nous utilisons Flux pour le GitOps. La boucle de réconciliation remplace kubectl apply par un modèle pull-based.

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  Dépôt Git   │────▶│  Flux       │────▶│  Kubernetes  │
│  (manifests) │     │  Controller │     │  Cluster     │
└─────────────┘     └─────────────┘     └─────────────┘
                          │
                          │ Réconcilie toutes les 1 min
                          │ Détecte le drift
                          │ Applique automatiquement
# flux-system/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
    name: platform
    namespace: flux-system
spec:
    interval: 1m
    sourceRef:
        kind: GitRepository
        name: infrastructure
    path: ./kubernetes/resources/overlay/prod
    prune: true
    healthChecks:
        - apiVersion: apps/v1
          kind: Deployment
          name: pimcore
          namespace: production

Flux interroge le dépôt Git toutes les minutes. Si les manifests ont changé, il les applique. Si quelqu'un a modifié une ressource manuellement (drift), Flux la rétablit pour correspondre à Git. C'est une véritable réconciliation, pas juste de l'automatisation de déploiement.

Sealed Secrets

Les secrets ne peuvent pas aller dans Git en texte clair. Utilise Bitnami Sealed Secrets :

# Chiffrer le secret pour le cluster
kubeseal --cert sealed-secrets.pem \
    -f secrets/database-secrets.yaml \
    -o yaml > secrets/database-secrets-sealed.yaml

# Commiter la version scellée (sûr dans Git)
# Flux l'applique, le controller le déchiffre dans le cluster

Pour savoir comment nous gérons les secrets dans les déploiements Kubernetes de Pimcore spécifiquement, consulte notre guide de mise à niveau Pimcore qui couvre l'ordre complet de déploiement.

Gérer 30+ services AWS

À l'échelle enterprise, tu gères beaucoup de services. L'organisation fait la différence.

Catalogue de services

CatégorieServicesModule Terraform ?
RéseauVPC, sous-réseaux, NAT, ALB, Route53Oui (module vpc)
ComputeEKS, ECS Fargate, LambdaOui (par type de service)
Base de donnéesRDS PostgreSQL, DynamoDBOui (module rds-postgres)
CacheElastiCache RedisOui (module redis)
RechercheOpenSearchOui (module opensearch)
StockageS3, EFSInline (assez simple)
CDNCloudFrontInline
MessagerieSQS, MSK (Kafka), RabbitMQInline
AuthCognitoCDK (config complexe)
MonitoringCloudWatch, X-RayInline
CI/CDECR, CodeBuildInline
SécuritéIAM, KMS, Secrets ManagerWorkspace global

Stratégie de tagging

Chaque ressource doit être taguée pour l'allocation des coûts, l'identification du propriétaire et la gestion du cycle de vie :

locals {
    common_tags = {
        Environment = var.environment    # prod, staging, dev
        Project     = var.project_name   # pimcore, commerce, ai
        ManagedBy   = "terraform"
        Team        = var.team           # platform, backend, data
        CostCenter  = var.cost_center
    }
}

resource "aws_instance" "example" {
    tags = merge(local.common_tags, {
        Name = "pimcore-web-1"
        Role = "web"
    })
}

Filtre AWS Cost Explorer par le tag Project pour voir exactement combien coûte chaque système. Filtre par ManagedBy pour trouver les ressources créées manuellement (non taguées "terraform").

Pièges courants

  1. Un seul fichier d'état pour tout. Un terraform plan qui touche 200 ressources prend des minutes et une erreur affecte tout. Découpe par domaine et environnement.

  2. Pas de verrouillage d'état. Deux ingénieurs lancent terraform apply simultanément. Les changements de l'un sont perdus ou l'état est corrompu. Utilise le verrouillage DynamoDB.

  3. Des modules avec 20+ variables. Si l'interface de ton module est aussi complexe que les ressources brutes, l'abstraction n'apporte aucune valeur. Garde les interfaces de module réduites.

  4. Remédiation automatique du drift. Détecter le drift, c'est bien. Le corriger automatiquement, c'est dangereux. Le "drift" pourrait être un hotfix valide pendant un incident. Investigue avant de revenir en arrière.

  5. Des secrets dans les fichiers d'état. L'état Terraform contient chaque attribut de chaque ressource, y compris les mots de passe de bases de données. Chiffre l'état au repos et restreins les accès.

  6. Pas de pinning des versions des providers. Une mise à jour de provider change le comportement des ressources. Fixe les versions dans required_providers et mets à jour délibérément.

  7. ClickOps pour "juste ce truc-là". Les changements en console créent du drift invisible jusqu'au prochain terraform plan. Applique l'infrastructure-as-code pour tout.

  8. Pas de tagging. Sans tags, impossible d'attribuer les coûts, d'identifier les propriétaires ou de trouver les ressources créées manuellement.

Points clés à retenir

  • Découpe l'état par domaine et environnement. Réseau, bases de données, compute et monitoring devraient être des workspaces séparés. Les changements de l'un ne doivent pas risquer d'affecter l'autre.

  • Les modules sont pour les patterns, pas pour l'abstraction. Extrais quand le même groupe de ressources apparaît 3+ fois. Ne crée pas de modules pour les ressources ponctuelles.

  • CDK + Terraform, c'est pragmatique. Terraform pour l'infrastructure statique, CDK pour Lambda/API Gateway, Kustomize + Flux pour Kubernetes. Chaque outil là où il excelle.

  • La détection de drift est un job planifié. Lance terraform plan quotidiennement dans la CI. Alerte sur le drift. Investigue avant de remédier.

  • GitOps avec Flux offre une vraie réconciliation. Pas juste de l'automatisation de déploiement. Flux détecte et annule les changements manuels. Sealed Secrets gardent les identifiants en sécurité dans Git.

  • Tague tout. Environnement, projet, équipe, centre de coûts, managed-by. Sans tags, l'attribution des coûts et l'audit des ressources sont impossibles.

Nous gérons l'infrastructure pour les déploiements cloud, les plateformes logicielles sur mesure et les systèmes d'ingénierie de données. Si tu as besoin d'aide avec l'IaC à grande échelle, contacte notre équipe ou demande un devis.

Sujets couverts

Terraform productionleçons IaCinfrastructure as code réelTerraform AWSCDK vs Terraformgestion détat TerraformGitOps Fluxmodules Terraform

Prêt à construire des systèmes IA prêts pour la production ?

Notre équipe est spécialisée dans les systèmes IA prêts pour la production. Discutons de comment nous pouvons aider.

Démarrer une conversation