From 1987c5791c9c52aacce2132951d794a53dc2a324 Mon Sep 17 00:00:00 2001 From: Fabien ZARIFIAN Date: Mon, 13 Apr 2026 23:40:52 +0200 Subject: [PATCH] feat(ansible-role-remote_users_fact): set `remote_users_fact_state` default var --- README.md | 103 +++++++++++++--- inventories/hosts.yml | 40 ++++-- roles/remote_users_fact/defaults/main.yml | 6 + roles/remote_users_fact/tasks/absent.yml | 52 ++++++++ roles/remote_users_fact/tasks/assert.yml | 135 +++++++++++++++++++++ roles/remote_users_fact/tasks/main.yml | 45 +++++-- roles/remote_users_fact/tasks/present.yml | 29 +++++ roles/remote_users_fact/tasks/summary.yml | 5 +- roles/remote_users_fact/tasks/validate.yml | 96 +++++++++++++-- site.yml | 22 +++- 10 files changed, 484 insertions(+), 49 deletions(-) create mode 100644 roles/remote_users_fact/tasks/absent.yml create mode 100644 roles/remote_users_fact/tasks/assert.yml create mode 100644 roles/remote_users_fact/tasks/present.yml diff --git a/README.md b/README.md index 46dff29..583bcd0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,18 @@ # remote_users_fact -Rôle Ansible qui déploie un **local fact** comptant les sessions distantes par protocole (SSH, Citrix, Horizon) et évaluant la fiabilité en comparant avec `who`. +Rôle Ansible qui gère un **local fact** comptant les sessions distantes par protocole (SSH, Citrix, Horizon) et évaluant la fiabilité en comparant avec `who`. + +## Gestion du state + +Le rôle est piloté par la variable `remote_users_fact_state` : + +| State | Comportement | +|---|---| +| `noop` (défaut) | Aucune action — le rôle ne touche à rien | +| `present` | Déploie le fact, valide le JSON, affiche le résumé | +| `absent` | Supprime le fact, purge ansible_local, nettoie le répertoire si vide | + +Le state peut être défini à tout niveau (all, groupe, hôte, extra-vars). `noop` par défaut garantit qu'un hôte ajouté à l'inventaire sans configuration explicite ne sera pas impacté. ## Arborescence @@ -10,37 +22,47 @@ Rôle Ansible qui déploie un **local fact** comptant les sessions distantes par │ └── hosts.yml # Inventaire exemple └── roles/ └── remote_users_fact/ - ├── defaults/main.yml # Variables par défaut - ├── files/remote_users.fact # Script fact déployé - ├── handlers/main.yml # Handler rechargement facts + ├── defaults/main.yml # Variables (dont state) + ├── files/remote_users.fact # Script fact + ├── handlers/main.yml # Rechargement facts ├── meta/main.yml # Métadonnées Galaxy └── tasks/ - ├── main.yml # Orchestration - ├── deploy.yml # Création répertoire + copie - ├── validate.yml # Exécution + parsing JSON - └── summary.yml # Affichage résumé + alertes + ├── main.yml # Assertions → routage par state + ├── assert.yml # Validation stricte du paramétrage + ├── present.yml # Déploiement (state=present) + ├── absent.yml # Suppression (state=absent) + ├── validate.yml # Exécution + assert structure JSON + └── summary.yml # Affichage matrice + alertes ``` ## Usage ```bash -# Déploiement complet +# Appliquer le state défini dans l'inventaire ansible-playbook -i inventories/hosts.yml site.yml -# Déploiement seul -ansible-playbook -i inventories/hosts.yml site.yml --tags deploy +# Forcer le déploiement partout (override) +ansible-playbook -i inventories/hosts.yml site.yml -e remote_users_fact_state=present -# Vérification seule (fact déjà déployé) -ansible-playbook -i inventories/hosts.yml site.yml --tags validate,summary +# Supprimer partout +ansible-playbook -i inventories/hosts.yml site.yml -e remote_users_fact_state=absent # Sur un groupe spécifique ansible-playbook -i inventories/hosts.yml site.yml -l citrix_servers + +# Tags disponibles +ansible-playbook ... --tags assert # Assertions seules +ansible-playbook ... --tags present,deploy # Déploiement seul +ansible-playbook ... --tags validate # Validation seule +ansible-playbook ... --tags summary # Résumé seul +ansible-playbook ... --tags absent,remove # Suppression seule ``` ## Variables | Variable | Défaut | Description | |---|---|---| +| `remote_users_fact_state` | `noop` | `present` / `absent` / `noop` | | `remote_users_fact_dir` | `/etc/ansible/facts.d` | Répertoire de destination | | `remote_users_fact_name` | `remote_users.fact` | Nom du script | | `remote_users_fact_owner` | `root` | Propriétaire | @@ -49,6 +71,26 @@ ansible-playbook -i inventories/hosts.yml site.yml -l citrix_servers | `remote_users_fact_display_summary` | `true` | Afficher le résumé | | `remote_users_fact_warn_verdicts` | voir defaults | Verdicts déclenchant un warning | +## Assertions + +Le rôle valide le paramétrage **avant** toute action : + +- `remote_users_fact_state` est défini et vaut `present`, `absent` ou `noop` +- `remote_users_fact_dir` est un chemin absolu (si state != noop) +- `remote_users_fact_name` respecte le pattern `*.fact` (si state != noop) +- `remote_users_fact_owner` et `group` sont définis (si state == present) +- `remote_users_fact_validate` et `display_summary` sont des booléens +- `remote_users_fact_warn_verdicts` est une liste de verdicts valides + +En cas de state `present`, la validation post-déploiement vérifie aussi : +- Le script s'exécute sans erreur (rc == 0) +- La sortie est du JSON parsable +- Toutes les clés obligatoires sont présentes +- Les compteurs sont >= 0 +- `total_by_protocol == ssh + citrix + horizon` +- Le verdict est dans la liste des verdicts connus +- Le fact est chargé dans `ansible_local` + ## Fact déployé Accessible via `ansible_local.remote_users` : @@ -84,21 +126,48 @@ Accessible via `ansible_local.remote_users` : | Verdict | Signification | |---|---| | `FIABLE` | who == total → compteurs alignés | -| `OK` | Écart ≤ 1 → tolérable | +| `OK` | Écart <= 1 → tolérable | | `WHO_SUP_TOTAL` | who > total → protocole non surveillé | | `WHO_INF_TOTAL` | who < total → sessions headless sans TTY | | `WHO_SEUL` | total == 0 → protocoles non détectés | | `PROTO_SEUL` | who == 0 → sessions sans allocation TTY | | `NEUTRE` | 0 == 0 → aucune session | +## Exemple d'inventaire avec states + +```yaml +all: + vars: + remote_users_fact_state: present # Défaut global + + children: + prod_servers: + hosts: + srv-01: {} + srv-02: {} + + # Hôte exclu + dev_servers: + hosts: + dev-01: + remote_users_fact_state: noop + + # Groupe en décommission + legacy: + vars: + remote_users_fact_state: absent +``` + ## Utilisation dans d'autres playbooks ```yaml - hosts: all gather_facts: true tasks: - - name: Refuser un déploiement si trop de sessions actives + - name: Bloquer un déploiement si trop de sessions ansible.builtin.fail: - msg: "{{ ansible_local.remote_users.sessions.total_by_protocol }} sessions actives, déploiement annulé" - when: ansible_local.remote_users.sessions.total_by_protocol | int > 10 + msg: "{{ ansible_local.remote_users.sessions.total_by_protocol }} sessions actives" + when: + - ansible_local.remote_users is defined + - ansible_local.remote_users.sessions.total_by_protocol | int > 10 ``` diff --git a/inventories/hosts.yml b/inventories/hosts.yml index 5be9917..2c76dfe 100644 --- a/inventories/hosts.yml +++ b/inventories/hosts.yml @@ -1,9 +1,29 @@ --- # ============================================================================= -# inventories/hosts.yml — Exemple d'inventaire +# inventories/hosts.yml — Exemple d'inventaire avec gestion du state +# ============================================================================= +# +# remote_users_fact_state: +# present → déploie le fact +# absent → supprime le fact +# noop → ne fait rien (défaut si non spécifié) +# +# Le state peut être défini à n'importe quel niveau : +# - all:vars → défaut global +# - group:vars → surcharge par groupe +# - host:vars → surcharge par hôte +# - extra-vars (-e) → surcharge absolue +# # ============================================================================= all: + vars: + ansible_user: ansible + ansible_become: true + ansible_become_method: sudo + # Défaut global : déployer partout + remote_users_fact_state: present + children: ssh_servers: @@ -12,6 +32,10 @@ all: ansible_host: 192.168.1.10 srv-linux-02: ansible_host: 192.168.1.11 + # Exemple : désactiver sur un hôte spécifique + srv-linux-03: + ansible_host: 192.168.1.12 + remote_users_fact_state: noop citrix_servers: hosts: @@ -20,8 +44,7 @@ all: ctx-vda-02: ansible_host: 192.168.2.11 vars: - # Optionnel : surcharger les verdicts d'alerte pour Citrix - # car WHO_INF_TOTAL est fréquent (apps publiées sans TTY) + # Surcharge par groupe : WHO_INF_TOTAL est normal sur Citrix remote_users_fact_warn_verdicts: - WHO_SUP_TOTAL - WHO_SEUL @@ -33,7 +56,10 @@ all: hrz-agent-02: ansible_host: 192.168.3.11 - vars: - ansible_user: ansible - ansible_become: true - ansible_become_method: sudo + # Exemple : groupe en cours de décommission + legacy_servers: + hosts: + old-srv-01: + ansible_host: 192.168.9.10 + vars: + remote_users_fact_state: absent diff --git a/roles/remote_users_fact/defaults/main.yml b/roles/remote_users_fact/defaults/main.yml index 14ba51e..e39e9b6 100644 --- a/roles/remote_users_fact/defaults/main.yml +++ b/roles/remote_users_fact/defaults/main.yml @@ -3,6 +3,12 @@ # defaults/main.yml — Variables par défaut du rôle remote_users_fact # ============================================================================= +# État souhaité du fact : +# present → déploie le fact + validation + résumé +# absent → supprime le fact + recharge les facts +# noop → ne fait rien (utile pour désactiver par host/groupe) +remote_users_fact_state: noop + # Répertoire de destination des local facts remote_users_fact_dir: /etc/ansible/facts.d diff --git a/roles/remote_users_fact/tasks/absent.yml b/roles/remote_users_fact/tasks/absent.yml new file mode 100644 index 0000000..e16364a --- /dev/null +++ b/roles/remote_users_fact/tasks/absent.yml @@ -0,0 +1,52 @@ +--- +# ============================================================================= +# tasks/absent.yml — Suppression du fact (state=absent) +# ============================================================================= + +- name: "Absent | Vérifier si le fact existe" + ansible.builtin.stat: + path: "{{ remote_users_fact_dir }}/{{ remote_users_fact_name }}" + register: _remote_users_fact_file + +- name: "Absent | Supprimer le script remote_users.fact" + ansible.builtin.file: + path: "{{ remote_users_fact_dir }}/{{ remote_users_fact_name }}" + state: absent + register: _remote_users_fact_removed + when: _remote_users_fact_file.stat.exists + notify: Recharger les local facts + +- name: "Absent | Recharger les facts pour purger ansible_local" + ansible.builtin.setup: + filter: ansible_local + when: _remote_users_fact_removed.changed | default(false) + +- name: "Absent | Vérifier que le fact n'est plus chargé" + ansible.builtin.assert: + that: + - ansible_local.remote_users is not defined + fail_msg: >- + Le fact remote_users est toujours présent dans ansible_local + après suppression. Vérifier le rechargement des facts. + success_msg: "Fact remote_users correctement supprimé et purgé d'ansible_local" + when: _remote_users_fact_removed.changed | default(false) + +- name: "Absent | Info suppression" + ansible.builtin.debug: + msg: >- + {{ 'Fact supprimé de ' ~ remote_users_fact_dir + if _remote_users_fact_removed.changed | default(false) + else 'Fact déjà absent, rien à faire' }} + +- name: "Absent | Nettoyer le répertoire facts.d si vide" + ansible.builtin.command: + cmd: "find {{ remote_users_fact_dir }} -maxdepth 0 -empty -type d" + register: _remote_users_factdir_empty + changed_when: false + failed_when: false + +- name: "Absent | Supprimer le répertoire facts.d si vide" + ansible.builtin.file: + path: "{{ remote_users_fact_dir }}" + state: absent + when: _remote_users_factdir_empty.stdout | length > 0 diff --git a/roles/remote_users_fact/tasks/assert.yml b/roles/remote_users_fact/tasks/assert.yml new file mode 100644 index 0000000..0254f96 --- /dev/null +++ b/roles/remote_users_fact/tasks/assert.yml @@ -0,0 +1,135 @@ +--- +# ============================================================================= +# tasks/assert.yml — Validation stricte du paramétrage avant exécution +# ============================================================================= + +- name: "Assert | remote_users_fact_state est défini" + ansible.builtin.assert: + that: + - remote_users_fact_state is defined + - remote_users_fact_state | length > 0 + fail_msg: >- + remote_users_fact_state n'est pas défini. + Valeurs acceptées : present, absent, noop + quiet: true + +- name: "Assert | remote_users_fact_state contient une valeur valide" + ansible.builtin.assert: + that: + - remote_users_fact_state in _remote_users_fact_valid_states + fail_msg: >- + remote_users_fact_state='{{ remote_users_fact_state }}' invalide. + Valeurs acceptées : {{ _remote_users_fact_valid_states | join(', ') }} + quiet: true + vars: + _remote_users_fact_valid_states: + - present + - absent + - noop + +- name: "Assert | remote_users_fact_dir est un chemin absolu" + ansible.builtin.assert: + that: + - remote_users_fact_dir is defined + - remote_users_fact_dir | length > 0 + - remote_users_fact_dir is match('^/') + fail_msg: >- + remote_users_fact_dir='{{ remote_users_fact_dir | default('') }}' + doit être un chemin absolu (ex: /etc/ansible/facts.d) + quiet: true + when: remote_users_fact_state != "noop" + +- name: "Assert | remote_users_fact_name est défini et valide" + ansible.builtin.assert: + that: + - remote_users_fact_name is defined + - remote_users_fact_name | length > 0 + - remote_users_fact_name is match('^[a-zA-Z0-9_.-]+\.fact$') + fail_msg: >- + remote_users_fact_name='{{ remote_users_fact_name | default('') }}' + doit correspondre au pattern [a-zA-Z0-9_.-]+.fact + quiet: true + when: remote_users_fact_state != "noop" + +- name: "Assert | remote_users_fact_owner est défini" + ansible.builtin.assert: + that: + - remote_users_fact_owner is defined + - remote_users_fact_owner | length > 0 + fail_msg: >- + remote_users_fact_owner ne peut pas être vide + quiet: true + when: remote_users_fact_state == "present" + +- name: "Assert | remote_users_fact_group est défini" + ansible.builtin.assert: + that: + - remote_users_fact_group is defined + - remote_users_fact_group | length > 0 + fail_msg: >- + remote_users_fact_group ne peut pas être vide + quiet: true + when: remote_users_fact_state == "present" + +- name: "Assert | remote_users_fact_validate est un booléen" + ansible.builtin.assert: + that: + - remote_users_fact_validate | string | lower in ['true', 'false', 'yes', 'no'] + fail_msg: >- + remote_users_fact_validate='{{ remote_users_fact_validate }}' + doit être un booléen (true/false) + quiet: true + when: remote_users_fact_state == "present" + +- name: "Assert | remote_users_fact_display_summary est un booléen" + ansible.builtin.assert: + that: + - remote_users_fact_display_summary | string | lower in ['true', 'false', 'yes', 'no'] + fail_msg: >- + remote_users_fact_display_summary='{{ remote_users_fact_display_summary }}' + doit être un booléen (true/false) + quiet: true + when: remote_users_fact_state == "present" + +- name: "Assert | remote_users_fact_warn_verdicts est une liste" + ansible.builtin.assert: + that: + - remote_users_fact_warn_verdicts is defined + - remote_users_fact_warn_verdicts is iterable + - remote_users_fact_warn_verdicts is not string + fail_msg: >- + remote_users_fact_warn_verdicts doit être une liste + (ex: [WHO_SUP_TOTAL, WHO_INF_TOTAL]) + quiet: true + when: remote_users_fact_state == "present" + +- name: "Assert | remote_users_fact_warn_verdicts contient des verdicts valides" + ansible.builtin.assert: + that: + - item in _remote_users_fact_valid_verdicts + fail_msg: >- + Verdict '{{ item }}' invalide dans remote_users_fact_warn_verdicts. + Valeurs acceptées : {{ _remote_users_fact_valid_verdicts | join(', ') }} + quiet: true + loop: "{{ remote_users_fact_warn_verdicts }}" + vars: + _remote_users_fact_valid_verdicts: + - FIABLE + - OK + - NEUTRE + - WHO_SUP_TOTAL + - WHO_INF_TOTAL + - WHO_SEUL + - PROTO_SEUL + when: remote_users_fact_state == "present" + +- name: "Assert | Résumé du paramétrage validé" + ansible.builtin.debug: + msg: >- + Assertions OK — state={{ remote_users_fact_state }} + {% if remote_users_fact_state == 'present' %} + dir={{ remote_users_fact_dir }} + name={{ remote_users_fact_name }} + validate={{ remote_users_fact_validate }} + summary={{ remote_users_fact_display_summary }} + {% endif %} diff --git a/roles/remote_users_fact/tasks/main.yml b/roles/remote_users_fact/tasks/main.yml index 672fd13..44a8294 100644 --- a/roles/remote_users_fact/tasks/main.yml +++ b/roles/remote_users_fact/tasks/main.yml @@ -1,24 +1,55 @@ --- # ============================================================================= -# tasks/main.yml — Déploiement et validation du local fact remote_users +# tasks/main.yml — Point d'entrée : assertions puis routage par état # ============================================================================= -- name: Inclure les tâches de déploiement - ansible.builtin.include_tasks: deploy.yml +- name: Inclure les assertions de paramétrage + ansible.builtin.include_tasks: assert.yml tags: - remote_users_fact + - assert + +- name: "État noop — aucune action" + ansible.builtin.debug: + msg: >- + remote_users_fact_state=noop sur {{ inventory_hostname }}, + aucune action effectuée. + when: remote_users_fact_state == "noop" + tags: + - remote_users_fact + +- name: "État present — déploiement du fact" + ansible.builtin.include_tasks: present.yml + when: remote_users_fact_state == "present" + tags: + - remote_users_fact + - present - deploy -- name: Inclure les tâches de validation +- name: "État present — validation" ansible.builtin.include_tasks: validate.yml - when: remote_users_fact_validate | bool + when: + - remote_users_fact_state == "present" + - remote_users_fact_validate | bool tags: - remote_users_fact + - present - validate -- name: Inclure les tâches de résumé +- name: "État present — résumé" ansible.builtin.include_tasks: summary.yml - when: remote_users_fact_display_summary | bool + when: + - remote_users_fact_state == "present" + - remote_users_fact_display_summary | bool tags: - remote_users_fact + - present - summary + +- name: "État absent — suppression du fact" + ansible.builtin.include_tasks: absent.yml + when: remote_users_fact_state == "absent" + tags: + - remote_users_fact + - absent + - remove diff --git a/roles/remote_users_fact/tasks/present.yml b/roles/remote_users_fact/tasks/present.yml new file mode 100644 index 0000000..762f8a1 --- /dev/null +++ b/roles/remote_users_fact/tasks/present.yml @@ -0,0 +1,29 @@ +--- +# ============================================================================= +# tasks/present.yml — Déploiement du fact (state=present) +# ============================================================================= + +- name: "Present | Créer le répertoire facts.d" + ansible.builtin.file: + path: "{{ remote_users_fact_dir }}" + state: directory + owner: "{{ remote_users_fact_owner }}" + group: "{{ remote_users_fact_group }}" + mode: "0755" + +- name: "Present | Déployer le script remote_users.fact" + ansible.builtin.copy: + src: "{{ remote_users_fact_name }}" + dest: "{{ remote_users_fact_dir }}/{{ remote_users_fact_name }}" + owner: "{{ remote_users_fact_owner }}" + group: "{{ remote_users_fact_group }}" + mode: "0755" + backup: true + register: _remote_users_fact_deployed + notify: Recharger les local facts + +- name: "Present | Info déploiement" + ansible.builtin.debug: + msg: >- + Fact {{ 'mis à jour' if _remote_users_fact_deployed.changed else 'déjà en place' }} + → {{ remote_users_fact_dir }}/{{ remote_users_fact_name }} diff --git a/roles/remote_users_fact/tasks/summary.yml b/roles/remote_users_fact/tasks/summary.yml index 46df44c..281e8b9 100644 --- a/roles/remote_users_fact/tasks/summary.yml +++ b/roles/remote_users_fact/tasks/summary.yml @@ -3,11 +3,12 @@ # tasks/summary.yml — Affichage du résumé et alertes éventuelles # ============================================================================= -- name: Afficher le résumé des sessions distantes +- name: "Summary | Résumé des sessions distantes" ansible.builtin.debug: msg: - "══════════════════════════════════════════" - "Host : {{ inventory_hostname }}" + - "State : {{ remote_users_fact_state }}" - "Timestamp : {{ ansible_local.remote_users.timestamp }}" - "──────────────────────────────────────────" - "SSH : {{ ansible_local.remote_users.sessions.ssh }}" @@ -25,7 +26,7 @@ - "Horizon Agt : {{ ansible_local.remote_users.detection.horizon_agent_installed }}" - "══════════════════════════════════════════" -- name: Alerter si le verdict est anormal +- name: "Summary | Alerte si verdict anormal" ansible.builtin.debug: msg: >- ⚠ ATTENTION sur {{ inventory_hostname }} — diff --git a/roles/remote_users_fact/tasks/validate.yml b/roles/remote_users_fact/tasks/validate.yml index 19ae2ab..4867b1c 100644 --- a/roles/remote_users_fact/tasks/validate.yml +++ b/roles/remote_users_fact/tasks/validate.yml @@ -3,35 +3,111 @@ # tasks/validate.yml — Validation du fact (exécution + parsing JSON) # ============================================================================= -- name: Forcer le rechargement des facts si déploiement effectué +- name: "Validate | Forcer le rechargement des facts" ansible.builtin.meta: flush_handlers -- name: Exécuter le script fact manuellement pour validation +- name: "Validate | Exécuter le script fact pour validation" ansible.builtin.command: cmd: "{{ remote_users_fact_dir }}/{{ remote_users_fact_name }}" register: _remote_users_fact_output changed_when: false failed_when: _remote_users_fact_output.rc != 0 -- name: Valider que la sortie est du JSON parsable +- name: "Validate | Parser la sortie JSON" ansible.builtin.set_fact: _remote_users_fact_parsed: "{{ _remote_users_fact_output.stdout | from_json }}" -- name: Vérifier la présence des clés obligatoires +- name: "Validate | Vérifier la structure JSON — clés racine" ansible.builtin.assert: that: + - _remote_users_fact_parsed.timestamp is defined - _remote_users_fact_parsed.sessions is defined + - _remote_users_fact_parsed.users_remote is defined + - _remote_users_fact_parsed.reliability is defined + - _remote_users_fact_parsed.detection is defined + fail_msg: "Clés racine manquantes dans la sortie JSON du fact" + success_msg: "Clés racine OK" + quiet: true + +- name: "Validate | Vérifier la structure JSON — sessions" + ansible.builtin.assert: + that: - _remote_users_fact_parsed.sessions.ssh is defined - _remote_users_fact_parsed.sessions.citrix is defined - _remote_users_fact_parsed.sessions.horizon is defined - _remote_users_fact_parsed.sessions.total_by_protocol is defined - _remote_users_fact_parsed.sessions.who_remote is defined - - _remote_users_fact_parsed.reliability is defined - - _remote_users_fact_parsed.reliability.verdict is defined - - _remote_users_fact_parsed.detection is defined - fail_msg: "Le fact remote_users ne retourne pas la structure JSON attendue" - success_msg: "Structure JSON validée avec succès" + - _remote_users_fact_parsed.sessions.ssh | int >= 0 + - _remote_users_fact_parsed.sessions.citrix | int >= 0 + - _remote_users_fact_parsed.sessions.horizon | int >= 0 + - _remote_users_fact_parsed.sessions.total_by_protocol | int >= 0 + - _remote_users_fact_parsed.sessions.who_remote | int >= 0 + fail_msg: "Structure 'sessions' invalide ou valeurs négatives" + success_msg: "Structure sessions OK" + quiet: true -- name: Recharger les ansible_local facts +- name: "Validate | Vérifier la cohérence total = ssh + citrix + horizon" + ansible.builtin.assert: + that: + - >- + _remote_users_fact_parsed.sessions.total_by_protocol | int == + (_remote_users_fact_parsed.sessions.ssh | int + + _remote_users_fact_parsed.sessions.citrix | int + + _remote_users_fact_parsed.sessions.horizon | int) + fail_msg: >- + total_by_protocol ({{ _remote_users_fact_parsed.sessions.total_by_protocol }}) + != ssh+citrix+horizon + ({{ _remote_users_fact_parsed.sessions.ssh }} + +{{ _remote_users_fact_parsed.sessions.citrix }} + +{{ _remote_users_fact_parsed.sessions.horizon }}) + success_msg: "Cohérence total OK" + quiet: true + +- name: "Validate | Vérifier la structure JSON — reliability" + ansible.builtin.assert: + that: + - _remote_users_fact_parsed.reliability.ratio_who_over_total is defined + - _remote_users_fact_parsed.reliability.verdict is defined + - _remote_users_fact_parsed.reliability.detail is defined + - _remote_users_fact_parsed.reliability.verdict in _remote_users_fact_valid_verdicts + fail_msg: >- + Structure 'reliability' invalide ou verdict inconnu : + '{{ _remote_users_fact_parsed.reliability.verdict | default('UNDEFINED') }}' + success_msg: "Structure reliability OK" + quiet: true + vars: + _remote_users_fact_valid_verdicts: + - FIABLE + - OK + - NEUTRE + - WHO_SUP_TOTAL + - WHO_INF_TOTAL + - WHO_SEUL + - PROTO_SEUL + +- name: "Validate | Vérifier la structure JSON — detection" + ansible.builtin.assert: + that: + - _remote_users_fact_parsed.detection.citrix_vda_installed is defined + - _remote_users_fact_parsed.detection.horizon_agent_installed is defined + - _remote_users_fact_parsed.detection.ssh_method is defined + - _remote_users_fact_parsed.detection.citrix_method is defined + - _remote_users_fact_parsed.detection.horizon_method is defined + fail_msg: "Structure 'detection' invalide" + success_msg: "Structure detection OK" + quiet: true + +- name: "Validate | Recharger ansible_local" ansible.builtin.setup: filter: ansible_local + +- name: "Validate | Confirmer la présence dans ansible_local" + ansible.builtin.assert: + that: + - ansible_local.remote_users is defined + - ansible_local.remote_users.sessions is defined + - ansible_local.remote_users.reliability is defined + fail_msg: >- + Le fact remote_users n'est pas visible dans ansible_local + après rechargement. Vérifier les permissions et le format. + success_msg: "Fact remote_users chargé dans ansible_local avec succès" diff --git a/site.yml b/site.yml index c73af91..7439f2e 100644 --- a/site.yml +++ b/site.yml @@ -1,23 +1,33 @@ --- # ============================================================================= -# site.yml — Playbook de déploiement du local fact remote_users +# site.yml — Playbook de gestion du local fact remote_users # ============================================================================= # # Usage : +# # Déployer sur tous les hôtes (state défini dans l'inventaire) : # ansible-playbook -i inventories/hosts.yml site.yml # -# # Déploiement seul (sans validation ni résumé) : -# ansible-playbook -i inventories/hosts.yml site.yml --tags deploy +# # Forcer le déploiement sur tous (override du state) : +# ansible-playbook -i inventories/hosts.yml site.yml \ +# -e remote_users_fact_state=present # -# # Validation + résumé seul (fact déjà déployé) : -# ansible-playbook -i inventories/hosts.yml site.yml --tags validate,summary +# # Supprimer partout : +# ansible-playbook -i inventories/hosts.yml site.yml \ +# -e remote_users_fact_state=absent # # # Limiter à un groupe : # ansible-playbook -i inventories/hosts.yml site.yml -l citrix_servers # +# # Tags disponibles : +# --tags assert → assertions seules +# --tags present,deploy → déploiement seul +# --tags validate → validation seule +# --tags summary → résumé seul +# --tags absent,remove → suppression seule +# # ============================================================================= -- name: Déployer et valider le local fact remote_users +- name: Gérer le local fact remote_users hosts: all become: true gather_facts: true