Search code examples
variablesansibledevopsansible-awx

How to dynamically set the hosts field in Ansible playbooks with a variable generated during execution?


I am trying to test something at home with the variables mechanism Ansible offers, which I am about to implement in one of my projects at work. So, been searching for a while now, but seems I can't get it working that easily, even with others` solutions here and there.

I will represent my project logic at work now, by demonstrating with my test directory & files structure at home. Here's the case, I have the following playbooks:

main.yaml
pl1.yaml
pl2.yaml

Contents of ./main.yaml:

  - import_playbook: /home/martin/ansible/pl1.yaml
  - import_playbook: /home/martin/ansible/pl2.yaml

Contents of ./pl1.yaml:

- name: Test playbook 1
  hosts: localhost
  tasks:
  - name: Discovering the secret host
    shell: cat /home/martin/secret
    register: whichHostAd
  - debug:
      msg: "{{ whichHostAd.stdout }}"
  - name: Discovering my hostname
    shell: hostname
    register: myHostnameAd
  - set_fact:
      whichHost: "{{ whichHostAd.stdout }}"
      myHostname: "{{ myHostnameAd.stdout }}"
    cacheable: yes

- name: Test playbook 1 part 2
  hosts: "{{ hostvars['localhost']['ansible_facts']['whichHost'] }}"
  tasks:
  - name: Structuring info
    shell: hostname
    register: secretHostname
  - name: Showing the secret hostname
    debug:
      msg: "{{ secretHostname.stdout }}"

Contents of ./pl2.yaml:

- name: Test Playbook 2
  hosts: "{{ whichHost }}"
  tasks:
  - name: Finishing up
    shell: echo "And here am i again.." && hostname
  - name: Showing var myHostname
    debug:
      msg: "{{ myHostname.stdout }}"

The whole idea is to have a working variable on the go at the hosts field between the plays. How do we do that?

The playbook does not run at all if I won't define the whichHost variable as an extra arg, and that's ok, I can do it each time, but during the execution I would like to have that variable manageable and changeable. In the test case above, I want whichHost to be used everywhere across the plays/playbooks included in main.yaml, specifically to reflect the output of the first task in pl1.yaml (or the output of the whichHostAd.stdout variable), so I can determine the host I am about to target in pl2.yaml.

According to docs, I should be able to at least access it with hostvars (as in my playbook), but this is the output I get when I try the above example:

ERROR! The field 'hosts' has an invalid value, which includes an undefined variable. The error was: 'dict object' has no attribute 'whichHost'

The error appears to have been in '/home/martin/ansible/pl1.yaml': line 22, column 3, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:


- name: Test playbook 1 part 2
  ^ here

set_fact also does not seem to be very helpful. Any help will be appreciated!


Solution

  • Ok, I've actually figured it out pretty fast.

    So, we definitely need to have a fact task, holding the actual data/output:

    - hosts: localhost
      tasks:
      - name: Saving variable
        set_fact:
          whichHost: "{{ whichHostAd.stdout }}"
    

    After that, when you want to invoke the var in other hosts and plays, we have to provide the host and the fact:

    "{{ hostvars['localhost']['whichHost'] }}"
    

    Like in my test above, but without ['ansible_facts']