Bibliothèque de playbooks
Terraform et infrastructure as code
La revue Terraform connaît une asymétrie qu'aucun code applicatif ne connaît : rater une ligne coûte un volume détruit ou une base de données ouverte sur Internet. Le HCL généré est fluide et plausible, et ses parties dangereuses — un backend absent, un module non épinglé, un CIDR d'ingress ouvert — ressemblent exactement à ses parties sûres.
- Écrit pour
- Les équipes plateforme et infrastructure qui relisent des pull requests Terraform ou OpenTofu.
- Non couvert
- Ces règles lisent la configuration, pas la sortie du plan ni votre compte cloud. Elles ne peuvent pas vous dire si le changement est sûr face au fichier d'état qui existe déjà — c'est le rôle d'un plan relu.
10 règles6 bloquantes2 warning2 info
01 Un état distant, verrouillé et chiffré
errorChaque module racine déclare un backend distant avec verrouillage de l'état et chiffrement au repos. Un module racine sans bloc `backend`, ou avec `local`, est refusé. Un état local signifie que deux `apply` concurrents écrasent mutuellement leur vision de la réalité, et que les ressources du perdant deviennent des orphelines introuvables.
Bloque : la corruption de l'état est la seule défaillance Terraform sans reprise propre.
Portée**/backend.tf**/main.tfenvironments/**
Tagsterraformstate
02 Épinglez chaque provider et chaque module
error`required_providers` épingle avec `~>` ou `=`, et chaque bloc `module` épingle une `version` du registre ou un SHA de commit git. `ref = "main"` et les providers sans contrainte sont refusés. Une montée de version non épinglée transforme une PR d'une ligne sans rapport en migration non prévue, découverte au moment de l'`apply`, dans l'environnement passé le premier.
Bloque : cela rend tout `apply` futur non reproductible, rollback compris.
Portée**/*.tf
Tagsterraformreproducibility
03 Aucun secret dans les variables, les locals ou les valeurs par défaut
errorLes secrets viennent d'une data source de gestionnaire de secrets ou d'une référence native au provider. Un identifiant en dur, ou une `variable` dont le `default` est un secret, est refusé. Les variables qui portent des secrets sont déclarées `sensitive = true`. Les valeurs se retrouvent dans le fichier d'état et dans la sortie du plan — et cette sortie est couramment publiée en commentaire de PR.
Bloque : le secret est divulgué à quiconque a accès au repo ou à l'état.
Portée**/*.tf**/*.tfvars
Tagsterraformsecuritysecrets
04 Pas de provisioner `local-exec` ni `remote-exec`
errorLes ressources ne portent pas de bloc `provisioner`. Utilisez plutôt un script de démarrage, une image préconstruite ou un véritable outil de configuration. Un provisioner ne s'exécute qu'à la création, reste invisible pour `plan`, et son échec marque la ressource `tainted` — l'`apply` suivant la détruit et la recrée, ce qui, sur une base de données ou un disque, emporte les données et pas seulement la ressource.
Bloque : une ressource à état marquée `tainted` est détruite au prochain `apply`, sans que rien dans le plan ne l'ait annoncé.
Portée**/*.tf
Tagsterraformsafetylifecycle
05 Les ressources à état gardent leur protection contre la suppression
errorLes bases de données, les buckets qui contiennent des données clients et les disques persistants activent la protection contre la suppression du provider (`deletion_protection`, `prevent_destroy`, politique de rétention) ainsi qu'une rétention de sauvegarde supérieure à zéro. La retirer ou la désactiver dans la même PR qu'un autre changement est refusé — cela part dans sa propre PR, délibérément.
Bloque : ce drapeau est la dernière chose qui sépare un mauvais plan d'une perte définitive.
Portée**/*.tf
Tagsterraformsafetydata
06 Pas d'ingress ouvert sans raison écrite
errorLes règles de security group, de pare-feu et d'ACL réseau ne doivent pas autoriser `0.0.0.0/0` ni `::/0` sur autre chose que les ports 80 et 443, ni attacher une IP publique à une base de données ou à un cache. Si une exception est réellement nécessaire, un commentaire juste au-dessus de la règle en nomme la raison et le responsable. Les ports 22 et 5432 ouverts au monde entier, c'est ainsi que des bases de données se retrouvent en quelques heures dans un index de scan public.
Bloque : l'exposition est immédiate et mondiale dès l'`apply`.
Portée**/*.tf
Tagsterraformsecuritynetwork
07 Chiffrement et blocage de l'accès public sont explicites
warningLes buckets et les volumes déclarent explicitement le chiffrement au repos et le blocage de l'accès public, même quand le défaut du provider le fait déjà. Ce défaut varie selon le provider, la région et la date de création du compte — c'est le bloc explicite qui reste vrai après une migration, et que l'auditeur peut réellement lire.
Avertit : c'est le plus souvent redondant avec un défaut moderne, donc bloquer ne ferait que du bruit — mais c'est la ligne explicite qui survit.
Portée**/*.tf
Tagsterraformsecuritycompliance
08 Utilisez `for_each`, pas `count`, pour les collections nommées
warningLes collections de ressources distinctes utilisent `for_each` avec une clé stable. `count` sur une liste est refusé partout où les éléments ont une identité. Retirer un élément renumérote tous les indices suivants : Terraform détruit et recrée alors des ressources qui n'ont pas changé — invisible dans le diff, visible seulement dans le plan.
Avertit : c'est un piège de correction, mais migrer un `count` existant coûte cher.
Portée**/*.tf
Tagsterraformcorrectness
09 Les variables déclarent type, description et validation
infoChaque bloc `variable` porte un `type` et une `description`, plus un bloc `validation` pour les énumérations, les CIDR et les limites de taille. Une variable non typée ou non validée échoue au moment de l'`apply`, à mi-parcours d'un changement, au lieu d'échouer au `plan`.
Indicatif : cela améliore le mode de défaillance plutôt que d'empêcher la défaillance.
Portée**/variables.tf**/*.tf
Tagsterraformmaintainability
10 Noms et tags suivent la convention
infoLes noms de ressources incluent l'environnement et la charge (`{env}-{service}-{role}`), et chaque ressource hérite du jeu de tags partagé (owner, service, cost-center) via `default_tags` ou `local.tags`. Les ressources sans tag sont inattribuables sur la facture et introuvables pendant un incident — et personne ne revient jamais les taguer.
Indicatif : de l'hygiène pure, mais du genre qui se cumule sur des centaines de ressources.
Portée**/*.tf
Tagsterraformfinopsconventions
Avant de tout importer
La sévérité est le levier qui décide si l'équipe garde la gate activée. Seul error bloque un merge — importez d'abord ces règles en warning, regardez ce qui se déclenche réellement sur quelques semaines de vraies pull requests, et promouvez celles qui l'ont mérité. Et réécrivez les globs de portée : les chemins ci-dessus sont des exemples, et une règle ne s'applique qu'aux fichiers correspondant à ses globs.
Les règles entrent dans Normik une par une — via le formulaire du playbook, ou en laissant un agent de code les proposer par MCP à partir d'un repo existant. Il n'y a pas d'import markdown en masse : le bouton de copie ci-dessus sert à votre AGENTS.md, à une PR où l'équipe débat de la liste, et à garder les règles sous les yeux pendant que vous saisissez celles que vous avez retenues.
Chaque règle ici est jugée comme Normik la juge : sur les lignes que la pull request ajoute. Rien dans ce playbook ne dépend de la description de la PR, d'un fichier que le diff ne touche pas, ni de l'historique d'un fichier — ce sont de vrais standards de revue, mais une gate qui n'en voit pas la preuve ne pourrait que rester muette ou deviner.
L'anatomie d'une règle, le comportement des sévérités et des globs, le versionnage et l'export AGENTS.md sont détaillés dans la documentation du playbook.