Construire un pipeline CI/CD sécurisée et optimisée pour déployer de l’IaC sur Azure

Introduction
L’importance croissante de l’automatisation et de la sécurité dans le déploiement de l’infrastructure
Les pratiques DevOps ont profondément transformé la manière dont les équipes IT conçoivent, déploient et maintiennent leurs infrastructures. Dans un contexte où les environnements cloud sont de plus en plus complexes, dynamiques et interconnectés, l’Infrastructure as Code (IaC) s’impose comme un levier clé pour répondre aux enjeux de scalabilité, de fiabilité et de rapidité d’exécution.
Mais automatiser ne suffit pas. Une pipeline CI/CD mal conçue peut introduire de nouveaux risques : erreurs de configuration, expositions de secrets, ou encore dérives entre l’état souhaité et l’état réel de l’infrastructure. C’est pourquoi la sécurisation, la traçabilité et la standardisation des chaînes CI/CD dédiées à l’IaC sont aujourd’hui incontournables pour garantir un déploiement maîtrisé et conforme aux bonnes pratiques de gouvernance cloud.
Face à ces défis, de nombreuses organisations – y compris les plus avancées – adoptent des architectures CI/CD modulaires, sécurisées et évolutives, s’appuyant sur :
- Terraform pour la gestion déclarative de l’infrastructure,
- Azure DevOps pour l’orchestration des workflows,
- Et Microsoft Azure comme environnement cible de déploiement.
Objectif de l’article
Cet article propose une démarche concrète et structurée pour concevoir et mettre en œuvre une pipeline CI/CD dédiée au déploiement d’IaC avec Terraform sur Azure, orchestrée via Azure DevOps.
Vous découvrirez comment :
- Comprendre les enjeux spécifiques d’une pipeline IaC
- Identifier les erreurs fréquentes et les risques d’une automatisation mal maîtrisée
- Définir une architecture robuste intégrant la sécurité, l’organisation des environnements et le contrôle qualité
- Construire pas à pas une pipeline CI/CD modulaire, réutilisable et bien documentée
- Adopter les bonnes pratiques de versioning, de gestion des secrets et de tests automatisés
- Évaluer les choix techniques autour d’Azure DevOps : agents d’exécution, Service Connections, templates YAML, etc
I- Comprendre les enjeux du CI/CD pour l’IaC
Problèmes fréquents : erreurs humaines, manque de validation, mauvaise organisation.
Déployer de l’IaC sans pipeline CI/CD revient à exécuter manuellement des scripts Terraform dans un terminal, souvent depuis des postes locaux. Cette pratique expose les projets à de nombreuses erreurs : oublis de paramètres, mauvaises configurations, fuites de secrets ou dérives entre l’infrastructure réelle et celle définie dans le code.
Une pipeline CI/CD bien conçue permet d’automatiser les étapes critiques du cycle de vie IaC (init, validate, plan, apply), tout en renforçant la sécurité et la traçabilité. Elle réduit les erreurs humaines, standardise les déploiements et permet d’appliquer systématiquement des contrôles qualité via des tests, des validations et des revues de code.
En parallèle, l’usage rigoureux d’un système de versioning comme Git est indispensable. Il permet de tracer chaque modification, de collaborer efficacement et d’assurer un alignement constant entre le code source et l’état des environnements. Sans cette rigueur, les projets s’exposent à des dérives : états désynchronisés, scripts modifiés localement sans historique clair, ou déploiements non audités.
C’est la combinaison d’un repository Git structuré et d’une pipeline CI/CD robuste qui constitue le socle d’une approche IaC maîtrisée et alignée avec les exigences de gouvernance cloud.
II-Préparer le projet IaC
La qualité d’une pipeline CI/CD repose d’abord sur une base solide : la structuration du projet Terraform. Trop souvent négligée, cette étape conditionne pourtant la maintenabilité, la sécurité et la collaboration à long terme.
Avant même d’écrire une ligne de YAML, il est essentiel de poser des fondations solides : organiser clairement le dépôt Git, anticiper la gestion multi-environnements, définir une stratégie de versioning, et centraliser les variables sensibles. Une organisation rigoureuse permet d’éviter bon nombre de pièges techniques ou organisationnels par la suite.
II.I- Organisation du dépôt Git : structure par environnement et modules
Dans une logique d’industrialisation, notamment sur Azure avec Terraform, une architecture modulaire et scalable s’impose.
Les bonnes pratiques consistent à :
- Séparer les modules Terraform dans un dépôt dédié, versionné, réutilisable sur plusieurs projets.
- Créer un dépôt par projet ou par environnement, dit « consommateur », qui appelle dynamiquement les modules via des tags (ref=vX.Y.Z).
Cette séparation permet de centraliser la maintenance des briques d’infrastructure (réseaux, bases de données, comptes de stockage, etc.), tout en laissant aux projets la liberté d’orchestrer leurs environnements selon leurs besoins spécifiques.
Structure globale du dépôt IaC consommateur
Voici la structure à adopter dans votre dépôt principal :

Fichier d’entrée : azure-pipelines.yml et templating YAML
Le fichier azure-pipelines.yml, situé à la racine du dépôt, constitue le point d’entrée de la pipeline CI/CD. Il référence un ou plusieurs templates YAML centralisés, généralement placés dans .azure/pipelines/, tels que stage.yml, où sont définis les jobs standards : init, validate, plan, apply.
Cette approche modulaire facilite la mutualisation des pipelines entre projets et permet de centraliser les mises à jour, tout en garantissant la cohérence des bonnes pratiques sur l’ensemble des environnements.
backend/ : gestion du backend Terraform
Le dossier backend/ contient les fichiers de configuration .conf propres à chaque environnement. Ces fichiers définissent l’emplacement et les paramètres du backend distant Azure utilisé pour stocker le terraform.tfstate.
Exemple de paramètres définis :
- resource_group_name : groupe de ressources Azure contenant le storage account
- storage_account_name : compte de stockage dédié au backend
- container_name : conteneur Blob où sera stocké l’état
- key : nom unique du fichier .tfstate par environnement
Cette organisation permet :
- Un verrouillage d’état fiable (state locking) évitant les conflits d’écriture
- Une isolation stricte des environnements
- Un suivi centralisé et sécurisé de l’état de l’infrastructure
vars/ : centralisation des variables d’environnement
Le dossier vars/ regroupe les fichiers .tfvars propres à chaque environnement (ex. dev.tfvars, prod.tfvars). Ces fichiers permettent d’injecter des valeurs spécifiques (noms, tailles, tags, etc.) tout en réutilisant une même base de modules.
Avantages :
- Reproductibilité des déploiements
- Clarté dans la gestion des paramètres
- Versioning centralisé des configurations
Fichiers racine : exécution Terraform
À la racine du dépôt, on retrouve les fichiers standards suivants :
- main.tf : appelle les modules distants
- variables.tf : déclare les variables attendues
- providers.tf : configure le provider Azure
- locals.tf : centralise les valeurs intermédiaires (tags, formats, concaténations, etc.)
Exemple d’appel d’un module distant dans main.tf :

L’utilisation de tags (ref=vX.Y.Z) permet de figer une version stable d’un module, garantissant traçabilité et compatibilité dans le temps.Cette architecture modulaire et templatisée offre plusieurs avantages :
Architecture modulaire : un modèle éprouvé
Cette organisation modulaire repose sur une séparation claire des responsabilités :
- Le dépôt template contient uniquement les modules Terraform génériques.
- Le dépôt projet orchestre les déploiements selon les environnements cibles.
Avantages :
- Industrialisation : mise à jour centralisée des modules
- Traçabilité : chaque version de module est identifiable
- Sécurité : séparation des responsabilités
- Scalabilité : ajout d’environnements ou de modules simplifié
II.II- Sécurisation des branches : stratégie Git, gestion des PR, approbation
Dans un projet IaC, une erreur peut avoir des conséquences critiques : ressources supprimées, configurations erronées ou fuites d’informations. La mise en place de branch policies strictes est donc incontournable.
Stratégie de branches recommandée
Une stratégie Git claire permet d’aligner sécurité, collaboration et contrôle qualité. Voici une structure éprouvée :
- main : branche protégée, contenant le code validé. Représente la source de vérité. Tout changement doit passer par une Pull Request (PR).
- develop : branche intermédiaire servant à valider les workflows sur l’environnement de qualification.
- Branches feature/ : utilisées pour introduire des modifications spécifiques, issues de develop.
La branche main doit être protégée par une policy interdisant tout push direct.
Politiques de validation
Les branch policies dans Azure DevOps permettent de :
- Lancer automatiquement la pipeline à l’ouverture de chaque PR
- Imposer une revue par un ou plusieurs membres de l’équipe
- Bloquer la fusion si la pipeline échoue ou si des critères qualité ne sont pas remplis
- Exiger un lien avec une User Story ou un ticket pour chaque PR
Cette démarche garantit la qualité du code, la traçabilité des décisions, et une gouvernance claire des modifications.
II.III- Organisation des groupes de variables
Plus l’infrastructure grandit, plus la gestion des variables devient critique. Pour garantir lisibilité, maintenabilité et cohérence, chaque environnement dispose de son propre fichier .tfvars, regroupé dans un dossier vars/.
Ce découpage permet de :
- Centraliser les paramètres spécifiques (noms, tailles, régions, tags, etc.)
- Réutiliser les mêmes modules avec des valeurs différentes
- Faciliter les relectures et les validations
Un simple appel avec le bon fichier .tfvars suffit à cibler un environnement spécifique sans modifier le code source.
Validation conditionnelle des variables
Pour fiabiliser les déploiements, il est essentiel d’intégrer des règles de validation dans les fichiers .tf. Cela permet de détecter rapidement les erreurs de saisie ou les valeurs non conformes dès le terraform plan.
Exemple :

Ce mécanisme évite des déploiements incorrects et renforce les standards internes, notamment dans les projets à grande échelle.
Variable Groups dans Azure DevOps : une approche complémentaire
En parallèle des fichiers .tfvars, les Variable Groups d’Azure DevOps permettent de gérer les paramètres et secrets directement depuis l’interface DevOps, sans modifier le code Git.
Avantages :
- Centralisation des variables utilisées dans plusieurs pipelines
- Séparation du code et de la configuration
- Intégration directe avec Azure Key Vault pour sécuriser les secrets
- Modification facile sans ouverture de PR
Exemple d’inclusion dans la pipeline YAML :

En une seule ligne, on charge toutes les variables nécessaires à un environnement donné.
Utilisation dans le code Terraform :
Une fois injectées dans la pipeline, ces variables peuvent être passées à Terraform en tant que -var lors du plan ou apply : terraform plan -var « vm_size=$(VM_SIZE) » -var « region=$(REGION) ». Dans le code Terraform, il suffit alors de déclarer : variable « vm_size » {} et variable « region » {}
Pourquoi combiner .tfvars + Variable Groups ?
Cette double approche permet de tirer le meilleur parti des deux mondes :

Cette organisation prépare également l’infrastructure à évoluer facilement : duplication vers d’autres environnements, équipes ou clients sans refactorisation.
III-Mettre en place les fondations Terraform
Une fois le dépôt structuré et les variables bien gérées, il est temps de poser les bases techniques du projet : backend distant, provider, verrouillage d’état.
Backend distant sécurisé
Le fichier terraform.tfstate est le cœur du projet Terraform : il contient l’état réel de l’infrastructure. Le stocker en local expose à des risques majeurs (corruption, suppression, perte de synchronisation).
La bonne pratique : configurer un backend distant dans Azure, basé sur un Storage Account.
Avantages clés :
- Collaboration fluide : état partagé entre tous les membres du projet
- Sécurité renforcée : contrôle d’accès via RBAC, endpoints privés, règles NSG
- Verrouillage automatique : évite les conflits lors d’exécutions concurrentes
- Versioning natif : possibilité de rollback en cas d’erreur
- Intégration CI/CD : état toujours synchronisé, que ce soit en local ou via pipeline
IV-Construction de la pipeline
IV.I-Définition d’une pipeline IaC efficace
Une pipeline Infrastructure as Code (IaC) efficace doit répondre à plusieurs impératifs :
- Automatiser les étapes clés du cycle Terraform : init, validate, plan, apply
- Renforcer la sécurité via l’authentification OIDC, la gestion centralisée des secrets et le contrôle des accès
- Gérer plusieurs environnements (développement, production, etc.) de manière isolée et cohérente
- Intégrer des validations manuelles pour sécuriser les déploiements sensibles
- Rester modulaire, lisible et facilement maintenable dans le temps
Dans notre approche, la pipeline s’articule autour des éléments suivants :
- Un fichier azure-pipelines.yml, agissant comme point d’entrée
- Un template stages.yml réutilisable pour chaque environnement
- Des groupes de variables partagés, facilitant la gestion des configurations
- Un Storage Account Azure utilisé comme backend distant pour stocker et verrouiller l’état (tfstate) de manière centralisée
IV.II- Étapes essentielles : init, validate, plan, apply
Nous décomposons chaque étape de Terraform dans notre pipeline. La première étape de notre pipeline sera l’initialisation :
- init
Initialise le projet et configure le backend distant :

- validate
Permet de détecter les erreurs de syntaxe Terraform avant de générer un plan :

- plan
Génère le plan Terraform (stocké en .tfplan) :

apply
Exécute le plan si validé :

IV.III- Séparer les jobs / stages (validation, plan, apply…)
Dans le fichier stage.yml, chaque étape clé du processus comme le plan ou l’apply est définie comme un stage distinct. Cette approche permet d’appliquer des politiques spécifiques à chaque étape (comme des approbations ou des délais d’attente), de mieux isoler et traiter les erreurs, et d’obtenir une pipeline plus modulaire, lisible et maintenable.
Extrait de stage.yml :

Et dans azure-pipelines.yml :

Ce fichier YAML déclenche automatiquement la pipeline lorsqu’un changement est détecté sur les branches main ou dev. Il s’appuie sur le template stages.yml pour structurer les étapes, et injecte dynamiquement la valeur de l’environnement (dev ou prd). Cela permet de charger les bons fichiers .tfvars et les fichiers de configuration du backend, assurant ainsi un déploiement cohérent selon le contexte.
Le fichier Yaml ressembleras à ça : Lien GitHub
IV.IV- Gestion des validations manuelles (approbations)
Avant d’exécuter un apply sur un environnement critique comme la production, il est essentiel d’ajouter une étape de validation manuelle. Cela peut être réalisé en configurant un environnement avec approbation dans Azure DevOps ou en ajoutant une tâche conditionnelle dédiée au sein de la pipeline. Cette précaution permet de garder un contrôle humain avant tout changement impactant. Voici comment l’on peut configurer cette étape :

Dans Azure DevOps, il suffit de configurer une approbation manuelle sur l’environnement GouvCloud-prod et le tour est jouer.
V- Sécurisation de la pipeline
Dans un contexte professionnel, sécuriser sa pipeline CI/CD est indispensable. Une mauvaise configuration peut exposer des identifiants, ouvrir des failles dans les infrastructures déployées, ou encore permettre des déploiements non autorisés. Cette section détaille les éléments clés pour sécuriser efficacement une pipeline Azure DevOps utilisée pour le déploiement Terraform sur Azure.
V.I- Méthodes d’authentification via les Service Connections Azure DevOps
Azure DevOps permet d’utiliser différentes méthodes d’authentification pour interagir avec Azure. Voici les trois principales :
- Service Principal classique (mot de passe / secret)
Cette méthode historique basée sur un ID d’application (clientId) et un secret (clientSecret) consiste à tout simplement créer un secret sur le Service et utilisé sa valeur afin d’avoir accès à des ressources. Son avantage c’est sa simplicité à mettre en place mais peu sécurisé. Et les inconvénients sont multiples :
- Les secrets expirent (par défaut après 1 an).
- Les secrets sont sensibles et doivent être stockés dans des variables sécurisées.
- Impossible à utiliser avec OIDC.
- Service Principal avec certificat
Ce service utilise un certificat plutôt qu’un secret. L’avantage c’est tout simplement une meilleure sécurité que les secrets (certificats stockés dans Azure Key Vault) tandis que l’Inconvénient est de taille car elle rajoute de la complexité en terme de gestion.
- Service Principal fédéré (OIDC)
Azure DevOps utilise un jeton OIDC émis dynamiquement pour s’authentifier auprès d’Azure. Pas besoin de stocker un secret ou un certificat. Le jeton est court-lived, ce qui limite les risques de compromission.
Avantages :
- Sécurité renforcée : aucun secret à stocker dans la pipeline.
- Gestion centralisée via Entra ID (anciennement Azure AD).
- Compatible avec les bonnes pratiques DevSecOps.
Cette approche est recommandée dans tous les contextes d’entreprise.
V.II- Permissions, scopes et accès restreints
Pour réduire les risques, il est essentiel de respecter le principe du moindre privilège (PoLP) lors de la configuration des permissions des Service Principals.
Bonnes pratiques :
- Créer un Service Principal par environnement (dev, preprod, prod).
- Limiter les rôles au strict nécessaire (Contributor, Reader, etc.).
- Éviter les rôles globaux tels que Owner.
- Restreindre les scopes d’accès au niveau du resource group plutôt qu’au niveau de l’abonnement.
- Documenter et auditer régulièrement les attributions de rôles (RBAC).
V.III- Sécurité des agents d’exécution
Dans une pipeline Azure DevOps, chaque job s’exécute sur un agent, une machine (virtuelle ou physique) chargée d’interpréter les étapes définies dans le YAML. On distingue deux types d’agents :
- Microsoft-hosted : machines éphémères provisionnées automatiquement par Azure.
- Self-hosted : agents que vous gérez vous-même, déployés sur votre propre infrastructure.
Pourquoi opter pour un agent self-hosted ?
Dans un cadre professionnel, notamment pour des environnements privés ou des ressources sensibles, les agents Microsoft-hosted atteignent vite leurs limites :
- Pas d’accès aux ressources internes (VNet, Key Vault privé, etc.).
- Pas de personnalisation possible (versions d’outils, configuration réseau).
- Temps d’attente variable (agents partagés entre plusieurs organisations).
Les agents self-hosted offrent une meilleure maîtrise de l’environnement d’exécution. Ils permettent de choisir librement les outils, les versions, et d’assurer une configuration adaptée, tout en garantissant un haut niveau de sécurité grâce à un contrôle total du réseau, des NSG et des accès. Ils permettent également une connectivité directe aux ressources internes sur Azure.
Pour les héberger efficacement, la meilleure pratique consiste à utiliser une VM Scale Set (VMSS), qui regroupe des machines virtuelles identiques capables de s’adapter automatiquement à la charge. Cette solution assure une scalabilité dynamique, un réseau privé sécurisé, un environnement standardisé via image ou script personnalisé (Terraform, Azure CLI, etc.), et une cohérence d’exécution entre les agents, limitant ainsi les erreurs.
Conclusion
Construire une pipeline CI/CD dédiée à l’Infrastructure as Code avec Terraform n’est pas une simple formalité. C’est un engagement pour la qualité, la sécurité et la pérennité d’un environnements cloud. Tout au long de cet article, nous avons posé les fondations d’un projet IaC bien structuré. Le but n’est pas de complexifier, mais d’industrialiser intelligemment, afin de rendre les déploiements répétables, auditables et maîtrisés.