Aller au contenu
Infrastructure as Code medium

register et set_fact Ansible : capturer et créer des variables au runtime

12 min de lecture

Logo Ansible

Pendant l’exécution d’un playbook, vous avez souvent besoin de capturer une sortie de module et de la réutiliser plus loin. C’est le rôle de register: (capture la sortie d’une tâche) et set_fact: (crée une variable au runtime). Cette page explique la différence, les champs disponibles dans un register: (stdout, rc, delta…), la durée de vie des variables, et l’option cacheable: true qui rend un fact persistant entre runs via le fact caching.

C’est l’outil de prédilection pour chaîner des tâches : récupérer un version, calculer un identifiant, conditionner une suite d’actions sur le résultat d’une commande.

  • register: var capture la sortie complète d’un module (rc, stdout, stderr, delta…)
  • Accéder aux champs : var.stdout, var.rc, var.changed, var.failed
  • set_fact: crée une variable de runtime, scope du play entier
  • cacheable: true rend le fact persistant via fact_caching (jsonfile/redis/memcached)
  • La différence entre register (lié à la tâche), set_fact (manuel) et les facts (gather)
- name: Récupérer le hostname
ansible.builtin.command: hostname -s
register: hostname_result
changed_when: false
- name: Afficher
ansible.builtin.debug:
msg: "stdout={{ hostname_result.stdout }} rc={{ hostname_result.rc }}"

hostname_result contient tout ce que le module a retourné. Pour le module command :

ChampContenu
stdoutSortie standard (string)
stdout_linesSortie standard (liste de lignes)
stderrSortie d’erreur
stderr_linesIdem en liste
rcExit code (0 = succès)
cmdLa commande exécutée
start, end, deltaTimestamps
changed, failed, skippedStatus booléens

Pour les autres modules, les champs varient. Exemple avec uri :

- name: Appeler une API
ansible.builtin.uri:
url: http://localhost
return_content: true
register: api_response

api_response contient :

  • status : code HTTP retourné
  • content : corps de la réponse
  • json : si la réponse est JSON, parsée automatiquement
  • cookies : cookies reçus
  • elapsed : temps écoulé

Toujours regarder la doc du module (ansible-doc <fqcn>) pour connaître les champs exposés.

- name: Calculer un identifiant à partir de plusieurs sources
ansible.builtin.set_fact:
server_id: "{{ inventory_hostname | upper }}-{{ ansible_default_ipv4.address | replace('.', '-') }}"
- name: Utiliser server_id
ansible.builtin.debug:
msg: "Server ID = {{ server_id }}"

set_fact: crée une variable de play disponible dans toutes les tâches suivantes. Elle est précédence niveau 19 — gagne sur le vars: du play (12) mais pas sur --extra-vars (22).

Plusieurs faits dans un même set_fact: :

- name: Plusieurs facts
ansible.builtin.set_fact:
server_id: "{{ inventory_hostname | upper }}"
deploy_date: "{{ ansible_date_time.iso8601 }}"
deploy_user: "{{ ansible_user_id }}"

Par défaut, un set_fact: est éphémère : disponible dans le play courant, perdu au prochain run. Avec cacheable: true, il est stocké dans le fact_caching :

- name: Set_fact persistant
ansible.builtin.set_fact:
last_deploy_date: "{{ ansible_date_time.iso8601 }}"
cacheable: true

Au prochain run sur le même hôte, last_deploy_date est automatiquement chargé depuis le cache avant la phase gather_facts. Vous pouvez l’utiliser pour comparer des dates.

Configuration du fact_caching dans ansible.cfg :

[defaults]
fact_caching = jsonfile
fact_caching_connection = ./.ansible_facts
fact_caching_timeout = 7200 # 2h

Backends supportés : jsonfile (fichiers locaux), redis (recommandé production), memcached.

Voici l’exemple validé sur le lab 16-ecrire-code-register-set-fact :

---
- name: Challenge register set_fact
hosts: db1.lab
become: true
tasks:
- name: Hostname
ansible.builtin.command: hostname -s
register: hostname_result
changed_when: false
- name: Kernel
ansible.builtin.command: uname -r
register: kernel_result
changed_when: false
- name: Construire system_id
ansible.builtin.set_fact:
system_id: "{{ hostname_result.stdout }}:{{ kernel_result.stdout }}"
- name: Poser le fichier
ansible.builtin.copy:
dest: /tmp/system-id.txt
content: "system_id={{ system_id }}\n"
mode: "0644"

Contenu de /tmp/system-id.txt sur db1.lab :

system_id=db1:5.14.0-628.el10.x86_64

Pattern : deux command registered, un set_fact qui combine, une tâche qui consomme la variable créée.

Une commande comme hostname -s ne modifie pas l’état du système — pourtant le module command la marque par défaut comme changed=true, ce qui pollue le PLAY RECAP et peut déclencher des handlers à tort.

Toujours poser changed_when: false sur une commande de lecture :

- name: Lire le hostname (lecture pure, pas de modif)
ansible.builtin.command: hostname -s
register: hostname_result
changed_when: false # ← ne marque pas comme changed
check_mode: false # ← exécute même en --check (pour avoir la vraie valeur)

Combiné à check_mode: false, vous obtenez la valeur réelle même quand le playbook tourne en --check.

Couplé avec register:, until: répète une tâche jusqu’à ce qu’une condition soit vraie :

- name: Attendre que le service réponde
ansible.builtin.uri:
url: http://localhost/health
status_code: 200
register: health_check
until: health_check.status == 200
retries: 30
delay: 2 # 30 retries × 2s = 60s d'attente max

Très utile après un démarrage de service ou un déploiement — la tâche échoue après le timeout total si la condition n’est jamais vraie.

Avec register:, vous pouvez redéfinir quand une tâche est considérée failed ou changed :

- name: Lancer un script qui retourne 0 ou 1 selon l'état
ansible.builtin.command: /usr/local/bin/check-state.sh
register: state_result
failed_when: state_result.rc not in [0, 1] # rc=2 = vraiment failed
changed_when: state_result.rc == 1 # rc=1 = changement détecté

Permet d’utiliser command: ou shell: de façon idempotente en mappant les codes retour sur la sémantique Ansible.

SymptômeCauseFix
'dict object' has no attribute 'stdout'La tâche a échoué avant — register n’a pas le champ attenduwhen: not result.failed ou failed_when: adapté
set_fact perdu entre 2 playsSans cacheable: true, le fact est limité au play courantAjouter cacheable: true (avec attention à la persistence)
changed=true à chaque run sur un command de lecturePas de changed_when: poséAjouter changed_when: false
register: ne capture rien quand la tâche est en --checkcommand est skippé en check modeAjouter check_mode: false sur la tâche
Fact persistant qui contient une valeur obsolètecacheable: true sans expirationAjuster fact_caching_timeout ou nettoyer le cache (rm -rf .ansible_facts/)
  • register: var capture la sortie d’un module ; champs courants : stdout, rc, changed, failed, delta.
  • set_fact: crée une variable de play au runtime ; précédence niveau 19.
  • cacheable: true sur set_fact = fact persistant via fact_caching (jsonfile/redis/memcached).
  • Sur les commandes de lecture pure : toujours changed_when: false + check_mode: false.
  • until + retries + delay transforme une tâche en boucle d’attente conditionnelle.
  • failed_when: et changed_when: redéfinissent la sémantique Ansible d’un module non-idempotent.

Cette page a un lab d’accompagnement : labs/ecrire-code/register-set-fact/ dans stephrobert/ansible-training. Il contient un README.md guidé, un Makefile (make verify lance les tests), et un challenge final auto-évalué : chaîner register: (hostname, kernel) puis set_fact: pour construire un system_id.

Une fois le lab provisionné :

Fenêtre de terminal
cd ~/Projets/ansible-training/labs/ecrire-code/register-set-fact/
cat README.md # tuto pas à pas
cat challenge/README.md # consigne du challenge final
pytest -v challenge/tests/ # lancer les tests testinfra

Si les tests passent, vous maîtrisez les concepts couverts dans ce guide. En cas de blocage, docs/troubleshooting.md à la racine du repo couvre les pièges fréquents (rate-limit SSH, clé absente, collection manquante).

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn