Aller au contenu

Écrire un outil jetable proprement, sans faille ni dette

Écrire un outil jetable proprement, sans faille ni dette

Section intitulée « Écrire un outil jetable proprement, sans faille ni dette »

Un outil jetable est un script ou un petit programme écrit pour un besoin ponctuel : extraire, convertir, migrer, purger, corriger en masse, vérifier une hypothèse en plein incident. L’expérience montre qu’il n’est presque jamais jeté : il est relancé six mois plus tard, copié, planifié, hérité. Cette page fixe le minimum à respecter même pour dix lignes, et les signes qui montrent qu’un outil a cessé d’être jetable.

→ Lire l’explication complète de la marmotte

Outil jetable : le compromis entre vitesse d’écriture et durée de vie réelle

Section intitulée « Outil jetable : le compromis entre vitesse d’écriture et durée de vie réelle »

Le compromis tient en une ligne. Ce qu’on gagne : des minutes, parfois des heures, sans cérémonie. Ce qu’on paie : un programme ni testé ni relu, lancé avec des droits élevés sur de la production, par une seule personne, souvent fatiguée.

La position défendue ici : l’effort se règle sur l’effet de l’outil, pas sur sa taille. Dix lignes qui suppriment méritent plus de soin que trois cents lignes qui produisent un rapport en lecture seule. Compter les lignes pour décider du niveau de précaution revient à évaluer un médicament au poids de la boîte.

L’excès inverse existe et coûte aussi cher. Exiger des tests, une chaîne d’intégration et un comité pour une conversion faite une fois, c’est garantir que le prochain script sera écrit hors de tout dépôt, sur un poste, loin des regards. Le minimum doit coûter quelques minutes, sinon il est contourné. Le reste de la page décrit ce minimum, puis ce qui s’ajoute quand l’outil modifie, supprime ou survit.

Prérequis de plateforme avant le premier script ponctuel

Section intitulée « Prérequis de plateforme avant le premier script ponctuel »

Un script propre ne s’écrit pas dans le vide. Quatre choses doivent exister avant, faute de quoi les règles qui suivent restent théoriques :

  • un dépôt de code avec un répertoire prévu pour les outils ponctuels, accessible en écriture à l’équipe ;
  • un moyen de délivrer un secret sans le copier (coffre de secrets, ou à défaut saisie interactive), et un moyen de le révoquer ;
  • un compte non personnel aux droits bornés, obtenable rapidement. Si la demande prend trois jours, le compte d’administrateur personnel gagnera à chaque fois ;
  • une sauvegarde dont la restauration a été essayée, et une copie hors production sur laquelle lancer un premier essai.

S’y ajoute un endroit où déposer la trace d’exécution : demande de changement, ticket d’incident ou journal centralisé.

Choix du langage du script selon la tâche et le relecteur

Section intitulée « Choix du langage du script selon la tâche et le relecteur »

Deux critères, dans cet ordre : la nature de la tâche, puis la personne qui devra relire. Le second est sous-estimé. Le langage à retenir est celui que lit la personne qui sera réveillée le jour où l’outil casse, pas celui que l’auteur préfère. Le script ponctuel n’est pas le lieu pour découvrir le langage à la mode du trimestre.

TâcheChoix raisonnableSigne qu’il faut en changer
Enchaîner des commandes existantes, manipuler des fichiersShellBesoin de structures de données, d’analyse d’un format structuré ou d’un traitement fin des erreurs
Appeler une API (Application Programming Interface), transformer des données, valider des entréesPython ou tout langage généraliste lu par l’équipeLes dépendances s’accumulent pour un besoin que la bibliothèque standard couvre
Corriger des données déjà présentes dans une baseSQL (Structured Query Language), dans une transactionLa logique exige un appel externe pour chaque ligne

Dernière ligne du tableau : quand les données sont dans la base, les corriger dans la base. Les extraire, boucler dessus depuis un poste et les réinjecter ajoute une copie de production en circulation et retire la transaction, soit deux problèmes pour le prix d’un. Le détail propre à chaque langage est traité ailleurs : Python pour l’automatisation défensive, les scripts Bash, JavaScript et SQL, et la frise des langages pour situer l’ensemble.

Minimum non négociable d’un script jetable, même pour dix lignes

Section intitulée « Minimum non négociable d’un script jetable, même pour dix lignes »

Six règles, sans dérogation liée à la taille ou à l’urgence :

  1. Aucun secret dans le code ni sur la ligne de commande.
  2. Un en-tête qui dit l’objet, le périmètre, l’effet, la date d’écriture et la date de suppression.
  3. Arrêt à la première erreur : un script qui continue après un échec exécute la suite sur un état que personne n’a prévu.
  4. Rangement dans un dépôt, pas dans un dossier personnel.
  5. Mode à blanc par défaut dès que l’outil modifie ou supprime.
  6. Une trace d’exécution conservée ailleurs que sur le poste.

Un outil en lecture seule sur des données non sensibles peut s’arrêter à la règle 4. Tout le reste de la page (idempotence, reprise, validation renforcée, relecture) s’ajoute à mesure que l’effet devient large ou irréversible.

Secrets, compte et droits d’exécution de l’outil

Section intitulée « Secrets, compte et droits d’exécution de l’outil »

Un secret dans le code finit dans le dépôt, puis dans chaque clone du dépôt ; la page sur la prévention des secrets dans le code source traite ce cas. La ligne de commande est le piège suivant, moins connu : sur un système Linux, les arguments d’un processus sont lisibles par les autres utilisateurs de la machine dans la liste des processus (sauf durcissement spécifique), et l’historique du shell les écrit sur disque.

Fenêtre de terminal
# À proscrire : visible dans la liste des processus et dans l'historique
./purge.sh --jeton "s3cr3t"
# Bash : saisie sans écho, jamais sur la ligne de commande ni dans le fichier
read -rs -p "Jeton : " JETON; echo

La variable ainsi remplie reste interne au shell tant qu’elle n’est pas exportée. Pour la transmettre à un programme appelé, passer par son entrée standard ou par un fichier lisible du seul propriétaire, pas par un argument. L’ordre de préférence : secret à durée courte récupéré dans un coffre à l’exécution, puis saisie interactive, puis fichier à droits restreints hors du dépôt. Le secret émis pour l’outil est révoqué quand l’outil a fini, pas « à la prochaine rotation ».

Côté compte, la règle est le moindre privilège : un compte dédié ou des droits temporaires, bornés au périmètre, en lecture seule pour une extraction. Le compte personnel d’administrateur, choisi par commodité, cumule trois défauts :

  • ses droits dépassent le besoin, donc une erreur de sélection n’a aucune borne ;
  • la trace attribue à une personne l’action d’un programme ;
  • le jour où le script est planifié, il vit sur ce compte, qui ne peut plus être désactivé au départ de son titulaire sans casser quelque chose.

Ce dernier mécanisme fabrique les identités que plus personne n’ose révoquer, sujet de la page sur les comptes de service à révoquer réellement.

Mode à blanc, idempotence et reprise après interruption

Section intitulée « Mode à blanc, idempotence et reprise après interruption »

Tout outil qui modifie ou supprime démarre à blanc (dry-run) : sans option, il lit, calcule, affiche ce qu’il ferait et ne touche à rien. L’exécution réelle exige une option explicite. Le sens du défaut compte : avec une option « à blanc » facultative, l’oubli détruit ; avec une option « exécuter » obligatoire, l’oubli coûte une relance.

Trois pièges vident le mode à blanc de sa valeur :

  • Le chemin de code distinct. Le mode à blanc doit faire les mêmes lectures, la même validation et la même sélection que l’exécution réelle, et ne diverger qu’au dernier appel d’écriture. Sinon il valide un programme qui n’est pas celui qui tournera.
  • L’état qui bouge entre les deux passes. Le mode à blanc produit un plan : la liste des objets visés et leur décompte. L’exécution réelle recalcule ce décompte et refuse de continuer s’il diffère, ou mieux, consomme le plan lui-même.
  • La confirmation réflexe. Un « o » tapé par habitude ne confirme rien. Faire saisir le décompte attendu ou le nom de l’environnement cible oblige à lire.

Le mode à blanc ne vérifie ni les droits d’écriture ni les contraintes d’intégrité : le premier lot réel reste petit.

L’idempotence est la propriété d’un outil dont la relance produit le même état final qu’une exécution unique. Elle s’obtient en décrivant un état cible (« ce compte est désactivé ») plutôt qu’une action (« désactiver »), et en sélectionnant par état (« ce qui n’est pas encore migré ») plutôt que par position dans un fichier. La reprise s’obtient en écrivant un point de reprise sur disque après chaque lot, parce qu’une session se perd, un délai expire, un quota tombe. Sans l’une ni l’autre, une interruption à mi-parcours laisse deux options : tout relancer et traiter la moitié deux fois, ou deviner où le traitement s’est arrêté. La divination a un taux de succès médiocre en production.

Validation des entrées, fichiers temporaires et données extraites

Section intitulée « Validation des entrées, fichiers temporaires et données extraites »

Le fichier fourni par l’auteur du script n’est pas plus fiable que les autres : il sort d’un tableur, a transité par une messagerie et a été retouché à la main à une heure où plus personne ne relit rien. Cinq contrôles couvrent l’essentiel des dégâts constatés :

  • Valeur vide. Une ligne vide ou une colonne manquante donne une variable vide. Concaténée dans un chemin, elle désigne le répertoire parent ; dans un filtre, elle désigne tous les enregistrements. Refuser le vide explicitement.
  • Caractères invisibles. Fins de ligne Windows, marque d’ordre des octets en tête de fichier, espaces de fin : l’identifiant ne correspond plus à rien, ou correspond à autre chose.
  • Format. Un motif admis pour chaque identifiant ; tout ce qui n’y entre pas est rejeté et compté. Un seul rejet arrête l’outil avant la première écriture.
  • Volume. Le décompte attendu est déclaré ; au-delà, refus. Un script censé toucher quarante objets et qui en trouve quarante mille a un problème de sélection, pas un surcroît de travail.
  • Cible. L’en-tête annonce le périmètre, le code le vérifie : nom de la base, de l’hôte ou de l’environnement, et refus sinon.

Les fichiers temporaires vont dans un répertoire créé par mktemp -d, à nom imprévisible et réservé au propriétaire, supprimé en sortie (en Bash, par un trap sur EXIT, qui ne se déclenche pas si le processus est tué sans préavis). Un nom fixe dans un répertoire partagé est une invitation.

Les données de production extraites sont le reliquat le plus durable d’un outil jetable. Une copie posée sur un poste échappe au contrôle d’accès, à la durée de conservation et à la procédure de suppression de la source ; les données personnelles qu’elle contient restent soumises au RGPD (Règlement général sur la protection des données), où qu’elles dorment. La règle : n’extraire que les colonnes nécessaires, vers un emplacement chiffré désigné, avec une date de suppression écrite dans l’en-tête, et pseudonymiser quand l’identité ne sert pas au traitement. La page sur la sécurité des postes de développeurs dit pourquoi ce poste est une cible.

Trace d’exécution, en-tête et rangement de l’outil dans un dépôt

Section intitulée « Trace d’exécution, en-tête et rangement de l’outil dans un dépôt »

La trace répond à une question posée des semaines plus tard : qui a lancé quoi, quand, sur quel périmètre, avec quel résultat. Elle contient l’identité de l’opérateur, l’hôte, la date, la version de l’outil (l’identifiant du commit), les arguments sans les secrets, le mode (à blanc ou réel), et trois décomptes : visés, traités, en échec. Elle ne contient ni secret ni contenu de données, seulement des identifiants et des nombres. Elle est déposée hors du poste, parce qu’un fichier local disparaît avec le poste. Le code de retour est non nul en cas d’échec partiel : un script planifié qui renvoie zéro après avoir raté la moitié du travail ment avec ponctualité.

L’en-tête est ce que lira l’héritier de l’outil, et souvent la seule chose qu’il lira :

#!/usr/bin/env bash
# OBJET : désactive les comptes listés dans le fichier passé en argument
# PÉRIMÈTRE : annuaire de recette uniquement ; toute autre cible est refusée
# EFFET : MODIFIE (réversible par réactivation manuelle)
# PROPRIÉTAIRE : équipe identités (une équipe, pas une personne)
# ÉCRIT LE : 2026-10-10 À SUPPRIMER APRÈS : 2026-11-10
# PRÉREQUIS : sauvegarde vérifiée, compte dédié à droits bornés
# USAGE : sans option = mode à blanc ; --execute pour modifier
set -euo pipefail # arrêt sur erreur, variable non définie ou tube en échec

La dernière ligne ne couvre pas tous les cas d’erreur du shell ; ses exceptions sont détaillées dans la page Bash citée plus haut.

Le rangement : un dépôt, dans un répertoire dédié, avec la date dans le nom du fichier. Le dépôt apporte l’historique, la recherche, la détection de secrets à la poussée et une copie qui fait foi. Le dossier personnel, la page de wiki et le message collé dans un salon de discussion n’apportent rien de tout cela ; la messagerie instantanée n’est pas un gestionnaire de versions, malgré une pratique du secteur qui suggère le contraire.

La bibliothèque standard d’abord. Chaque dépendance ajoutée amène les siennes, que personne n’a choisies (voir les dépendances transitives), pour un outil censé vivre une journée. Quand une dépendance est justifiée, sa version exacte est fixée dans un fichier rangé à côté du script, avec verrouillage par empreinte quand l’outil d’installation le permet. Un script non épinglé relancé plus tard n’exécute pas le même code que la première fois, et rien ne le signale.

L’installation se fait dans un environnement isolé, sur le poste d’administration ou le serveur de rebond. Installer des paquets avec des droits d’administrateur sur un serveur de production pour faire tourner un outil ponctuel laisse derrière soi un logiciel que plus personne ne mettra à jour.

Un outil jetable concentre des droits élevés, du code non relu et des données sensibles : c’est une cible, pas un détail d’intendance.

Attaque ou défaillanceContre-mesure
Lecture du secret dans la liste des processus, l’historique ou le dépôtSaisie sans écho ou coffre, secret à durée courte, révocation en fin d’usage
Injection : une valeur du fichier d’entrée interprétée comme commande ou comme requêteRequêtes paramétrées, arguments passés séparément et jamais concaténés, validation par motif admis
Fichier temporaire à nom prévisible, devancé par un lien symboliqueRépertoire créé par mktemp -d, droits réservés au propriétaire
Script planifié modifiable par un compte moins privilégié que celui qui l’exécuteFichier et répertoires parents en écriture pour le seul compte d’exécution
Dépendance remplacée ou modifiée entre deux exécutionsVersion exacte épinglée, empreinte vérifiée
Extraction de production oubliée sur un posteEmplacement chiffré désigné, suppression en fin d’exécution, date dans l’en-tête

La quatrième ligne est la plus sous-estimée : un fichier exécuté périodiquement par un compte privilégié et modifiable par un compte ordinaire constitue une élévation de privilèges, prête à l’emploi.

Exemple : déroulé d’une purge de masse, du mode à blanc au nettoyage

Section intitulée « Exemple : déroulé d’une purge de masse, du mode à blanc au nettoyage »

Le schéma suit une purge d’objets à partir d’un fichier d’identifiants. À lire : l’ordre des étapes, et le fait que l’exécution réelle ne fait confiance ni à l’opérateur ni au plan sans recalculer le décompte.

Séquence entre six acteurs : Pair, Opérateur, Outil, Cible, Journal et Poste. L'opérateur lance l'outil sans option, en mode à blanc, avec le fichier d'entrée ; l'outil fait des lectures seules sur la cible, avec la même sélection que l'exécution réelle, puis rend un plan (objets visés, décompte, rejets de validation). Si l'effet est irréversible, l'opérateur fait relire le plan par un pair. Il relance avec l'option d'exécution et saisit le décompte attendu. Si le décompte recalculé est différent du plan, l'outil s'arrête sans rien modifier ; s'il est identique, il modifie la cible par lots et écrit un point de reprise après chaque lot. L'outil écrit ensuite au journal l'opérateur, la date, le périmètre, la version et le résultat par lot, puis supprime du poste les fichiers temporaires et les extractions.

Le premier lancement ne modifie rien et produit un plan. Si la validation a rejeté ne serait-ce qu’une ligne, la suite n’a pas lieu : on corrige le fichier, on recommence. Quand l’effet est irréversible, le plan est lu par une seconde personne avant toute exécution ; c’est le plan qu’elle relit, pas le code, et cela prend quelques minutes. L’exécution réelle exige l’option et la saisie du décompte, recalcule la sélection, puis traite par lots en notant sa progression. Une interruption se résout par une relance, qui repart du dernier point noté. La trace part hors du poste, les fichiers de travail sont effacés. Le secret émis pour l’opération est révoqué à ce moment-là.

Fin de vie du script : suppression effective ou promotion en outil maintenu

Section intitulée « Fin de vie du script : suppression effective ou promotion en outil maintenu »

Un outil a cessé d’être jetable dès qu’un de ces signes apparaît :

  • il est lancé une seconde fois ;
  • il est lancé par quelqu’un d’autre que son auteur ;
  • il est planifié ;
  • un autre programme l’appelle ou consomme sa sortie ;
  • il figure dans une procédure ;
  • il a gagné des options « pour le cas de la dernière fois » ;
  • quelqu’un demande quelle copie est la bonne.

La planification tranche à elle seule : un outil planifié n’est plus jetable, quelle que soit sa taille. L’arbre ci-dessous donne les trois issues ; à y lire, le fait qu’aucune branche ne s’appelle « on verra ».

Arbre de décision sur la fin de vie d'un script. Un besoin ponctuel mène à une première exécution, avec en-tête, date de suppression et dépôt. Si le besoin est clos : suppression, soit retrait du dépôt, droits et secrets révoqués, extractions purgées. En cas de seconde exécution, de copie ou de date de suppression atteinte : décision. Relances manuelles et rares par l'équipe qui l'a écrit : outil gardé, avec propriétaire nommé et nouvelle date de suppression, puis retour à la décision quand cette date est atteinte. Planifié, lancé par d'autres, appelé par un programme ou effet irréversible répété : outil maintenu, avec tests, revue, compte de service et supervision.

La suppression est une vraie opération : retrait du dépôt (l’historique le conserve pour qui en aurait besoin), retrait de toute planification, révocation du compte, des droits et des secrets créés pour lui, purge des extractions. La promotion aussi : un propriétaire qui est une équipe, des tests sur les cas qui détruisent, une revue de code, un compte de service, une supervision des échecs. L’état intermédiaire, « gardé », est légitime à condition d’avoir un propriétaire et une échéance. Sans échéance, c’est un outil de production qui s’ignore.

Un outil qui respecte tout ce qui précède reste non testé et non relu. Les règles bornent les dégâts d’une erreur ; elles n’en réduisent pas la probabilité. Trois garanties doivent venir d’ailleurs :

  • La réversibilité. Avant une modification de masse, une sauvegarde dont la restauration a été essayée sur le périmètre concerné. Un témoin vert sur une tâche de sauvegarde n’est pas une restauration (voir le test de restauration des sauvegardes).
  • La justesse de la logique. Un essai sur une copie représentative, pas sur trois lignes fabriquées à la main : les cas qui cassent sont dans les données réelles.
  • Le second regard. Quand l’effet est irréversible, la relecture du plan par un pair avant exécution. Un mode à blanc relu par son seul auteur confirme ce que l’auteur croyait déjà.

Le mode à blanc lui-même ne prouve pas l’exécution réelle : il ne teste ni les écritures, ni les droits, ni le comportement sous charge de la cible.

Faut-il écrire des tests pour un script de dix lignes ?

Section intitulée « Faut-il écrire des tests pour un script de dix lignes ? »

Non, tant qu’il reste jetable : le mode à blanc, la validation des entrées et l’essai sur une copie tiennent lieu de filet. Les tests deviennent dus au moment de la promotion en outil maintenu, en commençant par les cas qui détruisent (entrée vide, cible erronée, relance après interruption).

Une variable d’environnement est-elle acceptable pour passer un secret ?

Section intitulée « Une variable d’environnement est-elle acceptable pour passer un secret ? »

Elle vaut mieux qu’un argument, puisqu’elle n’apparaît pas dans la liste des processus vue par les autres utilisateurs. Elle est en revanche héritée par tous les processus enfants et se retrouve facilement dans un journal de débogage qui affiche l’environnement. La saisie sans écho ou le coffre restent préférables ; si la variable est retenue, elle n’est jamais définie dans un fichier de profil du shell.

Que faire d’un outil ponctuel hérité sans en-tête ni propriétaire ?

Section intitulée « Que faire d’un outil ponctuel hérité sans en-tête ni propriétaire ? »

Le traiter comme non fiable : le lire en entier, identifier le compte et les secrets qu’il utilise, le lancer d’abord sur une copie. Puis appliquer l’arbre de décision, sans branche de faveur pour l’ancienneté. Un outil qui tourne depuis longtemps sans incident connu prouve surtout que personne n’a regardé.

Sans ces précautions, un mot de passe à droits élevés reste lisible dans l’historique d’un poste aussi longtemps que personne ne pense à le changer, c’est-à-dire longtemps. Une correction de masse interrompue laisse un état que nul ne sait décrire, et faute de savoir ce qui a été touché, la seule réparation possible devient la restauration complète, y compris de ce qui allait bien. La question « qui a modifié ces enregistrements » reste sans réponse, ce qui prive l’analyse d’incident de son point de départ. Une copie de production survit sur un ordinateur portable, hors de toute politique de suppression. Enfin, l’outil planifié sous un compte nominatif maintient ce compte actif après le départ de son titulaire, avec tous ses droits.

Un outil jetable est un petit programme écrit pour un travail fait une seule fois : copier des données, les transformer, en supprimer. En pratique, il est presque toujours relancé plus tard, par quelqu’un d’autre, parfois automatiquement. Il faut donc l’écrire avec quelques précautions dès le départ. D’abord, les mots de passe ne sont jamais écrits dans le programme ni tapés à la suite de son nom, car ils restent alors lisibles sur la machine. Ensuite, le programme tourne avec un compte qui n’a que les droits nécessaires : une erreur fait ainsi moins de dégâts. Avant de modifier quoi que ce soit, il montre ce qu’il s’apprête à faire sans le faire ; une personne vérifie cette liste, puis demande l’exécution réelle. Le travail avance par petits paquets, et le programme note où il en est : s’il est interrompu, il reprend sans refaire ce qui est déjà fait. À la fin, il écrit qui l’a lancé, quand, sur quoi et avec quel résultat, puis efface ses fichiers de travail. Quelques lignes au début du programme disent à quoi il sert et à quelle date le supprimer. Si, à cette date, il sert encore, il faut choisir : le supprimer, ou le confier à une équipe qui le teste et le relit. Ces précautions n’empêchent pas toutes les erreurs : une sauvegarde dont la restauration a été essayée reste indispensable.

À lire aussi

Pages voisines

: les deux pages se citent l’une l’autre.