guest@6admin.io:~$

Ansible avant Ansible : ssh, un CSV et un moteur de template

En 2006-2007, j’administrais un petit parc à coups de scripts, comme tout le monde, sauf que je croyais (jeunesse!) que mon bricolage était une architecture. Il avait trois composants: une liste d’hôtes, un CSV et du bash. Ansible n’existait pas encore (il est né en 2012), mais la forme du problème était déjà là et la mienne la modélisait à 80%.

L’inventaire: une liste + un CSV

La liste des machines, un fichier plat, une machine par ligne. Et par-dessus, un CSV de variables d’inventaire:

host,ip,user,role,env
web01,192.168.10.11,deploy,web,prod
web02,192.168.10.12,deploy,web,prod
db01,192.168.10.21,root,db,prod

La connexion, c’était ssh, directement: pas d’agent, pas de démon, pas de protocole. Le CSV donnait l’IP, l’utilisateur, le rôle; le script filtrait dessus (awk -F, '$4=="web"') et la boucle ssh poussait ce qu’il fallait. Je dois l’avouer: j’étais fier de ce bricolage, fier comme un coq sur son CSV. C’était l’ERP du pauvre et je le disais avec un aplomb que je ne m’autorise plus aujourd’hui. C’était lent, séquentiel et ça suffisait à un parc qui se comptait en dizaines.

Le moteur de template, l’histoire en trois itérations

Les variables du CSV alimentaient un moteur de template qui rendait les fichiers de conf par hôte (nginx par web, my.cnf par db). Et là, la mémoire est revenue en trois couches, parce que je suis passé par les trois moteurs, dans l’ordre.

D’abord, sed tout simplement. La substitution naïve: sed -e "s|{{host}}|$host|g" conf.tpl. Ça marchait, jusqu’à la première valeur contenant un délimiteur: un | dans une valeur casse le s///, un & réinsère la chaîne matchée, un backslash fuit. Le problème de fond: sed substitue des chaînes, il n’a aucun concept de variable et chaque caractère de la valeur est une syntaxe en puissance. C’est exactement pour ça que le CSV posait problème: sed ne voit pas des données, il voit du texte.

Puis php-cli. Entre 2005 et 2010 je faisais de la programmation système et même du client-serveur, en php-cli, donc le moteur naturel de l’époque était de ce style:

<?php
$host = $argv[1];
echo str_replace('{{host}}', $host, file_get_contents('conf.tpl'));

Les valeurs redeviennent des données (une variable PHP peut contenir n’importe quoi, virgules et délimiteurs compris), les boucles existent, le rendu multi-fichiers est trivial. L’inconvénient: chaque template devient un fichier PHP de plus à maintenir et l’échappement des {{var}} eux-mêmes est un parcours de santé. Mieux, pas bon.

Enfin, m4. Le vrai moteur, celui qui a tenu:

define(`HOST', `web01')dnl
server_name HOST.domain.tld;

L’expansion récursive (les macros composent), le dnl qui évacue les commentaires, le quotage explicite aux backquotes qui protège les virgules (le bug exact de sed) et il est installé partout: un m4, zéro dépendance, là où php-cli exigeait un runtime sur le générateur. Son piège: chaque token du template devient une macro en puissance et un define mal placé transforme ta conf en confettis. La leçon que les trois itérations m’ont laissée: chaque moteur existait pour réparer un bug précis du précédent (les délimiteurs, les données vs texte, la composition) et cette progression naïf → langage → vrai langage est exactement celle qu’ont suivie Jinja2 et les Go templates. Je l’ai vécue à l’échelle d’un seul parc.

Ce qui marchait, ce qui manquait

Ce qui marchait: l’inventaire comme donnée (le CSV était la source de vérité, pas les machines), ssh comme transport (zéro installation côté client, le seul prérequis était une clef) et des templates qui rendaient des confs identiques d’un hôte à l’autre.

Ce qui manquait et qu’Ansible a apporté cinq ans plus tard:

  • l’idempotence: mes scripts poussaient des fichiers, ils ne “convergeaient” rien. Rejouer n’était pas sans effet;
  • les facts: aucune découverte de l’état réel, le CSV mentait dès qu’un disque changeait;
  • le parallélisme: une boucle ssh séquentielle, une machine lente ralentissait tout le parc;
  • la gestion d’erreurs: le set -e du bash et la foi.

Ce que j’ai gardé

La leçon qui a survécu à tout: l’inventaire est une donnée, le transport est ssh, le reste est de la plomberie remplaçable. Ansible a industrialisé exactement ces trois choix (inventaire YAML/INI, ssh, modules idempotents) et j’ai repris la même philosophie à l’envers plus tard avec les inventaires dynamiques, où l’inventaire se lit dans le réseau ou l’hyperviseur au lieu d’un CSV.

[edit: 2026] Le bricolage a continué d’évoluer pendant des années avant que je ne l’abandonne vraiment. Le m4 est resté le cœur un temps, puis a cédé la place à envsubst et mo pour le rendu ; le CSV d’inventaire est devenu un .env clé-valeur lu directement par le shell ; et la boucle ssh maison a fondu dans un ~/.ssh/config bien tenu. Même triptyque qu’en 2007, l’inventaire en données, le transport en ssh, le rendu par template, mais avec des briques enfin propres et auditables. J’avais bien touché à Ansible dès 2012, sans en faire grand-chose et côté boulot c’était plutôt Puppet et Chef ; je n’ai vraiment basculé, y compris pour mes propres besoins, que vers 2016. Ansible apportait ce qui manquait depuis le début, l’idempotence, le parallélisme et la gestion d’erreurs. Ce toolkit minimal garde malgré tout sa place pour les besoins simples, là où le fait-main bat encore l’outil lourd, ce que j’ai développé sous l’angle culturel dans Frugalité Unix . Quant à l’inventaire qui se lit dans le réseau plutôt que dans un fichier, il a fini en inventaire Ansible piloté par DNS .