Exécuter la même tâche sur plusieurs machines avec Bash et sshpass
Dans le cadre du défi “100 jours de DevOps” de la plateforme KodeKloud, une des tâches fournies consistait à installer cronie sur trois serveurs et à créer la même tâche cron pour l’utilisateur root :
The Nautilus system admins team has prepared scripts to automate several day-to-day tasks. They want them to be deployed on all app servers in Stratos DC on a set schedule. Before that they need to test similar functionality with a sample cron job. Therefore, perform the steps below:
a. Installcroniepackage on all Nautilus app servers and startcrondservice.
b. Add a cron*/5 * * * * echo hello > /tmp/cron_textfor root user.
À la lecture de l’énoncé j’entrevoyais déjà la procédure attendue, manuelle et répétitive :
- récupérer les identifiants du premier serveur et s’y connecter en SSH
- installer le paquet cronie
- vérifier l’état du service
- éditer le crontab root
- déconnexion et répétition avec les deux serveurs suivants.
J’ai une horreur viscérale de ce genre de répétitions robotiques sans intérêt ; ça fait crier quelque chose au fond de moi : “Je ne suis pas une machine !” Car c’est à la machine que revient d’effectuer ce genre de tâches : le mot informatique n’est-il pas la contraction de information automatique après tout ?
Ce qui m’intéresse dans l’approche DevOps c’est exactement ça : mettre un terme à cette manière manuelle et fastidieuse de procéder, et parachever enfin la raison d’être de l’informatique. On veut de l’industrialisation, de l’automatisation, de l’Infra-as-Code enfin !
J’ai donc commencé par envisager mes alternatives :
- installer Ansible et créer un playbook : overkill pour un lab unique, entre l’installation et l’écriture ça aurait introduit une complexité inutile à ce stade ; je suis sûr que je dois pouvoir faire cela avec les outils à ma disposition.
- mon petit chouchou, GNU parallel : pas installé dans l’environnement du lab, suppose une connexion par clé SSH pour se passer de mot de passe.
- reste le script Bash traditionnel.
J’ai commencé par établir la procédure opératoire précise pour la traduire en commandes impératives :
-
pour se connecter à chaque serveur :
ssh user1@server1, acceptation de la clé d’hôte puis entrée du mot de passe de l’utilisateur -
exécution des trois commandes pour répondre à l’énoncé :
sudo dnf install -y cronie sudo systemctl start crond # pas nécessaire car le service est démarré automatiquement echo "*/5 * * * * echo hello > /tmp/cron_text" | sudo tee -a /etc/crontab
Pour automatiser cela :
- Je peux utiliser l’options
-o StrictHostKeyChecking=nopour contourner l’acceptation de la clé d’hôte - Je peux recourir à sshpass pour fournir le mot de passe de manière non-interactive (par miracle il est installé sur la machine du lab)
- Une petite recherche m’apprend que
sudoaccepte l’option-S, --stdinpour “lire le mot de passe depuis l’entrée standard”.
Tout ceci mis ensemble donne pour le premier serveur :
sshpass -p Ir0nM@n ssh -o StrictHostKeyChecking=no tony@stapp01 \
'sudo -S dnf install -y cronie <<< "Ir0nM@n" &&\
echo "*/5 * * * * echo hello > /tmp/cron_text" | sudo tee -a /etc/crontab'
Ou sur une seule ligne :
sshpass -p Ir0nM@n ssh -o StrictHostKeyChecking=no tony@stapp01 'sudo -S dnf install -y cronie <<< "Ir0nM@n" && echo "*/5 * * * * echo hello > /tmp/cron_text" | sudo tee -a /etc/crontab'
Ok, c’est un peu hacky mais à ma bonne surprise ça fonctionne !
La deuxième étape a été d’extraire les parties communes dans des variables :
SSH_OPTIONS='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR'
COMMAND='sudo -S dnf install -y cronie <<< $PASSWORD && echo "*/5 * * * * echo hello > /tmp/cron_text" | sudo tee -a /etc/crontab'
sshpass -p Ir0nM@n ssh $SSH_OPTIONS tony@stapp01 $COMMAND
sshpass -p Am3ric@ ssh $SSH_OPTIONS steve@stapp02 $COMMAND
sshpass -p BigGr33n ssh $SSH_OPTIONS banner@stapp03 $COMMAND
Restait à intégrer le mot de passe comme variable, utilisée à la fois pour se connecter en SSH et pour le premier sudo une fois connecté. Le petit défi est :
- de faire se correspondre le binôme
utilisateur@machineavec le bon mot de passe, et de les changer à chaque itération - de passer le mot de passe à l’intérieur de la variable contenant la commande…
À ce stade je voyais bien qu’il ne me manquait plus qu’un petit effort pour parvenir à automatiser cette procédure et qu’elle fonctionne sur les trois serveurs.
Mon intuition d’une boucle for ne convient pas : même en en imbriquant plusieurs les différents paramètres associés ne se conjugueraient pas correctement.
Attendez un instant, “paramètres associés”…Associatif… Comme un tableau associatif ?

Voilà, c’est ça qu’il me faut : établir un tableau associatif ‘utilisateur-serveur-mot de passe’ et itérer dessus pour exécuter les commandes !
N’utilisant pas souvent cette forme de variable dans Bash, c’était le moment pour moi de recourir à un LLM pour accélérer la conception du script. Je vous laisse admirer le résultat :
SSH_OPTIONS='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR'
declare -A HOSTS=(
["tony@stapp01"]="Ir0nM@n"
["steve@stapp02"]="Am3ric@"
["banner@stapp03"]="BigGr33n"
)
for USERHOST in "${!HOSTS[@]}"; do
PASSWORD="${HOSTS[$USERHOST]}"
COMMAND="echo '$PASSWORD' | sudo -S dnf install -y cronie &&
sudo systemctl enable --now crond &&
echo '*/5 * * * * root echo hello > /tmp/cron_text' | sudo tee -a /etc/crontab"
echo "→ Deploying to $USERHOST..."
sshpass -p "$PASSWORD" ssh $SSH_OPTIONS "$USERHOST" "$COMMAND"
done
Malin : c’est à l’intérieur de l’itération que se fait l’affection de la variable PASSWORD, en adéquation avec le binôme utilisateur-machine sur lequel on est actuellement, et ensuite la variable COMMAND intègre le mot de passe variabilisé en inversant l’utilisation des guillemets simples et doubles. Du propre dont j’apprends beaucoup !
Voilà, c’est ici que s’achève ce récit d’automatisation. On termine avec quelques titres accrocheurs suggérés par ce même LLM :
Fini le copier-coller et le SSH manuel, on peut tout à fait orchestrer plusieurs serveurs sans Ansible ni configuration complexe !
Pourquoi taper la même suite de commandes X fois quand Bash peut le faire pour vous ?
Apprenez à vous servir de la paresse comme d’un levier de développement !
😁