Automation
Terraform Azure : sécuriser le backend state privé sans casser la CI
Concevoir un backend Terraform Azure basé sur Storage Account privé avec identité CI, réseau contrôlé, verrouillage, bootstrap séparé et runbook de diagnostic quand init ou plan échoue.
Le backend state Terraform est une dépendance critique que l’on remarque surtout quand elle tombe. Tant que terraform init fonctionne, le sujet semble réglé. Le jour où un runner CI ne peut plus joindre le Storage Account, où le lock reste bloqué, ou où une règle réseau coupe l’accès à l’équipe, le state devient le point de passage obligé de toute l’infrastructure.
Le scénario traité ici est celui d’une plateforme Azure pilotée par Terraform, avec un backend azurerm stocké dans un Storage Account. L’objectif est de réduire l’exposition du state sans rendre les déploiements impossibles : accès par identité, réseau maîtrisé, bootstrap séparé, diagnostic clair et procédure de récupération.
Traiter le state comme une ressource d’exploitation
Le fichier state contient des identifiants de ressources, des dépendances et parfois des valeurs sensibles selon la manière dont les modules sont écrits. Il ne doit pas être traité comme un simple artefact technique. Il faut savoir qui peut le lire, qui peut le modifier, qui peut supprimer un lock, et depuis quels réseaux ces actions sont possibles.
Questions à trancher
Où est stocké le state ?
Quelle identité CI peut le lire et l'écrire ?
Quelles personnes peuvent intervenir en urgence ?
Depuis quels réseaux l'accès est autorisé ?
Comment supprimer un lock bloqué sans casser l'historique ?
Comment restaurer une version précédente si nécessaire ? Cette clarification évite deux extrêmes : un Storage Account ouvert trop largement pour que tout fonctionne, ou un backend tellement verrouillé que chaque changement réseau casse les déploiements.
Séparer bootstrap et infrastructure gérée
Le backend Terraform ne peut pas dépendre entièrement du code qui l’utilise. Si le Storage Account du state, son container, ses règles réseau et ses droits sont créés par le même state, une erreur peut rendre la pile difficile à récupérer. Il est préférable de séparer le bootstrap du backend et les stacks applicatives.
Bootstrap backend
Resource group du state
Storage Account
Container state
Private Endpoint si utilisé
Rôles minimaux pour l'identité CI
Procédure de récupération documentée
Stacks Terraform applicatives
Réseaux, services, identités, applications
Consomment le backend existant
Ne recréent pas le socle du state à chaque plan Le bootstrap peut rester très simple. L’important est qu’il soit compréhensible et récupérable. Une équipe doit pouvoir reconstruire ou vérifier le backend sans lancer toute l’infrastructure applicative.
Utiliser une identité CI dédiée
L’accès au state ne devrait pas reposer sur un secret partagé ou un compte personnel. Une identité dédiée à la CI, avec fédération OIDC quand c’est possible, rend les accès plus lisibles. Elle doit avoir les droits nécessaires sur le container de state, mais pas plus.
Identité CI Terraform
Lire le state
Ecrire le state
Gérer le lock
Lire les ressources nécessaires au plan
Appliquer uniquement dans les scopes prévus
A éviter
Compte administrateur global
Secret longue durée non renouvelé
Même identité pour toutes les plateformes
Droits Owner par défaut sans justification Cette séparation facilite aussi les revues. Quand un changement Terraform écrit dans le state, l’action doit être reliée à un pipeline et à une identité compréhensible, pas à une clé copiée dans plusieurs environnements.
Verrouiller le réseau sans oublier les runners
Passer le Storage Account en accès privé est souvent souhaitable, mais cela change le chemin de la CI. Si les runners sont hébergés hors du réseau privé, terraform init ne pourra plus atteindre le backend. Il faut donc décider où s’exécute Terraform : runner dans Azure, runner connecté au réseau privé, ou exception réseau strictement maîtrisée.
Option A : runner dans un réseau Azure autorisé
Accès au Storage Account via Private Endpoint
DNS privatelink validé depuis le runner
Modèle robuste pour environnements sensibles
Option B : runner externe avec accès public limité
Règles réseau strictes
Identité forte obligatoire
Plus simple, mais dépend d'adresses source stables
Option C : runner temporaire de maintenance
Utilisé pour récupération ou bootstrap
Accès court, tracé et désactivé après intervention Le point critique est le DNS. Avec un Private Endpoint, le runner doit résoudre le nom du Storage Account vers l’adresse privée attendue. Si ce n’est pas le cas, le problème apparaîtra comme une erreur Terraform alors qu’il s’agit d’une résolution ou d’un chemin réseau.
Rendre les erreurs Terraform lisibles
Un échec terraform init ou terraform plan peut venir du backend, de l’identité, du réseau, d’un lock ou du provider Azure. Le runbook doit aider à isoler la couche avant de modifier les droits ou les règles réseau.
Erreur : impossible de joindre le backend
Vérifier DNS du Storage Account depuis le runner
Vérifier route, firewall et Private Endpoint
Tester l'accès HTTPS au blob endpoint
Erreur : authorization failed
Vérifier rôle de l'identité CI sur le scope attendu
Vérifier fédération OIDC ou secret utilisé
Vérifier abonnement et tenant ciblés
Erreur : state lock
Identifier le pipeline ou l'utilisateur propriétaire du lock
Vérifier si un apply est encore en cours
Supprimer le lock seulement après validation Cette grille évite les corrections dangereuses, comme ajouter des droits globaux pour un problème DNS ou supprimer un lock alors qu’un apply est encore actif.
Prévoir le verrouillage et la récupération
Le lock protège contre les écritures concurrentes. Il ne doit pas être contourné par habitude. En revanche, il faut savoir quoi faire après une coupure de pipeline, un runner interrompu ou une erreur réseau au mauvais moment. La procédure de récupération doit être courte et stricte.
Procédure lock bloqué
1. Identifier le lock ID et l'heure de création
2. Vérifier le pipeline associé
3. Confirmer qu'aucun apply n'est en cours
4. Prévenir l'équipe concernée
5. Supprimer le lock avec la commande appropriée
6. Relancer un plan avant tout apply Le dernier point est important. Après une intervention sur le state, on ne repart pas directement sur un changement destructif. Un plan permet de vérifier que Terraform comprend encore l’état réel.
Garder des contrôles simples mais réguliers
Un backend state sécurisé peut dériver : rôle retiré, DNS privé cassé, runner déplacé, règle réseau modifiée, versioning désactivé, accès de maintenance oublié. Quelques contrôles périodiques suffisent à éviter les surprises.
Contrôles périodiques
terraform init depuis le runner principal
Résolution DNS du Storage Account depuis le runner
Rôles actifs sur le Storage Account et le container
Réseau public désactivé ou limité selon le modèle choisi
Versioning et protection de suppression vérifiés
Procédure de récupération testée par une autre personne Ces contrôles ne remplacent pas la supervision, mais ils donnent une preuve exploitable. Le backend n’est pas seulement présent : il reste atteignable par les bons chemins et fermé aux chemins non prévus.
Conclusion
Sécuriser un backend Terraform Azure ne consiste pas seulement à créer un Storage Account privé. Il faut penser au cycle complet : bootstrap, identité CI, chemin réseau, DNS, verrouillage, récupération et validation périodique. Le state est une ressource d’exploitation, pas un détail de pipeline.
La base saine consiste à isoler le socle du backend, limiter les droits de l’identité CI, choisir explicitement où les runners s’exécutent, puis documenter les diagnostics init, plan et lock. Avec cette discipline, le backend reste protégé sans devenir le point de blocage de chaque changement d’infrastructure.