Ansible playbook that generates and shares variable between hosts

ansible register variable
ansible inventory variables
ansible variables example
ansible assign variable to another variable
ansible global variables
ansible set_fact vs register
ansible set_fact from command
ansible set_fact variable

My Ansible playbook deploys to both database and webservers and I need to use some shared variables between them. The answer from this question almost gives me what I need:

---
- hosts: all
  tasks:
  - set_fact: my_global_var='hello'

- hosts: db
  tasks:
  - debug: msg={{my_global_var}}

- hosts: web
  tasks:
  - debug: msg={{my_global_var}}

However, in my case the variable is a password that is generated randomly by the playbook on each run and then has to be shared:

---
- hosts: all
  tasks:
  - name: Generate new password
    shell: "tr -dc _[:alnum:] < /dev/urandom | head -c${1:-20}"
    register: new_password    
  - name: Set password as fact
    set_fact:
      my_global_var: "{{ new_password.stdout }}"

- hosts: db
  tasks:
  - debug: msg={{my_global_var}}

- hosts: web
  tasks:
  - debug: msg={{my_global_var}}

This above example doesn't work as the password is now re-generated and completely different for each host in the all hosts (unless you coincidentally use the same machine/hostname for your db and web servers).

Ideally I don't want someone to have to remember to pass a good random password in on the command-line using --extra-vars, it should be generated and handled by the playbook.

Is there any suggested mechanism in Ansible for creating variables within a playbook and having it accessible to all hosts within that playbook?

You may want to try to generate pass on localhost and then copy it to every other host:

---
- hosts: localhost
  tasks:
  - name: Generate new password
    shell: "tr -dc _[:alnum:] < /dev/urandom | head -c${1:-20}"
    register: new_password    

- hosts: all
  tasks:
  - name: Set password as fact
    set_fact:
      my_global_var: "{{ hostvars['localhost'].new_password.stdout }}"

- hosts: db
  tasks:
  - debug: msg={{my_global_var}}

- hosts: web
  tasks:
  - debug: msg={{my_global_var}}

set_fact – Set host facts from a task, My Ansible playbook deploys to both database and webservers and I need to use some shared variables between them. The answer from this question almost  My Ansible playbook deploys to both database and webservers and I need to use some shared variables between them. The answer from this question almost gives me what I need: --- - hosts: all task

Just set the run_once flag on your tasks:

- hosts: all
  tasks:
  - name: Generate new password
    shell: "tr -dc _[:alnum:] < /dev/urandom | head -c${1:-20}"
    run_once: True
    register: new_password

  - name: Set password as fact
    set_fact:
      my_global_var: "{{ new_password.stdout }}"

Then this password will only be generated once

Using Variables, Normally this module creates 'host level variables' and has much higher The set_fact module takes key=value pairs as variables to set in the playbook scope. In Ansible, passing a variable from one playbook to another playbook is not a straight forward. (If the target hosts are different). We might need to get the variable value from one host and use that value against another host in some cases.

I know this is an old question but I settled on an alternative method that combines the two answers provided here and this issue, by using an implicit localhost reference and doing everything within the same play. I think it's a bit more elegant. Tested with 2.8.4.

This is the working solution in my context, where I wanted a common timestamped backup directory across all my hosts, for later restore:

---
tasks:
  - name: Set local fact for the universal backup string
    set_fact:
      thisHostTimestamp: "{{ ansible_date_time.iso8601 }}"
    delegate_to: localhost
    delegate_facts: true

  - name: Distribute backup datestring to all hosts in group
     set_fact:
       backupsTimeString: "{{ hostvars['localhost']['thisHostTimestamp'] }}"

I believe this should translate to the OP's original example like this, but I have not tested it:

---
- hosts: all
  tasks:
  - name: Generate new password
    shell: "tr -dc _[:alnum:] < /dev/urandom | head -c${1:-20}"
    register: new_password  
    delegate_to: localhost
    delegate_facts: true

  - name: Set password as fact
    set_fact:
      my_global_var: "{{ hostvars['localhost'].new_password.stdout }}"

Sharing ansible variable between plays, Ansible uses variables to help deal with differences between systems. Often you'll want to set variables for an individual host, or for a group of hosts in your inventory. As described in Roles, variables can also be included in the playbook via This is a rather important feature as it means it is possible to code​-generate  Ansible has 3 main scopes: Global: this is set by config, environment variables and the command line. Play: each play and contained structures, vars entries, include_vars, role defaults and vars. Host: variables directly associated to a host, like inventory, facts or registered task outputs.

4. Variables and Facts - Ansible: Up and Running [Book], A value you set with set_fact will be available between different plays. Keep in mind that set_fact are set for a specific host. Your first play is run  You need to keep in mind that in Ansible, the variable app_git_sha1 assigned to the host localhost is distinct from the variable app_git_sha1 assigned to the host main or any other host. If you want to access one hosts facts/variables from another host then you need to explicitly reference it via the hostvars variable.

How to pass variables between Ansible plays and hosts, Variables and Facts Ansible is not a full-fledged programming language, Recall from Example 2-8 that we used this approach to define Let's say we run the playbook shown in Example 4-3. name : show return value of command module hosts : server1 tasks The generated file would look like this: However, registered variables are only stored in memory. (Ansible facts are backed by whatever cache plugin you have configured.) Registered variables are only valid on the host for the rest of the current playbook run. Finally, registered variables and facts have different precedence levels.

Mastering Ansible: Effectively automate configuration management , I'm working on a set of Ansible plays in which there is a common or base play, a master play (items relevant only to the master server) and a How to pass variables between Ansible plays and hosts creates: /home/ubuntu/node_joined​.txt. From reading the ansible docs on variable scope, it says that variables defined in a play are not available outside that play unless it's being applied to the same set of hosts. That's not possible in this case, so I need to use a var with global scope, and from what I've read, set_fact is the appropriate way to do that.

Comments
  • In this case my_global_var will be available only for single host (first in the play).
  • You are right, thanks, I adjusted run_once to be run only on pw generation.
  • Registered variables are host-level variables. I think "new_password.stdout" will be set for localhost, but not for other hosts in the inventory.