Aller au contenu

Utilisation correct des modules Ansible raw, command et shell

Mise à jour :

logo

Pour garantir des opérations stables et fiables, Ansible repose sur un principe fondamental : l’idempotence. Cela signifie qu’une tâche exécutée plusieurs fois doit produire le même résultat, sans modifier l’état du système si celui-ci est déjà conforme.

L’outil Ansible propose une large palette de modules dédiés pour gérer les paquets, configurer des services, manipuler des fichiers, … Ces modules, comme apt pour installer des logiciels sur un système basé sur Debian ou copy pour transférer des fichiers, sont conçus pour assurer l’idempotence. Cela signifie que si vous demandez d’installer un paquet déjà présent sur un serveur, Ansible ne tentera pas de le réinstaller, évitant ainsi des changements superflus ou des comportements indésirables.

Cependant, il arrive que vous deviez exécuter des commandes personnalisées sur un hôte distant, notamment dans des cas spécifiques où aucun module Ansible dédié n’existe. C’est là qu’interviennent les modules raw, command, et shell, qui permettent d’exécuter des commandes directement sur les systèmes cibles. Bien qu’efficaces, ces modules ne garantissent pas l’idempotence. Ils exécutent simplement la commande demandée, peu importe l’état initial du système. De ce fait, ils doivent être utilisés avec précaution et en dernier recours.

Dans ce guide, je vais détailler l’utilisation des modules raw, command, et shell, en expliquant leurs différences, leurs cas d’usage, et leurs limites. Vous comprendrez dans quels scénarios ils sont nécessaires, et comment les utiliser efficacement pour éviter les mauvaises pratiques dans vos codes Ansible.

Le module raw : L’exécution brute sans interprétation Python

Le module raw est le plus basique et le plus direct des trois modules que nous allons explorer. Contrairement aux modules command et shell, qui font appel à des interpréteurs , le module raw exécute des commandes brutes sur l’hôte cible. Il envoie directement la commande via SSH, sans aucune transformation ou interprétation par Ansible. C’est donc le module de dernier recours, souvent utilisé lorsque l’infrastructure ne dispose pas encore des prérequis nécessaires au bon fonctionnement des autres modules, notamment Python.

Par exemple, si vous administrez un routeur ou un système embarqué où Python n’est pas disponible et que vous souhaitez exécuter une commande basique, le module raw vous permet d’envoyer directement cette commande sans vous soucier de l’installation de dépendances.

Malgré sa flexibilité, le module raw présente plusieurs inconvénients importants qui limitent son utilisation à des scénarios très spécifiques :

  1. Absence de structuration des résultats : Contrairement aux autres modules Ansible, qui renvoient des résultats sous forme de dictionnaires structurés, le module raw renvoie simplement la sortie brute de la commande exécutée. Cela peut rendre l’analyse des résultats plus complexe, surtout si vous devez gérer les erreurs ou interpréter des données complexes.

  2. Sécurité limitée : Le module raw exécute des commandes directement sur le système sans passer par le shell, ce qui peut comporter des risques. Il n’y a pas de contrôle spécifique sur la commande envoyée, ni de protection contre des attaques d’injection de commandes. Il est donc primordial de bien valider les commandes exécutées pour éviter tout comportement imprévu ou dangereux.

Le module command : Exécution simple sans shell

Le module command est l’un des modules les plus simples d’Ansible. Il permet d’exécuter des commandes basiques directement sur un hôte distant, mais sans utiliser de shell. Contrairement au module shell, qui exécute des commandes via un interpréteur de shell (comme Bash), command ne prend pas en charge des fonctionnalités avancées comme les redirections (>), les pipes (|), ou les chaînes de commandes multiples (&&). Cette simplicité en fait un module plus sûr, car il évite les risques associés à l’exécution de commandes complexes via un shell.

Le module command est particulièrement adapté pour les tâches qui nécessitent l’exécution de commandes simples et non interactives. Par exemple, il peut être utilisé pour exécuter des scripts. Comme il n’utilise pas de shell, il est à privilégier lorsque vous avez besoin d’exécuter une commande sans manipulation avancée et que la sécurité est une préoccupation, car il ne permet pas l’exécution de scripts complexes ni l’utilisation de caractères spéciaux pouvant représenter un risque d’injection.

Limites du module command

Bien que le module command soit simple et sûr, il présente également certaines limitations :

  1. Pas de gestion des redirections et pipes : Comme mentionné précédemment, command ne permet pas l’utilisation de redirections (>, >>), de pipes (|), ou de chaînes de commandes (&&). Si vous avez besoin de combiner plusieurs commandes ou de rediriger les sorties vers un fichier, vous devrez utiliser le module shell.

Le module shell : Exécution de commandes via un shell

Le module shell d’Ansible est le plus flexible des trois modules que nous explorons. Contrairement à command, qui se limite à l’exécution de commandes simples sans shell, shell permet d’exécuter des commandes via l’interpréteur de commandes natif de l’hôte distant (comme Bash ou sh). Cela signifie que vous pouvez utiliser des fonctionnalités avancées du shell, telles que les redirections de sortie, les pipes, et la chaîne de commandes avec des opérateurs logiques comme && ou ||.

Le module shell est particulièrement adapté aux situations où vous devez exécuter des commandes complexes nécessitant un shell pour interpréter des symboles spécifiques comme les pipes (|), les redirections (>, >>), ou pour exécuter plusieurs commandes en séquence avec des opérateurs comme && (si la première commande réussit, la seconde est exécutée). Cela le rend très utile pour des scripts complexes ou des commandes qui manipulent plusieurs flux de données ou fichiers simultanément.

Limites du module shell

Malgré sa puissance, le module shell comporte des risques et des limitations qu’il est important de bien comprendre avant de l’utiliser dans vos playbooks Ansible.

  1. Risques de sécurité : En utilisant le module shell, vous ouvrez la porte à des risques de sécurité supplémentaires, notamment les injections de commandes. Comme le shell interprète directement les commandes que vous envoyez, des variables ou des entrées non contrôlées peuvent mener à des exécutions inattendues ou dangereuses. Il est donc crucial de bien valider et de limiter les commandes passées via shell.

  2. Performance : L’utilisation du shell pour chaque commande peut entraîner une légère perte de performance, car le système doit lancer une nouvelle instance de shell à chaque exécution. Dans des environnements à grande échelle, cette surcharge peut s’accumuler, surtout si le module est utilisé de manière répétée.

Gérer l’idempotence avec creates et removes

Pour pallier l’absence d’idempotence, les modules command permet de spécifier des conditions sous forme de fichiers ou de répertoires existants avec les options creates et removes. Par exemple, vous pouvez utiliser l’option creates pour indiquer qu’une commande ne doit être exécutée que si un certain fichier n’existe pas encore.

Exemple avec creates :

- name: Extraire une archive seulement si le fichier n'existe pas
ansible.builtin.command:
cmd: tar xzf archive.tar.gz
creates: /path/to/extracted/file

Dans cet exemple, la commande tar sera exécutée uniquement si le fichier /path/to/extracted/file n’existe pas encore. Cela permet de rendre la tâche plus idempotente, en évitant d’extraire l’archive plusieurs fois.

De même, l’option removes peut être utilisée pour indiquer qu’une commande doit être exécutée seulement si un fichier ou un répertoire spécifique est présent, et qu’il doit être supprimé après l’exécution de la commande.

Exemple avec removes :

- name: Supprimer un fichier après l'exécution d'une commande
ansible.builtin.command:
cmd: rm /tmp/tempfile
removes: /tmp/tempfile

Dans cet exemple, Ansible exécutera la commande rm pour supprimer le fichier /tmp/tempfile uniquement si ce fichier existe, ce qui ajoute une condition simple mais efficace à l’exécution de la tâche.

Gestion des status changed et failed

Un point important, les modules shell et command s’il ne plante pas retourne toujours le status changed. Pour définir réellement quand une commande échoue ou change quelque chose sur le système, vous devez utiliser les instructions changed_when et failed_when.

Exemple de gestion des erreurs avec register et failed_when :

- name: Exécuter une commande et capturer le résultat
ansible.builtin.shell: "cat /var/log/syslog"
register: result
- name: Vérifier si la commande a échoué
ansible.builtin.debug:
msg: "La commande a échoué avec le message : {{ result.stderr }}"
failed_when: result.rc != 0

Dans cet exemple, la commande cat est utilisée pour lire le fichier de logs système, et le résultat est capturé dans la variable result. Si la commande échoue (par exemple, si le fichier n’existe pas), Ansible affiche le message d’erreur renvoyé par la commande via result.stderr.

Conclusion

Dans ce guide, nous avons exploré en détail les modules Ansible raw, command, et shell, en mettant en lumière leurs cas d’utilisation, leurs avantages, ainsi que leurs limites. Ces modules permettent d’exécuter des commandes sur des hôtes distants, mais ils ne garantissent pas l’idempotence, une propriété clé pour maintenir la stabilité et la répétabilité des actions dans un environnement automatisé.

Le module raw est à utiliser en dernier recours, principalement sur des systèmes dépourvus de Python. Le module command est idéal pour des tâches simples et sûres, mais sans la flexibilité des commandes shell. Enfin, le module shell offre une grande liberté, avec la possibilité de manipuler des flux complexes et de chaîner des commandes, mais il introduit des risques accrus en termes de sécurité et d’absence d’idempotence.

En maîtrisant l’utilisation de raw, command, et shell, vous pourrez gérer des tâches spécifiques et complexes dans vos playbooks Ansible, tout en veillant à maintenir un environnement sécurisé et stable.