IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Ansible pour gérer ses machines

Ansible est une plateforme permettant de gérer et d’automatiser la configuration des ordinateurs d’un parc informatique. Cet article présente l’outil et explique comment il fonctionne afin de l’utiliser pour vos systèmes et donne quelques exemples simples pour démarrer rapidement.

3 commentaires Donner une note à l´article (5)

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Ansible est un logiciel libre (licence GNU v3) permettant de déployer et lancer des tâches sur une partie ou l’ensemble d’un parc informatique. Grâce à Ansible, vous pourrez notamment installer un logiciel, mettre à jour des fichiers de configuration, configurer un service sur l’ensemble de vos machines et cela, de manière automatique et à distance. L’exploitation commerciale d‘Ansible est, depuis 2015, la propriété de Red Hat.
Même si l’outil est prévu pour un parc informatique, il peut être intéressant de l’utiliser chez soi, et ce, à partir d’un nombre restreint de machines. En effet, Ansible pourra alors permettre de dupliquer la configuration des logiciels ou même du système, sur ces machines. Par exemple, Ansible permet d’avoir une même configuration des outils sur deux machines utilisées pour du développement et de mettre à jour cette configuration sans pour autant effectuer les manipulations sur les machines en question.

Cet article n’a pas pour but de remplacer la documentation officielle (qui est d’ailleurs de bonne qualité).

I-A. Configuration de test

Pour l’élaboration de cet article, Ansible a été mis en place au sein d’un parc virtuel de trois machines sous Linux :

  • une machine sur laquelle Ansible est installé ;
  • deux machines recevant les ordres d‘Ansible.

Ces machines sont évidemment placées sur le même réseau Ethernet et peuvent donc communiquer entre elles.

II. Installation

Ansible est distribué sous la forme d’un paquet Python et s’installe donc avec la commande :

 
Sélectionnez
pip install ansible

Ansible doit uniquement être installé sur la machine de contrôle. Quant aux machines gérées par Ansible, elles ne nécessitent qu’une installation de Python pour exécuter le code généré par Ansible et un compte utilisateur auquel il est possible de se connecter au travers de SSH.

Cela correspond à un mode de fonctionnement dit « mode push » où les configurations sont envoyées par une machine de contrôle vers les machines cibles. C’est cette utilisation qui est décrite tout au long de l’article.

Ansible peut aussi être utilisé dans le mode de fonctionnement dit « mode pull » (donc, à l’inverse du « mode push »). Dans ce deuxième mode de fonctionnement, Ansible est installé directement sur les machines cibles (il n’y a pas de machine de contrôle). Les fichiers de configuration d‘Ansible devront alors être récupérés sur la machine à configurer (par exemple, en les récupérant depuis un dépôt Git) et appliqués, grâce à Ansible, localement.

Quel que soit le mode que vous utilisez, les fichiers de configuration et les commandes sont identiques. Seule la machine cible à indiquer à Ansible change (localhost, pour indiquer la machine locale, en « mode pull »).

II-A. Configuration d’un nœud Windows

Ansible peut aussi se connecter aux machines fonctionnant sous Windows. Toutefois, une machine sous Windows requiert une configuration spécifique. Premièrement, Ansible peut utiliser le protocole SSH ou le protocole WinRM pour la connexion. Dans le premier cas, un serveur SSH peut être installé sous Windows par le biais des « Fonctionnalités facultatives » présentes dans les paramètres du système.

Le service « OpenSSH SSH Server » n’est pas démarré automatiquement après installation. Il est donc nécessaire de le démarrer manuellement et si nécessaire, d’activer le démarrage automatique (par exemple, depuis le « Gestionnaire des services ».

De plus, si votre compte utilisateur n’a pas de mot de passe, il ne sera pas possible de s’y connecter à travers SSH.

WinRM peut s’activer avec la commande winrm quickconfig. Cette commande mettra en place une configuration par défaut, suffisante pour une utilisation avec Ansible, mais à modifier pour des questions de sécurité.

Que ce soit avec SSH ou WinRM, Il sera certainement nécessaire de mettre à jour les règles du pare-feu Windows pour permettre aux machines externes de se connecter à ces services.

Sur la machine de contrôle, il sera possible de spécifier le protocole, ssh ou winrm, à travers la variable ansible_connection, notamment, depuis l’inventaire. Le support de WinRM nécessite l’installation du paquet Python pywinrm.

Finalement, l’utilisation de WinRM repose sur les variables suivantes :

  • ansible_port: 5985 pour changer le port utilisé par Ansible. En effet, il est possible que par défaut, Ansible utilise le port 5986 en considérant que la connexion sera sécurisée (HTTPS), alors que la configuration par défaut de WinRM utilise le port 5985 (HTTP) ;
  • ansible_winrm_transport: ntlm pour définir la méthode d’authentification. Il est possible de choisir parmi basic, ntlm, kerberos, certificate et credssp. Par contre, basic et ntlm n’offrent pas de chiffrement et sont donc à éviter en HTTP.

III. Fonctionnement

Ansible vient avec son propre vocabulaire. Ainsi, vous serez rapidement confronté aux mots suivants :

  • machine de contrôle : la machine qui exécute Ansible et qui va envoyer des ordres aux autres machines ;
  • machine gérée : une machine gérée par Ansible, qui va donc exécuter les tâches envoyées par Ansible. En « mode pull », la machine gérée est aussi la machine de contrôle ;
  • inventaire (« inventory ») : la liste de machines gérées par Ansible ;
  • playbook : un recueil de tâches à effectuer ;

Le parc informatique devant être géré par Ansible doit être décrit dans un fichier (pouvant être au format INI ou YAML) nommé inventaire (« inventory »). Les tâches à effectuer sur les machines du parc sont décrites dans un fichier (au format YAML) appelé « playbook ». Grâce à ses informations, Ansible se connectera donc aux machines mentionnées dans l’inventaire, grâce à SSH, pour lancer les tâches voulues.

IV. Premier pas

Pour débuter avec Ansible, la première étape sera de créer un inventaire puis une tâche simple à exécuter. Le rôle de cette première tâche sera de joindre et d’obtenir une réponse des machines gérées par Ansible. Le but est multiple :

  • créer un premier inventaire ;
  • créer une première tâche ;
  • vérifier que la connexion entre les machines, spécifiquement entre la machine de contrôle et les machines gérées est fonctionnelle ;

IV-A. Inventaire

Dans sa forme la plus simple, l’inventaire est une simple liste contenant l’adresse IP ou le nom de la machine. Au format INI :

inventory.ini
Sélectionnez
[group1]
192.168.100.20
192.168.100.21

Ou, son équivalent au format YAML :

inventory.yml
Sélectionnez
group1:
  hosts:
    computer1:
      ansible_host: 192.168.100.20
    computer2:
      ansible_host: 192.168.100.21

Bien que les deux formats, INI et YAML, offrent les mêmes possibilités, le format YAML sera probablement privilégié en raison de sa structure plus facile à manipuler pour les parcs informatiques composés de nombreuses machines.

Dans les exemples ci-dessus, le nom du groupe (ici group1) peut être modifié pour mieux correspondre à votre objectif. Il en est de même pour le nom des machines (ici computer1 et computer2). De plus, il est possible de définir plusieurs groupes dans un même inventaire.

IV-A-1. Test

Déjà, il est possible de vérifier si l’inventaire ne contient pas d’erreur de syntaxe avec la commande :

 
Sélectionnez
ansible-inventory -i inventory.ini --list

Et de tester la connexion avec les machines listées avec la commande :

 
Sélectionnez
ansible group1 -m ping -i inventory.ini

Le premier paramètre (group1) est l’ensemble de machines (ici, un nom de groupe) sur lequel effectué la tâche. La tâche en question est définie par le module ping (option -m). L’option -i permet d’indiquer quel inventaire utiliser et pour que cette commande fonctionne, cet inventaire doit définir le group1.

Les commandes ci-dessus utilisent l’inventaire au format INI mais auraient très bien pu utiliser le fichier inventory.yml au format YAML.

Lorsque les machines sont joignables par le contrôleur, le message suivant est affiché :

 
Sélectionnez
computer1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3.12"
    },
    "changed": false,
    "ping": "pong"
}
computer2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3.12"
    },
    "changed": false,
    "ping": "pong"
}

Dans le cas où aucun serveur SSH n’est actif sur la machine gérée, le message suivant apparaîtra :

 
Sélectionnez
computer1 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.100.20 port 22: Connection refused",
    "unreachable": true
}

Et si la connexion n’est pas automatique (si l’utilisateur doit entrer un mot de passe pour se connecter), le message suivant apparaîtra :

 
Sélectionnez
computer2 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: alexandre@192.168.100.21: Permission denied (publickey,password).",
    "unreachable": true
}

Finalement, si Python n’est pas trouvé sur la machine, vous obtiendrez le message suivant :

 
Sélectionnez
computer2 | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "module_stderr": "Shared connection to 192.168.100.21 closed.\r\n",
    "module_stdout": "/bin/sh: line 1: /usr/bin/python3: No such file or directory\r\n",
    "msg": "The module failed to execute correctly, you probably need to set the interpreter.\nSee stdout/stderr for the exact error",
    "rc": 127
}

Si un message similaire (toutefois avec un résultat dans module_stderr différent) apparaît pour une machine Windows, vérifiez que votre playbook utilise les modules adéquats, spécifiques à Windows. Dans l’exemple précédent, il sera nécessaire d’utiliser win_ping à la place de ping. En effet, les modules pour Linux et autres vont aussi produire un tel message d’erreur pour les machines Windows.

Avec cette première étape, il est possible de constater que :

  • la connexion SSH s’effectue avec le nom d’utilisateur ayant lancé la commande Ansible : un utilisateur différent peut être renseigné dans l’inventaire (variable ansible_ssh_user) ;
  • le premier argument pour Ansible est l’ensemble de machines sur lesquelles la tâche doit être exécutée. Cet argument peut être une machine unique, un ensemble ou une expression plus complexe constituée d’opérateurs binaires ou même de variables, comme l’indique la documentation.

IV-B. Tâche

Le test de l’étape précédente repose sur le module « ping » d’Ansible, ce qui est très bien pour vérifier le bon fonctionnement de l’installation. Toutefois, il deviendra vite intéressant de mettre en place des tâches personnalisées à exécuter sur les machines gérées. Cette configuration se fait au travers d’un « playbook » (au format YAML) : un fichier contenant un ensemble de tâches. Le fichier suivant est un exemple simple dans lequel deux tâches sont configurées : un ping et l’affichage d’un message de débogage (« debug ») « Bonjour monde » :

ping.yml
Sélectionnez
- name: Hello World
  hosts: group1
  tasks:
   - name: Ping my hosts
     ansible.builtin.ping:

   # Une deuxième tâche:
   - name: Print message
     ansible.builtin.debug:
      msg: Bonjour monde

Pour exécuter le playbook, il faudra utiliser la commande ansible-playbook, comme suit :

 
Sélectionnez
ansible-playbook -i ./inventory.yml ./ping-playbook.yml

Ce qui permettra d’obtenir la sortie suivante :

 
Sélectionnez
PLAY [Hello World] *************************************************************

TASK [Gathering Facts] *********************************************************
ok: [computer1]
ok: [computer2]

TASK [Ping my hosts] ***********************************************************
ok: [computer1]
ok: [computer2]

TASK [Print message] ***********************************************************
ok: [computer1] => {
    "msg": "Bonjour monde"
}
ok: [computer2] => {
    "msg": "Bonjour monde"
}

Aussi, ce deuxième exemple montre les points suivants :

  • un playbook peut contenir plusieurs tâches ;
  • les tâches sont exécutées dans l’ordre, de haut en bas (ce comportement est modifiable à travers les stratégies) ;
  • la liste des machines sur lesquelles exécuter les tâches est déterminée dans le playbook (champ hosts).

La tâche « Gathering Facts » est une tâche d’Ansible effectuée par défaut (pouvant être désactivée). Celle-ci va récupérer un ensemble d’informations sur la machine cible. Ces informations seront alors disponibles au travers de la variable ansible_factsVariables.

IV-C. Les modules Ansible

Maintenant qu‘Ansible est en place, que l’inventaire est prêt et qu’il est possible d’exécuter des tâches, voyons comment mettre en place des tâches utiles permettant de modifier la configuration des machines gérées.
Pour la première tâche, nous avons utilisé deux modules :

Ansible propose une multitude de modules, listés sur cette page. Afin de s’habituer à la mise en place de playbooks, quelques modules sont décrits ci-dessous.

Les exemples suivants se veulent simples. Chaque module décrit dans cet article, possède de nombreuses options permettant de couvrir la majorité des cas. Toutefois, ces options ne sont pas mentionnées ici et il est donc nécessaire de se référer à la documentation officielle.

IV-C-1. Copier un fichier

Vous souhaitez envoyer un fichier à toutes les machines d’un groupe, pour ce faire, votre tâche pourra utiliser le module ansible.builtin.copy (et win_copy pour les machines Windows).

copy-file.yml
Sélectionnez
- name: Copy some files
  hosts: group1
  tasks:
   - name: Copy a file in tmp
     ansible.builtin.copy:
      src: /home/alexandre/fichier
      dest: /tmp/fichier

Le module possède des options pour :

  • indiquer le contenu du fichier à envoyer, directement dans le playbook (content) ;
  • définir des droits, un propriétaire et un groupe particuliers (owner, group, mode) ;
  • utiliser une somme de contrôle pour vérifier le succès de la copie (checksum) ;
  • …

Par contre, de la sorte, il ne sera pas possible d’écrire dans un dossier d’un autre utilisateur, ou plus précisément, un dossier auquel seul le compte administrateur a accès, comme cela est majoritairement le cas pour les fichiers de configuration d’une machine.

IV-C-1-a. Obtenir les droits administrateur

Rapidement, il sera nécessaire d’exécuter des tâches en tant qu’administrateur (de la machine gérée). Pour cela, Ansible propose les commandes become, become_user, become_method et become_flags. Le playbook précédent deviendra :

copy-file-root.yml
Sélectionnez
- name: Copy some files
  hosts: group1
  tasks:
   - name: Copy a file
     ansible.builtin.copy:
      src: /home/alexandre/stamp
      dest: /etc/stamp
     become: yes

Mais avec une telle solution (et en utilisant l’option --check pour jouer le playbook), Ansible indiquera qu’il ne sait pas comment devenir administrateur :

 
Sélectionnez
TASK [Copy a file in tmp] ******************************************************
fatal: [computer2]: FAILED! => {"msg": "Missing sudo password"}

Il est possible de donner le mot de passe, et ce, de plusieurs manières :

  • directement dans le fichier YAML ;
  • avec l’option -K de l’ansible-playbook, Ansible demandera le mot de passe dans le terminal ;
  • de le mettre dans un fichier annexe ;
  • de le renseigner au travers d’une variable d’environnement.

Bien que fonctionnelles, la sécurité de ces méthodes peut être discutée. Heureusement, Ansible propose une méthode pour stocker des informations de manière sécurisée : les coffres-forts.

IV-C-1-a-i. Coffre-fort

Ansible propose une solution au problème du stockage de mot de passe, d’information ou de fichier sensible sous la forme d’un coffre-fort (« vault » en anglais). Pour gérer le coffre-fort, Ansible propose une commande dédiée : ansible-vault.
La création d’un coffre-fort se fait ainsi :

 
Sélectionnez
ansible-vault create root_passwords.yaml

Une fois un mot de passe entré, la commande ouvre automatiquement le fichier en édition, dans vi. Dans ce fichier, vous pourrez renseigner une variable contenant le mot de passe. Pour l’exemple de copie d’un fichier, nécessitant une élévation de privilège demandé au travers du mot clef become, il suffit de définir le mot de passe dans la variable standard dédiée à cet usage :

 
Sélectionnez
ansible_become_password: MotDePasse

Le playbook peut alors être modifié pour charger le coffre-fort :

copy-file-root-2.yml
Sélectionnez
- name: Copy some files
  hosts: group1
  vars_files:
    - root_passwords.yaml
  tasks:
   - name: Copy a file in tmp
     ansible.builtin.copy:
      src: /home/alexandre/stamp
      dest: /etc/stamp
     become: yes

Et si on lance le playbook, on obtient immédiatement la réponse suivante :

 
Sélectionnez
ERROR! Attempting to decrypt but no vault secrets found

Dorénavant, il faut utiliser l’option --ask-vault-pass lorsque l’on veut lancer le playbook :

 
Sélectionnez
ansible-playbook --ask-vault-pass -i ./inventory.yml ./tasks/copy_file_root-2.yml

Le mot de passe du coffre-fort peut être stocké dans un autre fichier ou encore, dans un gestionnaire de secrets.

La commande ansible-vault permet aussi de lire, éditer, déchiffrer et re-chiffrer un coffre-fort.

IV-C-2. Récupérer un fichier depuis les machines gérées

Le module fetch permet d’effectuer l’opération inverse de la commande copy vue précédemment : grâce à fetch, vous pouvez récupérer des fichiers depuis les nœuds gérés.

fetch_file.yml
Sélectionnez
- name: Fetch file
  hosts: group1
  tasks:
   - name: Fetch fstab
     ansible.builtin.fetch:
      src: /etc/fstab
      dest: /tmp/out

Le fichier fstab sera alors stocké, sur la machine de contrôle, à l’emplacement /tmp/out/computer1/etc/fstab (pour la machine nommée computer1).

Le module fetch dispose d’une option flat, désactivée par défaut, permettant de stocker le fichier directement dans le fichier ou le dossier spécifié :

fetch_file_flat.yaml
Sélectionnez
- name: Fetch file
  hosts: group1
  tasks:
   - name: Fetch fstab
     ansible.builtin.fetch:
      src: /etc/fstab
      dest: /tmp/out/
      flat: yes

Par conséquent, chaque fois qu’un fichier est récupéré d’une machine, le fichier est écrasé par le nouveau. Par conséquent, cette option n’est utile que lorsque vous souhaitez récupérer un fichier à partir d’une seule machine.

IV-C-3. Télécharger un fichier

Pour télécharger un fichier, il faudra utiliser le module get_url (et win_get_url pour les machines Windows) :

get_file.yml
Sélectionnez
- name: Get file hosted online
  hosts: group1
  tasks:
   - name: Get some good hosts
     ansible.builtin.get_url:
      url: https://sebsauvage.net/hosts/hosts
      dest: /tmp/hosts

Entre autres, ce module dispose d’une option (cheksum) pour vérifier la somme de contrôle du fichier téléchargé.

IV-C-4. Cloner un dépôt Git

Le module git permet récupérer des fichiers depuis Git. Il existe une commande équivalente pour hg et pour subversion.

clone_godot.yml
Sélectionnez
- name: Get godot
  hosts: group1
  tasks:
   - name: Get latest godot sources
     ansible.builtin.git:
      repo: https://github.com/godotengine/godot.git
      dest: /tmp/godot

IV-C-5. Exécuter une commande quelconque

De manière plus générale, il est possible d’exécuter une commande quelconque avec le module command (win_command sous Windows) ou encore un script, avec le module script (le script est local et sera transféré sur le nœud). En plus du module command, il existe un module shell (et win_shell), reprenant le même principe que command mais disposant des spécificités d’un shell (c’est-à-dire, les variables telles que $HOME ou les redirections). Finalement, il existe le module raw, une variante bas niveau des commandes précédentes permettant d’exécuter une commande à travers SSH. L’avantage de cette dernière étant qu’il est possible de l’utiliser pour installer Python ou pour automatiser des tâches sur une machine sans Python. Toutefois, son utilisation doit être évitée, autant que possible.

curl_google.yml
Sélectionnez
- name: Fetch Google
  hosts: group1
  tasks:
   - name: Make request
     ansible.builtin.command: curl www.google.com --output /tmp/website.html

V. Variables

Ansible permet de créer des variables. Celles-ci peuvent être définies au travers d’un playbook, d’un inventaire, en les passant en option lors du lancement d’Ansible ou encore, dans un fichier à inclure (comme vu avec les coffre-forts). Une tâche peut aussi produire une nouvelle variable (en utilisant le mot clé register). La documentation indique dans la section « return values », les différentes variables produites par un module. Voici un exemple d’utilisation de register :

Register_vars.yml
Sélectionnez
- name: Récupération d’un fichier en ligne
  hosts: group1
  tasks:
   - name: Récupération d’un fichier hosts
     ansible.builtin.get_url:
      url: https://sebsauvage.net/hosts/hosts
      dest: /tmp/hosts
     register: get_hosts

   # Utilisation de la variable précédemment créée
   - name: Debug
     ansible.builtin.debug:
      msg: "Taille : {{ get_hosts.size }}"

Le nom d’une variable est sujet à quelques restrictions : le nom ne peut contenir que des lettres, des chiffres et des soulignés. Plus précisément, le nom d’une variable ne peut pas commencer par un chiffre et ne peut pas être un mot clef du langage Python ou des playbooks.

Voici un autre exemple, où la variable est définie dans le playbook :

playbook_var.yml
Sélectionnez
- name: variable test in playbook
  hosts: group1
  vars:
    my_var: "Developpez.com"
  tasks:
   - name: Simple variable test
     ansible.builtin.debug:
      msg: "Hello {{ my_var }}"

La même chose peut être réalisée, mais en définissant la variable dans l’inventaire :

inventory_var.yaml
Sélectionnez
group1:
  hosts:
    computer1:
      ansible_host: 192.168.100.20
      my_var: "Developpez.com"
    computer2:
      ansible_host: 192.168.100.21
      my_var: "Developpez.net"

Ce qui permet d’avoir une variable spécifique à la machine l’exécutant.

Si la variable n’est définie que pour une machine et non pour l’autre, la tâche s’exécutera correctement pour la machine pour laquelle la variable est définie, mais échouera pour l’autre.

Finalement, la variable peut être définie lors de l’exécution de la commande avec l’option -e ou --extra-vars. Les différentes variables doivent être séparées par une espace :

 
Sélectionnez
ansible-playbook -e "my_var=World" -i ../inventory.yaml ./var-test.yaml

Par conséquent, il est possible que votre variable soit définie à deux endroits différents. La documentation officielle indique l’ordre de précédence.

Les variables peuvent être de différents types, comme des booléens, des listes et des dictionnaires. Avec la présence des listes ou des dictionnaires, il peut vite devenir utile d’itérer sur les différents éléments de ceux-ci, ce qu‘Ansible propose avec le mot clef loop. Ce dernier inscrit l’élément de l’itération en cours dans la variable item :

loop.yml
Sélectionnez
- name: Test de boucle
  hosts: group1
  vars:
    a_list:
     - "Developpez.com"
     - "Developpez.net"
  tasks:
   - name: Say hi
     ansible.builtin.debug:
      msg: "Hello {{ item }}"
     loop: "{{ a_list }}"

Ce qui affiche :

 
Sélectionnez
TASK [Say hi] ******************************************************************
ok: [computer1] => (item=Developpez.com) => {
    "msg": "Hello Developpez.com"
}
ok: [computer1] => (item=Developpez.net) => {
    "msg": "Hello Developpez.net"
}
ok: [computer2] => (item=Developpez.com) => {
    "msg": "Hello Developpez.com"
}
ok: [computer2] => (item=Developpez.net) => {
    "msg": "Hello Developpez.net"
}

Ansible fournit un ensemble de variables, appelées « faits » (facts), qu’il est possible de consulter de deux manières différentes :

  • avec une ligne de commande :

     
    Sélectionnez
    ansible -m setup group1 -i ../inventory.yaml
  • avec un playbook :
facts.yml
Sélectionnez
- name: Debug
  hosts: group1
  tasks:
   - name: Afficher les variables
     ansible.builtin.debug:
      var: ansible_facts

Ansible propose aussi le mot clef when permettant d’ajouter une condition à l’exécution d’une tâche. La tâche suivante ne s’exécute que sur les machines non Windows :

condition.yml
Sélectionnez
- name: Test de condition
  hosts: group1
  tasks:
   - name: Tâche pour Linux, mais pas Windows
     ansible.builtin.debug:
      msg: "Hello Linux"
     when: ansible_facts['os_family'] != "Windows"

VI. Template

Les template permettent d’utiliser un fichier modèle pour générer un fichier sur les machines gérées. En effet, les variables utilisées dans le modèle seront remplacées par leur valeur telle que connue par Ansible. Ce mécanisme repose sur un langage spécifique : Jinja2. De plus, pour transformer un fichier modèle vers son état final, il est nécessaire d’utiliser le module Ansible template (et win_template pour Windows), qui, de manière globale, possède les mêmes fonctionnalités que le module copyCopier un fichier.

Avec le fichier Jinja 2 suivant :

motd.j2
Sélectionnez
Welcome on {{ machine_name }}

Et ce playbook :

template_motd.yml
Sélectionnez
- name: Send customized motd
  hosts: group1
  tasks:
   - name: Greetzs
     ansible.builtin.template:
      src: motd.j2
      dest: /tmp/motd

Vous obtiendrez, sur les machines gérées, le fichier suivant :

 
Sélectionnez
Welcome on Ordinateur 2

lorsque la variable machine_name est définie à « Ordinateur 2 ». Dans le cas présent, une telle variable serait certainement définie pour chaque machine de l’inventaire.

Le module template dispose d’une option validate permettant de lancer une commande vérifiant la validité du fichier créé avant d’effectuer la copie sur la machine de destination. Cela permet d’éviter de bloquer la machine gérée lors de la mise à jour des fichiers de configuration de cette dernière (par exemple, le fichier sudoers).

La documentation officielle de Jinja2 permet d’en apprendre plus sur les possibilités de ce langage.

VII. Handler

Ansible propose un mécanisme, sous le nom de « handler », permettant d’effectuer une tâche uniquement si la machine gérée a été modifiée (état « changed »). Plus précisément, un « handler » est une tâche placée dans une section dédiée : handlers. Pour que le « handler » soit exécuté, une tâche classique doit en faire la demande avec le mot clef notify. Voici un exemple, reprenant le playbook pour récupérer un fichier en ligne :

get_file_with_handler.yml
Sélectionnez
- name: Get file hosted online
  hosts: group1
  tasks:
   - name: Get some good hosts
     ansible.builtin.get_url:
      url: https://sebsauvage.net/hosts/hosts
      dest: /tmp/hosts
     notify:
      - Debug handler

  handlers:
    - name: Debug handler
      ansible.builtin.debug:
        msg: "File successfully downloaded"

Une telle tâche pourra être à l’origine de la sortie suivante :

 
Sélectionnez
PLAY [Get file hosted online] **************************************************

TASK [Gathering Facts] *********************************************************
ok: [computer1]
ok: [computer2]

TASK [Get some good hosts] *****************************************************
ok: [computer2]
changed: [computer1]

RUNNING HANDLER [Debug handler] ************************************************
ok: [computer1] => {
    "msg": "File successfully downloaded"
}

PLAY RECAP *********************************************************************
computer1                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
computer2                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Ici, seul « computer1 » a exécuté le « handler ». En effet, sur « computer2 », le fichier à récupérer était déjà présent, faisant qu’aucun changement n’a été effectué sur cette machine. Par conséquent, le « handler » n’a pas été exécuté.
Un cas classique d’utilisation des « handlers » est de redémarrer un service après la modification d’un fichier de configuration.

Ansible permet :

  • de notifier plusieurs « handlers » en une fois ;
  • d’écrire plusieurs « handlers » dans un même playbook ;
  • d’exécuter un « handler » à des moments donnés et non après l’exécution de toutes les tâches du playbook ;
  • de forcer une tâche à indiquer un état « changed » avec changed_when.

VIII. Rôles

Les rôles d‘Ansible sont des ensembles de fichiers pouvant définir des tâches, des « handlers », des variables, des templates ou des fichiers quelconques. L’objectif est de mettre en place un ensemble de fichiers pouvant être utilisés dans une ou plusieurs tâches.

Les rôles se définissent au travers de la hiérarchie de fichiers suivante :

 
Sélectionnez
roles/
    common/             
        tasks/          
            main.yml    
        handlers/       
            main.yml    
        templates/      
            ntp.conf.j2 
        files/          
            bar.txt     
            foo.sh      
        vars/           
            main.yml    
        defaults/       
            main.yml    
        meta/           
            main.yml    
        library/        
        module_utils/   
        exemple_plugins/

Dans une telle hiérarchie, le nom du rôle est défini par le nom du dossier dans le dossier roles, ici :common. Ansible cherchera un fichier main.yml dans chacun des sous dossiers. Les sous dossiers d’un rôle sont les suivants :

  • tasks : les tâches définies par ce rôle ;
  • handlers : les « handlers » définis par ce rôle ;
  • templates : les fichiers de modèles que ce rôle utilise ;
  • files : les fichiers quelconques que ce rôle utilise ;
  • vars : les variables définies par ce rôle ;
  • defaults : d’autres variables, ayant une priorité faible, définies par ce rôle ;
  • meta : permet d’appeler un autre rôle lors de l’inclusion de ce rôle (telle une dépendance). Peut aussi contenir des méta-données pour GalaxyGalaxy ;
  • library : des modules personnalisés ;
  • module_utils : du code supplémentaire pour les modules ;
  • exemple_plugins : pour stocker des données spécifiques aux extensions, ici appelées « exemple ».

Avec la particularité que tout dossier est optionnel et peut-être absent si vous n’en avez pas besoin. Un rôle est valide s’il contient au moins un de ces dossiers.

Ansible cherche un fichier main.yml, dans les sous-dossiers d’un rôle. Il est toujours possible d’inclure d’autres fichiers, par exemple, un deuxième fichier de tâches, à partir du fichier main.yml.

Les rôles doivent être placés dans le dossier du playbook, ou un chemin relatif à celui-ci ou dans le dossier défini par roles_path. Il est aussi possible, lors de l’utilisation d’un rôle de spécifier un chemin complet.

Ci-dessous, un exemple d’un rôle, appelé test et définissant un unique fichier tasks/main.yml :

tasks/main.yml
Sélectionnez
- name: Une tâche sans intérêt
  ansible.builtin.debug:
   msg: "Hello"

Et un playbook utilisant ce rôle :

test-role.yml
Sélectionnez
- name: Role test
  hosts: group1
  roles:
   - role: test
  tasks:
   - name: Récupération d’un bon fichier hosts
     ansible.builtin.get_url:
      url: https://sebsauvage.net/hosts/hosts
      dest: /tmp/hosts

La tâche définie par le rôle est exécutée avant la tâche du playbook. Il est à noter que le playbook aurait aussi pu uniquement contenir :

test-role-2.yml
Sélectionnez
- name: Role test
  hosts: group1
  roles:
   - role: test

Exécutant uniquement la ou les tâches définies dans le rôle.

VIII-A. Galaxy

En plus des modules proposés avec Ansible, il est possible d’obtenir des collections et des rôles provenant de la communauté et disponible sur le site Galaxy. En complément de l’interface Web, il est possible d’explorer Galaxy avec la commande ansible-galaxy.
La commande est aussi utile pour l’installation d’un rôle trouvé sur Galaxy :

 
Sélectionnez
ansible-galaxy role install namespace.role_name

IX. Mise en pratique

Maintenant que les fonctionnalités principales d‘Ansible ont été expliquées, il est temps de mettre en place des tâches utiles.

La création d’une tâche suit généralement le processus suivant :

  • expérimentation manuelle pour déterminer les étapes nécessaires de la tâche ;
  • lecture de la documentation afin de trouver les modules adéquats pour réaliser les étapes ;
  • écriture du playbook et des fichiers associés en se reposant sur la page de documentation dédiée à chaque module ;
  • vérification du playbookVérification d’un playbook ;
  • test du playbook sur une machine virtuelle de test. Cela est d’autant plus facile sachant que la machine virtuelle peut figurer dans un groupe spécifique de l’inventaire ;
  • déploiement.

IX-A. Configuration de git

La tâche suivante va créer un nouveau compte utilisateur et configurer Git de façon à ce que le nom de l’utilisateur et son adresse de courriel soient définis.
En bref, la tâche effectue les actions suivantes :

  • création d’un nouvel utilisateur avec le module user ;
  • mise en place d’une configuration de Git spécifique à l’utilisateur avec le module git_config ;
  • clonage d’un dépôt Git avec le module git.

Pour que la tâche soit plus intéressante, elle repose sur une liste d’utilisateurs dans un fichier de configuration, tel que le suivant :

users.yml
Sélectionnez
utilisateurs:
  - utilisateur:
      login: "utilisateur1"
      username: "Developpez Com"
      email: "utilisateur1@example.com"
  - utilisateur:
      login: "utilisateur2"
      username: "Developpez Net"
      email: "utilisateur2@example.com"

Il est aussi possible d’utiliser un fichier CSV, grâce au module read_csv.

Par contre, dans un vrai environnement, il est étonnant de configurer deux développeurs sur la même machine. Une autre façon de faire serait de définir ces variables dans l’inventaire.

La tâche peut être rédigée ainsi :

setup-git-user.yml
Sélectionnez
- name: Configure un nouvel utilisateur et prépare sa configuration Git
  hosts: group1
  vars_files:
   - users.yml
   - root_passwords.yml
  tasks:
   - name: Création de l’utilisateur
     ansible.builtin.user:
       name: "{{ item.utilisateur.login }}"
       # group: developers
       # password: https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#how-do-i-generate-encrypted-passwords-for-the-user-module
     loop: "{{ utilisateurs }}"
     become: yes

   - name: Configuration du username Git
     ansible.builtin.git_config:
       name: utilisateur.name
       scope: global
       value: "{{ item.utilisateur.username }}"
     loop: "{{ utilisateurs }}"
     become: yes
     become_user: "{{ item.utilisateur.login }}"

   - name: Configuration de l’email Git
     ansible.builtin.git_config:
       name: utilisateur.email
       scope: global
       value: "{{ item.utilisateur.email }}"
     loop: "{{ utilisateurs }}"
     become: yes
     become_user: "{{ item.utilisateur.login }}"


   - name: Récupération des sources de Godot
     ansible.builtin.git:
      repo: https://github.com/godotengine/godot.git
      dest: ~/godot
     loop: "{{ utilisateurs }}"
     become: yes
     become_user: "{{ item.utilisateur.login }}"

IX-B. Installation et configuration d’un logiciel

La tâche ci-dessous installe et configure automatiquement le logiciel fzf. Le playbook utilise les conditions pour rendre le processus compatible avec différentes distributions Linux et différents shells. Cette fois, le mot de passe administrateur est demandé par Ansible au lancement du playbook (grâce à vars_prompt). Pour activer fzf, il est nécessaire d’ajouter une ligne dans le fichier de configuration du shell. La mise à jour de ce fichier de configuration est réalisée par le module lineinfile, qui permet de chercher si une ligne est déjà présente dans le fichier (option search_string) et de l’ajouter dans le cas contraire (option line).

install-fzf.yml
Cacher/Afficher le codeSélectionnez

Il est possible d’améliorer le processus. Notamment, il serait judicieux d’avoir un rôle spécifique à la mise à jour du système et qui serait appelé au lancement du playbook ci-dessus. En effet, il n’est pas toujours nécessaire de faire la mise à jour du système à chaque lancement du playbook, ou inversement, il serait intéressant d’avoir un playbook pour uniquement mettre à jour le système.

IX-C. Déploiement de point de montage réseau

Le scénario suivant a pour but de mettre en place un point de montage réseau (par exemple, pour se connecter automatiquement à un NAS) grâce à systemd automount.

Pour cela, le module systemd sera utile, notamment pour charger la nouvelle configuration à travers un handlerHandler, ainsi que les commandes template et copy pour envoyer des fichiers sur les machines cibles.

Les fichiers de configuration à envoyer seront :

mnt-server.mount
Sélectionnez
[Install]
WantedBy=multi-user.target
[Unit]
Description=SMB Server

[Mount]
What=//mon-server.example.com/server
Where=/mnt/server
Options=_netdev,credentials=/root/smb.pass,x-systemd.automount,uid=1000,gid=1000
Type=cifs
TimeoutSec=300
mnt-server.automount
Sélectionnez
[Automount]
Where=/mnt/server

Ainsi qu’un troisième fichier devant être préparé par Ansible et donc au format Jinja 2.

smb.pass.j2
Sélectionnez
username={{ username }}
password={{ server_password }}
domain=WORKGROUP

Et la tâche en elle-même :

smb-server-automount.yml
Sélectionnez
- name: Configuration d’un automount systemd
  hosts: group1
  vars:
    username: "developpez"
    server_password: "super_mot_de_passe"
  vars_files:
   - root_passwords.yml
  tasks:
   - name: Copie des informations de connexion
     ansible.builtin.template:
       src: ./smb.pass
       dest: /root/smb.pass
     become: yes

   - name: Copie des fichiers automount
     ansible.builtin.copy:
       src: ./mnt-server.mount
       dest: /etc/systemd/system/mnt-server.mount
     become: yes

   - name:  Copie des fichiers automount
     ansible.builtin.copy:
       src: ./mnt-server.automount
       dest: /etc/systemd/system/mnt-server.automount
     become: yes

   - name: Activation au démarrage de la machine
     ansible.builtin.systemd:
       daemon_reload: yes
       name: mnt-server.automount
       enabled: yes
     become: yes

   - name: Montage
     ansible.builtin.systemd:
       name: mnt-server.automount
       state: started
     become: yes

Dans cet exemple, Ansible configure un point de montage automatique sur les machines cibles. Le processus (avec les modules template et copy) est le même pour la configuration d’une multitude de logiciels ou même, du système. Généralement, la configuration de ceux-ci est stockée dans des fichiers.

X. Astuces

X-A. Vérification d’un playbook

La commande ansible-playbook dispose d’une option --check permettant de vérifier ce qui va être réalisé lors de l’exécution d’un playbook (sans pour autant effectuer les tâches en question) :

 
Sélectionnez
ansible-playbook --check -i ./inventory.yaml ./ping.yaml

Cette option est donc un bon outil pour vérifier que les tâches effectueront ce qui est demandé ou encore, pour déboguer un playbook ne fonctionnant pas comme attendu.

En plus de l’option --check, ansible-playbook propose aussi les options --diff, --list-hosts, --list-tasks et --syntax-check.

X-B. Linter

De plus, Ansible propose un outil, ansible-lint, vérifiant la syntaxe et des erreurs communes lors de l’écriture d’un playbook.
Celui-ci n’est pas installé par défaut avec Ansible. La commande suivante permet de l’installer :

 
Sélectionnez
pip install ansible-lint

L’outil s’exécute grâce à la commande suivante :

 
Sélectionnez
ansible-lint ./ping.yaml

Ce qui pourra donner la sortie suivante :

 
Sélectionnez
WARNING  Listing 1 violation(s) that are fatal
yaml[indentation]: Wrong indentation: expected 6 but found 8
ping.yaml:9

Read documentation for instructions on how to ignore specific rule violations.

# Rule Violation Summary

  1 yaml profile:basic tags:formatting,yaml

Failed: 1 failure(s), 0 warning(s) on 1 files. Last profile that met the validation criteria was 'min'.

XI. Conclusion

Ansible est un outil puissant, utile pour gérer un parc informatique, mais pouvant aussi être pratique dans un environnement plus modeste, tel que celui à la maison.
Cet article a permis de découvrir les différentes fonctionnalités d’Ansible et détaille la mise en place de tâches simples. Libre à vous de réutiliser ces connaissances et d’étendre les différents playbooks afin de gérer la configuration de vos machines.

XII. Remerciements

Merci à Franck Talbart et Christophe pour leurs suggestions. Merci également à ALT pour sa relecture orthographique.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2025 Alexandre Laurent. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.