En bref

En clair : Au lieu d’une clé d’accès par composant, une seule pour tout le projet : plus simple à renouveler, au prix de droits plus larges.

Mon projet avait sept identités IAM et quatorze secrets pour les mêmes buckets, et douze secrets GitHub à recopier à la main à chaque rotation. J’ai tout ramené à un seul jeu de credentials, et fait appliquer à la CI le plan qu’elle affiche. Le bénéfice est réel, mais j’ai aussi annoncé un gain de lignes de code que la mesure a démenti, et accepté un risque que je décris ici.

Quel est le contexte ?

Mon projet personnel de prix de carburants tourne sur Scaleway, déployé par Terraform depuis GitHub Actions. Chaque bloc logique (ingestion, compactage, rapprochement des marques…) avait sa propre identité et ses propres clés d’accès au stockage. L’infrastructure est répartie en plusieurs stacks Terraform, dont une qui crée les credentials et une autre qui les lit. Le contexte du projet est sur la fiche Plein Data.

Quel était le problème ?

Le principe « un projet, un secret » n’était pas respecté. Chaque rotation annuelle demandait de régénérer sept clés et de recopier douze secrets dans GitHub, une opération manuelle où l’on peut oublier une copie sans que rien ne le signale.

Qu’ai-je fait ?

AvantAprès
Identités IAM de stockage71
Secrets du gestionnaire de secrets142
Secrets GitHub de stockage120
Rotation annuelle7 clés, 12 secrets à recopier1 apply
Coût du gestionnaire de secretsenviron 5 € de moins par mois

Les décisions à ne pas redécouvrir.

  • La policy est au niveau du projet : les règles IAM de Scaleway ne se limitent pas à un bucket, donc les anciennes policies étaient déjà larges. La nouvelle porte l’union des droits. J’ai accepté ce risque.
  • Pas d’état Terraform partagé entre les deux stacks : une source de données ne lit que des métadonnées et ne couple pas les états.
  • Migration en deux temps, sans import ni retrait d’état : créer et repointer, vérifier par un run réel, puis supprimer.
  • Le plan et l’apply tournent dans le même job de déploiement. Le plan de la pull request n’est pas réutilisable, car le tag d’image est le hash du commit, qui change au merge. Le fichier de plan n’est jamais envoyé comme artefact : il contient des valeurs sensibles.
  • Les identités de test et de déploiement restent séparées de celle de production, exprès.

Six incidents en cours de route.

  1. Un accès refusé sur le backend S3 : les clés du fichier de variables local n’accédaient pas au bucket du state, celles du profil de la CLI oui.
  2. Un conflit de nom de secret : la source de données aurait lu le mauvais secret sans erreur. J’ai renommé, et la leçon est de lister les secrets existants avant de choisir un nom.
  3. Un attribut inconnu à la lecture du plan : le fichier de verrouillage des providers était ignoré par git, donc la CI prenait la dernière version du provider alors que mon poste restait sur l’ancienne.
  4. Un job de production en erreur d’image introuvable : le fichier de variables local l’emporte sur les variables d’environnement, et un tag périmé y avait écrasé le bon.
  5. Des variables sensibles sans valeur en local : une valeur factice aurait créé de mauvaises versions de secret.
  6. Un fichier de plan de 47 Ko avec des valeurs sensibles, non ignoré par git. Il l’est maintenant.

La factorisation des jobs, vérifiée par un plan en lecture seule avec la version de Terraform de la CI. Une seule variable de tag d’image au lieu de neuf. Un module pour les huit jobs, avec des blocs de déplacement : le plan indique huit ressources déplacées et aucune créée, modifiée ou détruite. J’avais annoncé 150 à 200 lignes économisées : c’était faux, le total passe de 477 lignes à 466 (351 de jobs et 115 de module), soit un gain à peu près nul. Le bénéfice réel est un seul endroit pour le câblage des secrets. Les fichiers de verrouillage sont maintenant versionnés. Et un bug est apparu en vérifiant : un déclencheur de file de messages était détruit puis recréé à chaque déploiement, à cause d’une ligne redondante que le provider ne relit pas.

Ce qui reste. Une liste de variables dupliquée entre deux workflows et quelques nettoyages manuels d’anciennes clés. Le premier déploiement avec le nouveau flux n’avait pas encore tourné au moment de l’écriture : il faut le surveiller.

Qu’ai-je appris, et à quoi cela renvoie-t-il ?

Quelles règles en retenir ?

  • Ne pas annoncer un gain avant de l’avoir mesuré : mon estimation de lignes était fausse.
  • Tester avec la version de Terraform de la CI, pas seulement celle du poste.
  • Générer un plan « local » depuis un dossier sans fichier de variables, qui peut contenir des valeurs périmées.
  • Vérifier le nom d’un secret avant de le créer : une collision se lit sans erreur.
  • Ne jamais envoyer un fichier de plan comme artefact quand il embarque des valeurs sensibles.

Quels principes connus ce cas illustre-t-il ?

C’est le moindre privilège, dans sa version nuancée : une policy plus large est un risque accepté quand la plateforme n’offre pas de périmètre plus fin. Et c’est la reproductibilité : un plan n’a de valeur que s’il est celui qui s’applique.

Que ferais-je autrement ?

  • Créer un jeu de credentials par niveau de risque (lecture seule, écriture) au lieu d’un seul avec tous les droits. Cela réduit le risque accepté, mais ramène de la complexité et deux secrets à faire tourner. Je l’ai écarté pour un projet à un seul exploitant.
  • Faire échouer la CI si le plan contient une destruction non attendue. Cela aurait attrapé plus tôt le déclencheur recréé à chaque déploiement, au prix d’une liste d’exceptions à maintenir. Je ne l’ai pas fait.
  • Mesurer le gain de lignes avant de l’annoncer. Un diff de comptage sur une branche de test aurait pris cinq minutes.