Yoav Sheetrit
Yoav Sheetrit

Reputation: 149

Ansible set_facts by localhost json file and copy to remote hosts

I'm trying to copy a json file from my localhost to remote hosts, and use the json "version" key value as a parameter in the destination file name.

JSON:

{"name": "test", "version": 3}

YML:

---
- name: Get json file version
  hosts: locahost
  become: true
  tasks:
    - name: Register licenses file
      command: "cat conf/licenses.json"
      register: result

    - name: Save the json data
      set_fact:
        jsondata: "{{ result.stdout | from_json }}"

    - name: Save licenses file version
      set_fact:
        file_version: "{{ jsondata | json_query(jmesquery) }}"
      vars:
        jmesquery: 'version'

- name: Deploy Licenses File
  hosts: hosts
  become: true
  tasks:
    - name: Copy licenses file
      copy:
        src: "conf/licenses.json"
        dest: "/tmp/licenses_{{ file_version }}.json"

When I run the playbook above, the Deploy Licenses File key doesn't find the file_version fact, even though I can see it is successfully saved in the Get json file version key.

Set file_version fact:

ok: [localhost] => {
    "ansible_facts": {
        "file_version": "1"
    },
    "changed": false
}

Error:

The task includes an option with an undefined variable. The error was: 'file_version' is undefined

I think the facts are saved on the given host granularity and are not global facts per playbook initiation.

My current workaround is to combine the keys to a single task and then it works correctly, but I prefer to get the version once instead of repeating it for each remote host.

Upvotes: 1

Views: 815

Answers (2)

Vladimir Botka
Vladimir Botka

Reputation: 68124

The first play runs at localhost only. Therefore the variables declared in the first play are available to localhost only. This is the reason that the variables are not available to host in the second play

The error was: 'file_version' is undefined

Run once the tasks in a block of the first play at all hosts. This way the variables will be available to all hosts in the second play. For example, the simplified playbook below does the job

- hosts: all
  tasks:
    - block:
        - include_vars:
            file: licenses.json
            name: jsondata
        - set_fact:
            file_version: "{{ jsondata.version }}"
      run_once: true

- hosts: hosts
  tasks:
    - copy:
        src: licenses.json
        dest: "/tmp/licenses_{{ file_version }}.json"

Created file at the remote host

shell> ssh admin@test_11 cat /tmp/licenses_3.json
{"name": "test", "version": 3}

The code can be further simplified. The single play below does the job as well

- hosts: hosts
  tasks:
    - include_vars:
        file: licenses.json
        name: jsondata
      run_once: true
    - copy:
        src: licenses.json
        dest: "/tmp/licenses_{{ jsondata.version }}.json"

Upvotes: 1

β.εηοιτ.βε
β.εηοιτ.βε

Reputation: 39264

To access facts of another host, you can always use the hostvars special variable.

So, in your case:

dest: "/tmp/licenses_{{ hostvars.localhost.file_version }}.json"

Now, you actually do not need that level of complication with two plays and could well do:

- name: Deploy Licenses File
  hosts: hosts
  become: true

  tasks:
    - name: Copy licenses file
      copy:
        src: "{{ _licence_file }}"
        dest: "/tmp/licenses_{{ file_version }}.json"
      vars:
        _licence_file: conf/licenses.json
        file_version: >-
          {{ (lookup('file', _licence_file) | from_json).version }}

Upvotes: 1

Related Questions