Pourquoi versionner l'infrastructure code en multi-environnement
Versionner l'infrastructure code n'est pas un luxe : c'est le fondement de la reproductibilité et de la traçabilité dans une organisation qui opère sur AWS à grande échelle. Lorsque plusieurs équipes ou plusieurs projets partagent la même infrastructure cloud, sans versioning structuré, les divergences s'accumulent rapidement. L'environnement de production finit par dériver de celui de staging, qui lui-même n'a aucun lien avec le code source. Une urgence impose un changement manuel directement en console AWS, puis c'est au platform engineer de deviner ce qui s'est passé des mois après. Un déploiement rate, on ne sait pas si c'est une variable oubliée, une version incompatible d'un module ou une configuration locale. La traçabilité disparait, les incident post-mortems s'allongent. Versionner l'infrastructure code signifie donc coder chaque ressource AWS dans un outil comme Terraform, Git ou CloudFormation, puis garantir que chaque changement passe par une branche, une révision, une revue de code (même processus que le code métier), et une promotion progressive de dev vers prod. Cela impose un workflow DevOps discipliné : chaque version du code infrastructure doit être testable, reproductible sur un environnement neuf, et traçable jusqu'au commit Git et à l'auteur du changement. Sans cela, même avec Terraform, vous n'êtes que quelques mois devant une migration AWS chaotique, des rollbacks impossibles, ou des coûts en dérive constante. Le versioning multi-environnement n'est donc pas une optimisation pour plus tard, c'est un prérequis dès que l'infrastructure dépasse la dizaine de ressources et qu'elle intéresse plus d'une personne.
Structure Git et branches pour l'infrastructure code
La première étape pour versionner l'infrastructure code est de traiter Git comme le point de vérité unique. Cela signifie un dépôt dédié à l'IaC (souvent appelé 'infrastructure' ou 'terraform'), distinct du code applicatif, même si c'est tentant de les mélanger. Ce dépôt suit une stratégie de branchage claire : une branche main ou master représente la configuration en production, des branches de feature ou hotfix pour les changements, et des branches par environnement (ou simplement des dossiers Terraform) pour dev, staging et prod. L'approche la plus robuste est d'avoir un dossier par environnement dans le même dépôt ('environments/dev', 'environments/staging', 'environments/prod'), chacun contenant sa propre configuration Terraform avec ses variables (variables.tf) et ses fichiers d'état (state backend) séparés. Ainsi, un changement lancé sur dev ne risque jamais de toucher prod accidentellement. Chaque branche de feature suit un processus strict : un ingénieur crée une branche depuis main, y code les ressources ou modifications, envoie un pull request avec une description lisible ('Ajouter RDS Aurora en prod pour la nouvelle API'), et la revue s'engage sur le code, les risques, l'estimation des coûts AWS. Une fois approuvée, la branche fusionné sur main déclenche automatiquement un pipeline CI/CD qui valide la syntaxe Terraform, teste la cohérence (terraform plan), puis demande une approbation manuelle avant de déployer sur prod. Ce workflow garantit que personne ne déploie directement en console AWS, que chaque changement est écrit, revu et tracé dans Git. Les commit messages doivent être explicites ('Augmenter vcpu RDS prod pour répondre à charge Q4') pour que retrouver l'auteur et la raison d'une ressource deux ans plus tard soit possible. Les tags Git servent à marquer les versions majeures ('v1.2.0', correspondant à une release stable), ce qui permet de revenir facilement à une version antérieure si un rollback est nécessaire.
Validation et test de l'infrastructure code avant déploiement
Avant de déployer la moindre ressource en production, l'infrastructure code doit passer une suite de validations automatisées. Cette étape, souvent appelée 'infrastructure validation', est la première garde-fou du pipeline CI/CD. La validation de base comprend la syntaxe Terraform (terraform validate) qui s'exécute rapidement et rejette tout fichier mal formé. Ensuite, terraform fmt s'assure que la formatage est cohérent dans toute la codebase, ce qui facilite les revues de code. Mais ces vérifications ne suffisent pas. Un code Terraform syntaxiquement correct peut ne pas être logique : un groupe de sécurité AWS qui autorise en entrée le port 22 depuis 0.0.0.0 en production est un risque, même si Terraform le valide. C'est pourquoi des outils comme Checkov ou TFLint scannent le code IaC pour détecter les problèmes de sécurité et de conformité avant le déploiement. Un pipeline CI/CD robuste lance ces outils sur chaque commit et bloque une fusion pull request si des issues critiques sont détectées. Ensuite vient terraform plan, qui construit un plan d'exécution détaillé : liste des ressources qui seront créées, modifiées ou supprimées, estimé des coûts supplémentaires en AWS. Ce plan doit être revue humainement, particulièrement en production où une erreur a des conséquences financières et opérationnelles. Certaines organisations vont plus loin et exécutent des tests d'intégration minimalistes : après un terraform apply en environnement de test, ils vérifient que les ressources créées répondent aux critères attendus (ex: la RDS est accessible sur le bon port, le bucket S3 a les bonnes permissions). Ces tests s'écrivent en Terratest (framework Go) ou en scripts Python simples, et ils échouent fast si la configuration était incorrecte. Sans ces boucles de rétroaction courtes, un ingénieur ne le découvre que quand l'alerte de coût arrive ou quand un utilisateur signale une indisponibilité.
Gestion des variables et secrets multi-environnement
Une infrastructure Terraform qui déploie identiquement sur dev et prod n'existe pas vraiment : la taille des instances, les replicas RDS, les ACLs, les tags de coûts diffèrent. La clé est de paramétrer cette différence via les variables Terraform et les fichiers tfvars séparés par environnement. Chaque dossier environnement (dev, staging, prod) contient un fichier 'terraform.tfvars' (ou 'dev.tfvars', 'prod.tfvars') qui spécifie les valeurs : 'instance_type = t3.micro' pour dev et 't3.large' pour prod, 'replica_count = 1' pour dev et 3 pour prod. Ces fichiers sont versionnés dans Git car ils ne contiennent que des paramètres non sensibles. En revanche, les secrets (clés d'API externes, mots de passe RDS, certificats SSL) ne doivent JAMAIS être en Git. Ces secrets sont stockés dans AWS Secrets Manager ou AWS Systems Manager Parameter Store, puis injectés dans Terraform via des data sources ou via des variables d'environnement passées au moment du terraform apply. Un pipeline CI/CD peut récupérer ces secrets depuis un coffre sécurisé (Vault HashiCorp, AWS Secrets Manager) au moment de l'exécution et les injecter uniquement en mémoire, sans jamais les écrire sur disque. Certaines organisations adoptent une approche HCL stricte avec des workspaces Terraform ('terraform workspace select prod') pour isoler l'état par environnement, mais cette approche est moins flexible car elle ne permet pas aisément de différencier la configuration (variables) entre dev et prod. L'approche des dossiers séparés, avec un fichier .terraform-lock.hcl commun pour la reproductibilité des versions de providers, est plus recommandée. Cela garantit qu'un secret ne s'échappe jamais dans les logs de commit, qu'un changement de secret en production n'affecte pas dev, et que chaque environnement reste maître de sa configuration sensible.
Promotion progressive et approbation entre environnements
Déployer l'infrastructure code sur dev, valider, puis sur prod d'un clic est séduisant mais risqué. Une approche DevOps mature instaure des jalons explicites : une branche de feature, une fois approuvée, fusionne sur une branche 'develop' qui déploie automatiquement sur dev. L'infrastructure change en dev, elle est testée quelques heures ou jours par les applicatifs et les utilisateurs internes. Ensuite, la même code passe en revue supplémentaire, puis est promue sur 'staging' pour une batterie de tests intégration plus exhaustifs. Une fois validée en staging, une demande d'approbation explicite (dans la CI/CD) est envoyée aux administrateurs ou au lead DevOps, qui examinent à nouveau le terraform plan, estiment les coûts supplémentaires, vérifient qu'aucune dépendance critique n'est rompue. Seulement après approbation, le code fusionne sur main et se déploie en production. Cette progression dev -> staging -> prod instaure des délais (quelques heures, quelques jours selon la culture), mais elle a des bénéfices mesurables : les bugs d'infrastructure sont détectés sur dev ou staging plutôt qu'en prod où les utilisateurs en sont affectés, les coûts supplémentaires sont estimés et anticipés avant la facturation AWS, l'équipe engendre confiance en voyant les même changements tester sans casse. Certaines organisations, particulièrement les scale-ups, ajoutent une étape intermédiaire : un 'canary release' où le changement se déploie d'abord sur une fraction de la prod (2-3% du traffic) via une configuration blue/green, puis s'étend progressivement si aucune erreur n'est détectée. Cela réduit le risque de casse utilisateur tout en conservant la vélocité. Quelle que soit la stratégie, l'écrire explicitement dans la CI/CD (fichier .gitlab-ci.yml, GitHub Actions, Jenkins pipeline) et la documenter garantit que tous les ingénieurs suivent le même chemin et qu'un changement non suivi n'est pas possible par accident.
Tagging, documentation et traçabilité des versions
Une infrastructure qui scale a besoin de visibilité : qui a créé cette ressource, pour quel projet, à quelle date, avec quelle version du code ? Les tags AWS et la documentation du code IaC répondent à cela. Chaque ressource AWS créée par Terraform doit être taguée avec au minimum 'Environment' (dev/staging/prod), 'Project' (nom du projet), 'ManagedBy' (Terraform, pour éviter de confondre avec les ressources manuelles), et 'CostCenter' (qui paye?). Ces tags sont définis une fois dans les variables Terraform et appliqués à chaque ressource (via une local ou un default tags block), ce qui assure la cohérence. Côté code, chaque bloc Terraform doit avoir un commentaire explicite au-dessus, spécialement pour les décisions non évidentes. Pourquoi cette RDS est-elle en Multi-AZ? Quel volume de traffic justifie cette taille d'instance? La prochaine personne à lire le code (ou même toi après 6 mois) doit comprendre la raison sans deviner. Les versions sont marquées via des tags Git ('v1.5.0') et la branche main est toujours en production, ce qui signifie que n'importe quel commit sur main peut être relu en Git pour voir ce qui a changé à quelle date. Un changelog ou un fichier VERSIONS.md peut documenter les points clés de chaque release ('v1.5.0 : Ajout RDS Aurora en prod, mise à jour de Terraform de 1.2 à 1.4'). Certaines organisations approfondissent avec un audit trail : chaque terraform apply génère un log horodaté avec le user, le commit, le plan, et le résultat. Ces logs sont stockés dans AWS CloudTrail ou CloudWatch Logs pour une inspection ultérieure ou pour du compliance. Ce niveau de traçabilité prend quelques heures à mettre en place, mais il se paie dix fois en audits, incident response et en confiance des stakeholders. Sans traçabilité, c'est le 'qui a cassé la prod?' sans réponse; avec elle, c'est un apprentissage documenté.
Orchestration du versioning avec une CI/CD robuste
Le versioning de l'infrastructure code est une intention, mais c'est la CI/CD qui l'applique concrètement. Un pipeline GitLab CI, GitHub Actions ou Jenkins doit enchaîner les étapes : checkout du code, validation Terraform, scan Checkov, terraform plan en output, approbation manuelle, terraform apply. Pour que cela fonctionne sans friction, quelques prérequis : les credentials AWS doivent être injectés via des variables d'environnement sécurisées (jamais en dur dans le code), le state Terraform doit être stocké dans un backend distant (S3 avec verrous DynamoDB, ou Terraform Cloud) pour que plusieurs personnes travaillent en parallèle sans créer des conflits. Le backend doit avoir une stratégie de backup (versioning S3, copies quotidiennes), car perdre l'état Terraform est une catastrophe. Le fichier .terraform-lock.hcl doit être versioned dans Git pour garantir que tous les contributeurs utilisent les mêmes versions des providers Terraform (éviter 'terraform init' qui télécharge la dernière version et crée des divergences). Enfin, les pipelines doivent être idempotentes : un terraform apply lancé deux fois doit donner le même résultat que lancé une fois. Si ce n'est pas le cas, c'est un signe que l'IaC a une dépendance externe non maîtrisée (ex: un AMI qui change, une variable injectée aléatoirement). Tous ces éléments combinés font que versioning de l'infrastructure code n'est pas une question de git et Terraform, mais une culture DevOps où chaque changement est écrit, revu, testé et approuvé avant d'affecter la production. C'est cet ensemble cohérent qui transforme une infrastructure cloud chaotique en un asset maîtrisé et reproductible.