Le savoir n'a guère d'intérêt s'il n'est pas partagé.

Si vous avez déjà eu l'occasion de lancer un playbook Ansible, vous avez déjà sûrement dû remarquer, lors de son exécution, que la première tâche qui est réalisée par celui-ci n'est pas une tâche que vous lui avez ordonné. 🤦‍♂️

Mais alors, à quoi ça lui sert à Ansible de récupérer les informations de notre machine avant d'appliquer notre playbook?

Eh bien, c'est tout simple, ces informations sont récupérées, dans le but de pouvoir éventuellement être appelée dans notre playbook par la suite via des variables "magiques" ansible_*.

Je vous conseil, dans le cas où vous ne souhaitez justement pas utiliser des informations provenant des facts 😒, de déclarer la configuration suivante dans votre playbook:

---
- name: print out operating system
  hosts: all
  gather_facts: no

La tâche [Gathering Facts], qui ne fait à première vue rien, correspond en réalité à la connexion à la ou les différentes machines sur lesquelles seront exécutées les playbooks.

A noter que dans le cas où la connexion échoue, le playbook échouera sur cette cible, et continuera éventuellement sur les autres machines.

Enfin, si l'hôte répond, alors Ansible en profite pour récupérer un nombre incalculable d'informations en rapport direct avec cette/ces machine(s) et les stockents dans une variable citée plus haut.

Sinon, quelle tronche ça a ? 😎

Il est possible d'utiliser le module "setup", le même module qui est appelé lors de l'étape [Gathering facts], pour afficher les données récupérer!

Vous obtiendrez un magnifique retour au format JSON, ce qui n'est pas forcément très digeste. Mais avec jq, vous pouvez facilement traiter le résultat.

Pour l'exemple:

ansible vps -m setup
srv | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
...

J'ai pas mis la totalité par que 587 lignes... C'est encore moins digeste. Il y a une quantité d'informations disponible extrêmement impressionnante.

Certaines informations sont superbes pour alimenter sa CMDB.😉Vous n'avez plus d'excuse!

Et je fais quoi de tout ça ? 🤷‍♂️

Parce qu'il n'y a rien de mieux que l'explication par l'exemple, que je vais ainsi vous montrer différentes astuces utilisables pour récupérer des informations.

Quelle distrib' ? 🙌

# Récupérer des informmations sur la distrib'
ansible vps -m setup -a 'filter=ansible_distribution*'
srv | SUCCESS => {
    "ansible_facts": {
        "ansible_distribution": "Debian",
        "ansible_distribution_file_parsed": true,
        "ansible_distribution_file_path": "/etc/os-release",
        "ansible_distribution_file_variety": "Debian",
        "ansible_distribution_major_version": "10",
        "ansible_distribution_release": "buster",
        "ansible_distribution_version": "10",
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}

# Récupérer la famille de distrib'
ansible vps -m setup -a 'filter=ansible_os_family*'
srv | SUCCESS => {
    "ansible_facts": {
        "ansible_os_family": "Debian",
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}

Comme vous pouvez le constater, j'ai spécifier l'option -a pour pouvoir utiliser un filtrer sur le résultat (sans avoir à utiliser un utilitaire comme jq dans un pipe).

Les facts sont vraiment très puissant pour alimenter un inventaire de nos machines, qui plus est, à jour!

De beaux rapports HTML 😎

Dans certains cas, il peut être intéressant d'utiliser le système de template Jinja2 qu'intègre Ansible.

Par exemple, on peut obtenir le résultat suivant:

En ayant ainsi configuré la chose de la manière suivante:

    - name: "Generate HTML report"
      template:
        src: templates/html-report.html.j2
        dest: "$HOME/{{ inventory_hostname }}.html"
playbook.yml

Au niveau du template Jinja2:

<!DOCTYPE html>
<html lang=en>
    <head>
        <title>Integration report of {{ inventory_hostname }}</title>

        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        <!-- Material Design for Bootstrap fonts and icons -->
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons">

        <!-- Material Design for Bootstrap CSS -->
        <link rel="stylesheet" href="https://unpkg.com/bootstrap-material-design@4.1.1/dist/css/bootstrap-material-design.min.css" integrity="sha384-wXznGJNEXNG1NFsbm0ugrLFMQPWswR3lds2VeinahP8N0zJw9VWSopbjv2x7WCvX" crossorigin="anonymous">

        <!-- jQuery first, then Popper.js, then Bootstrap JS -->
        <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
        <script src="https://unpkg.com/popper.js@1.12.6/dist/umd/popper.js" integrity="sha384-fA23ZRQ3G/J53mElWqVJEGJzU0sTs+SvzG8fXVWP+kJQ1lwFAOkcUOysnlKJC33U" crossorigin="anonymous"></script>
        <script src="https://unpkg.com/bootstrap-material-design@4.1.1/dist/js/bootstrap-material-design.js" integrity="sha384-CauSuKpEqAFajSpkdjv3z9t8E7RlpJ1UP0lKM/+NdtSarroVKu069AlsRPKkFBz9" crossorigin="anonymous"></script>
        <script>$(document).ready(function() { $('body').bootstrapMaterialDesign(); });</script>
    </head>
    <body>
        <nav class="navbar navbar-dark bg-primary">
            <!-- Navbar content -->
            <a class="navbar-brand" href="#">Report of: <b>{{ inventory_hostname }}</b></a>
          </nav>
        <ul>
            <br>
            <li>Interfaces : {{ ansible_default_ipv4.alias }}</li>
            <li>Architecture : {{ ansible_architecture }}</li>
            <li>OS : {{ ansible_distribution }} {{ ansible_distribution_major_version }} {{ ansible_distribution_release }}</li>
            <li>Node name: {{ ansible_nodename }}</li>
        </ul>
    </body>
    <footer>
    </footer>
</html>
html-report.html.j2

Comme vous pouvez le constater, les facts sont super pratiques. On peut pousser le vis encore plus loins.

En effet, par exemple, dans le playbook suivant:

    - name: Specific configuration for Debian Only
      lineinfile:
        path: /etc/apt/apt.conf.d/50unattended-upgrades
        backup: yes
        create: yes
        state: present
        regexp: "{{ item.line }}"
        line: "{{ item.new }}"

      loop:
        - {
            line: '^(//)?      "o=Debian(,a=stable)?"',
            new: '      "o=Debian"',
          }
      when: ansible_distribution == 'Debian'
playbook.yml

Nous avons la configuration:

when: ansible_distribution == 'Debian'

Ainsi, nous pourrons donc appliquer la configuration que lorsque le fact ansible_distribution est à Debian. Ainsi, la configuration ne sera appliqué que lorsque la machine est une machine Debian (Nannnnn sans déconner...).

Vous voyez donc ainsi le nombre incalculable de possibilités avec ces facts.

Bien entendu, il est possible d'en faire bien plus avec tout ça, mais le but de cet article est d'expliquer simplement ce que sont les facts Ansible.

Tout ça avec des exemples, pour moi, la compréhension passe en grande partie par l'exemple.