feat(ansible-role-remote_users_fact): set remote_users_fact_state default var

This commit is contained in:
2026-04-13 23:40:52 +02:00
parent 51f19b678f
commit 1987c5791c
10 changed files with 484 additions and 49 deletions
+86 -17
View File
@@ -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
```
+32 -6
View File
@@ -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
# Exemple : groupe en cours de décommission
legacy_servers:
hosts:
old-srv-01:
ansible_host: 192.168.9.10
vars:
ansible_user: ansible
ansible_become: true
ansible_become_method: sudo
remote_users_fact_state: absent
@@ -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
+52
View File
@@ -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
+135
View File
@@ -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 %}
+38 -7
View File
@@ -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
+29
View File
@@ -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 }}
+3 -2
View File
@@ -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 }} —
+86 -10
View File
@@ -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"
+16 -6
View File
@@ -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