# Using Set Fact from hosts.yaml json format

**URL:** <https://forum.ansible.com/t/using-set-fact-from-hosts-yaml-json-format/41874>\
**Category:** Get Help\
**Created:** [April 22, 2025, 1:52pm UTC](https://forum.ansible.com/t/using-set-fact-from-hosts-yaml-json-format/41874 "2025-04-22T13:52:10Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![penguinpages](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/penguinpages/32/8179_2.png) [@penguinpages](https://forum.ansible.com/u/penguinpages)\
**Post date:** [April 22, 2025, 1:52pm UTC](https://forum.ansible.com/t/using-set-fact-from-hosts-yaml-json-format/41874/1 "2025-04-22T13:52:10Z")

</div>

I need to create a workflow where I have a “host” device where it has a key value of “SN”

I need to use passed playbook “extra var” input , and using that set fact SN that I can use in later tasks. Seems simple just reuturn key / value from hosts.. but turns out it is not working. I have tried many ways.

command:  
ansible-playbook -i inventories/production/hosts.yaml --limit triton tabletop-deploy.yaml --user ansible -e tabletop\_number=“$tabletop\_number”

tasks:

```auto
- name: Search ansible hosts and set fact based on SN
  set_fact:
    sn_value: "{{ hostvars[item].SN }}"
  loop: "{{ groups['arduino_boards'] | list }}"
  when: item == tabletop_number
  register: sn_result

- name: Print SN value
  debug:
    msg: "SN value: {{ sn_result.results.ansible_facts.sn_value }}"

```

Example hosts.yaml

```auto
all:
  children:
    pi_servers:
      hosts:
        triton:
          ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
          # ssh_pub_key: # ssh_key_pub in SCM
          ssid: penguinpages
          wifi_password: $PASSWORD # Set in SCM
        baclava:
          ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
    arduino_boards:
      hosts:
        t01:
          SN: 24EC4A2365A4
          ip_address: 172.16.100.121
          ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
        t02:
          SN: 48CA435CF294
          ip_address: 172.16.100.122
          ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
        t03:
          SN: 1234567
          ip_address: 172.16.100.123
          ansible_ssh_common_args: '-o StrictHostKeyChecking=no'

```

I would expect output to be if input was “t02” then SN fact set would be SN=48CA435CF294

But I have tried many many ways and keep getting back bad results.

Error:

```auto
<triton> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="ansible"' -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o 'ControlPath="/root/.ansible/cp/0901649b71"' triton '/bin/sh -c '"'"'rm -f -r /home/ansible/.ansible/tmp/ansible-tmp-1745329422.0644264-70-103872123862905/ > /dev/null 2>&1 && sleep 0'"'"''
<triton> (0, b'', b"OpenSSH_9.9p2, OpenSSL 3.3.3 11 Feb 2025\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 22: include /etc/ssh/ssh_config.d/*.conf matched no files\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/root/.ssh/known_hosts'\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/root/.ssh/known_hosts2'\r\ndebug1: auto-mux: Trying existing master at '/root/.ansible/cp/0901649b71'\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 58\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: mux_client_read_packet_timeout: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n")
ok: [triton]
TASK [serial-scan : Set Ansible facts for shell variables] *********************
task path: /builds/penguinpages/infra/shuffleboard/roles/serial-scan/tasks/main.yaml:1
ok: [triton] => {
    "ansible_facts": {
        "tabletop_number": "t01"
    },
    "changed": false
}
TASK [serial-scan : Output Ansible facts] **************************************
task path: /builds/penguinpages/infra/shuffleboard/roles/serial-scan/tasks/main.yaml:9
ok: [triton] => {
    "msg": [
        "tabletop_number: t01"
    ]
}
TASK [serial-scan : Search ansible hosts and set fact based on SN] *************
task path: /builds/penguinpages/infra/shuffleboard/roles/serial-scan/tasks/main.yaml:18
ok: [triton] => (item=t01) => {
    "ansible_facts": {
        "sn_value": "24EC4A2365A4"
    },
    "ansible_loop_var": "item",
    "changed": false,
    "item": "t01"
}
skipping: [triton] => (item=t02) => {
    "ansible_loop_var": "item",
    "changed": false,
    "false_condition": "item == tabletop_number",
    "item": "t02",
    "skip_reason": "Conditional result was False"
}
skipping: [triton] => (item=t03) => {
    "ansible_loop_var": "item",
    "changed": false,
    "false_condition": "item == tabletop_number",
    "item": "t03",
    "skip_reason": "Conditional result was False"
}
<snip>
}
TASK [serial-scan : Print SN value] ********************************************
task path: /builds/penguinpages/infra/shuffleboard/roles/serial-scan/tasks/main.yaml:25
fatal: [triton]: FAILED! => {
    "msg": "The task includes an option with an undefined variable.. 'list object' has no attribute 'ansible_facts'\n\nThe error appears to be in '/builds/penguinpages/infra/shuffleboard/roles/serial-scan/tasks/main.yaml': line 25, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: Print SN value\n ^ here\n"
}
PLAY RECAP *********************************************************************
triton : ok=4 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 

```

Its like it loops through “hosts” and finds the value "“sn\_value”: “24EC4A2365A4” but never can return it as a fact or usable. I have tried dozens of means to get this done and nothing can end in "input = “hostname” and return a sub fact under that host of field “SN” set as a fact.

---

<div class="post-metadata">

**Author:** ![binbashroot](https://avatars.discourse-cdn.com/v4/letter/b/ad7895/32.png) [@binbashroot](https://forum.ansible.com/u/binbashroot)\
**Post date:** [April 22, 2025, 4:58pm UTC](https://forum.ansible.com/t/using-set-fact-from-hosts-yaml-json-format/41874/2 "2025-04-22T16:58:35Z")

</div>

You don’t need sn\_result.results.ansible\_facts.sn\_value. It’s not valid var name. Try this:

```auto
- name: Debug
  ansible.builtin.debug:
      msg: " SN value: {{ sn_value }}"

```

Edit: Grammar

---

<div class="post-metadata">

**Author:** ![penguinpages](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/penguinpages/32/8179_2.png) [@penguinpages](https://forum.ansible.com/u/penguinpages)\
**Post date:** [April 22, 2025, 5:38pm UTC](https://forum.ansible.com/t/using-set-fact-from-hosts-yaml-json-format/41874/3 "2025-04-22T17:38:18Z")

</div>

Thanks for response.. I agree that the value should be simple call value “SN”

But not sure I track how your code example will help. Ex: if extra var passed is “tabletop\_number=t02” then it should return fact “SN: 48CA435CF294” and as important I need ot set the fact so later on I can make a call like next stage:

```auto
- name: Run arduino-cli board list command
  command: arduino-cli board list --format json
  register: arduino_cli_output

- name: Set Arduino CLI output as a fact
  set_fact:
    arduino_cli_data: "{{ arduino_cli_output.stdout | from_json }}"

- name: Find matching serial number and set address
  set_fact:
    address: "{{ item.port.address }}"
  loop: "{{ arduino_cli_data.detected_ports }}"
  when: item.port.properties is defined and item.port.properties.serialNumber is defined and item.port.properties.serialNumber == tabletop_serial

- name: Output address fact address which is TTY port for {{ tabletop_number }}
  debug:
    msg:
      - "{{ tabletop_number }}"
      - "{{ tabletop_serial }}"
      - "{{ board_id }}"
      - "{{ address }}"

```

because I have to use key value of SN to then learn what TTY port the device is on to flash firmware to board.

---

<div class="post-metadata">

**Author:** ![binbashroot](https://avatars.discourse-cdn.com/v4/letter/b/ad7895/32.png) [@binbashroot](https://forum.ansible.com/u/binbashroot)\
**Post date:** [April 22, 2025, 6:30pm UTC](https://forum.ansible.com/t/using-set-fact-from-hosts-yaml-json-format/41874/4 "2025-04-22T18:30:44Z")

</div>

> [@penguinpages](#):
>
> `arduino-cli board list --format json`

You don’t need to loop your inventory hosts to get your data. Since you’re specifying the inventory file and using limit. The hosts and vars from the inventory are still availalble to you:

So I have to make some assumptions since I don’t see your full playbook, but I am using the extra\_vars passed at run time

```auto
- hosts: all
  gather_facts: true
  become: true
  tasks:
    
     # You don't need to loop to set a fact here. You can just set a fact or call it natively
    - name: Print the parameters of the defined tabletop_number value
      ansible.buiitin.debug:
          msg: 
           - "{{ hostvars['tabletop_number']['SN'] }}"
           - "{{ hostvars['tabletop_number']['ip_address'] }}"

```

Now since I don’t know what you’re data from the cli output is, but assuming it’s a dictiionary, I would then use selectattr and map to get the board\_id when the tabletop\_serial number matches a key value that you’re looking for.

Maybe these two articles can help you towards your goals\>

> **[How to work with a list of dictionaries in Ansible](https://www.redhat.com/en/blog/ansible-jinja-lists-dictionaries)**
>
> My friend and peer Roberto Nozaki recently wrote How to work with lists and dictionaries in Ansible. After reading it, this felt like a perfect opportun...

> **[No wizardry needed to use Ansible's magic variable 'hostvars'](https://www.redhat.com/en/blog/hostvars-magic-variable-ansible)**
>
> First, some basic definitions and rules: In Ansible, there are a set of reserved variables called magic variables. Because they are reserved, they canno...

---

<div class="post-metadata">

**Author:** ![wayt](https://avatars.discourse-cdn.com/v4/letter/w/3be4f8/32.png) [@wayt](https://forum.ansible.com/u/wayt)\
**Post date:** [April 23, 2025, 4:59am UTC](https://forum.ansible.com/t/using-set-fact-from-hosts-yaml-json-format/41874/5 "2025-04-23T04:59:33Z")

</div>

At risks of answering your question without answering your question; the intention of this work is because you need to know which Linux device maps to which Arduino when more than one is plugged in.

Thankfully we have persistent device names and udev in Linux. Out of the box you can reliably and persistently identify USB devices by their path. Look inside `/dev/serial/by-path` to understand what devices are available. While convenient this may not work with Arduino tooling specifically; historically there were bugs where it expected certain device names. We can rely on UDEV rules to have symbolic links created when a device is inserted by fields like serial number.

Here I map a specific Arduino Uno R3 by serial number to `/dev/ttyACM89` by placing an entry in a file called `/etc/udev/rules.d/99-arduino.rules`:

```auto
SUBSYSTEM=="usb", ATTR{serial}=="88354323435351F001B2", SYMLINK+="ttyACM89"

```

In the line of the actual question; if a host in the inventory has an `inventory_hostname` of `t02` you can access it at any point by using the `hostvars` magic variable (dictionary) by `hostvars["t02"].SN` or:

```yaml
- ansible.builtin.debug:
    msg: >-
      The serial number for board {{ b }} is {{ hostvars[b].SN }}
  vars:
    b: t02

```

If you want to persist discovery information about a different host in the inventory and saving it that is when you use `delegate_to` and `delegate_facts: true` like:

```yaml
- ansible.builtin.set_fact:
    tty: /dev/ttyACM0
  delegate_to: t02
  delegate_facts: true

```

---

<div class="post-metadata">

**Author:** ![penguinpages](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/penguinpages/32/8179_2.png) [@penguinpages](https://forum.ansible.com/u/penguinpages)\
**Post date:** [April 23, 2025, 1:31pm UTC](https://forum.ansible.com/t/using-set-fact-from-hosts-yaml-json-format/41874/6 "2025-04-23T13:31:51Z")

</div>

Two things

1. Never thought of idea to use udev to set hard path for tty. Great idea for my use case

2. My issue with using vars I think was not just format, but also idea that I could use them in other tasks as direct variables. I first have to set as fact the item.

Ex: (fails)

```auto
- name: Set Arduino CLI output as a fact
  set_fact:
    arduino_cli_data: "{{ arduino_cli_output.stdout | from_json }}"

- name: Find matching serial number and set address
  set_fact:
    address: "{{ item.port.address }}"
  loop: "{{ arduino_cli_data.detected_ports }}"
  when: item.port.properties is defined and item.port.properties.serialNumber is defined and item.port.properties.serialNumber == {{ hostvars[tabletop_number].ip_address }}

- name: Output address fact address which is TTY port for {{ tabletop_number }}
  debug:
    msg:
      - "{{ address }}"

```

but if I set fact first.. it works

```auto
- name: Set Ansible facts
  ansible.builtin.set_fact:
	tabletop_number: "{{ lookup('env', 'tabletop_number') | string }}"
    tabletop_number: "{{ lookup('env', 'tabletop_number') | string }}"
    tabletop_serial: "{{ hostvars[tabletop_number].SN }}"

- name: Run arduino-cli board list command
  command: arduino-cli board list --format json
  register: arduino_cli_output

- name: Set Arduino CLI output as a fact
  set_fact:
    arduino_cli_data: "{{ arduino_cli_output.stdout | from_json }}"

- name: Find matching serial number and set address
  set_fact:
    address: "{{ item.port.address }}"
  loop: "{{ arduino_cli_data.detected_ports }}"
  when: item.port.properties is defined and item.port.properties.serialNumber is defined and item.port.properties.serialNumber == tabletop_serial

- name: Output address fact address which is TTY port for {{ tabletop_number }}
  debug:
    msg:
	  - "{{ tabletop_number }}"
      - "{{ tabletop_serial }}"
      - "{{ address }}"

```

Thanks for help

---

<div class="post-metadata">

**Author:** ![binbashroot](https://avatars.discourse-cdn.com/v4/letter/b/ad7895/32.png) [@binbashroot](https://forum.ansible.com/u/binbashroot)\
**Post date:** [April 23, 2025, 2:09pm UTC](https://forum.ansible.com/t/using-set-fact-from-hosts-yaml-json-format/41874/7 "2025-04-23T14:09:52Z")

</div>

Without seeing youor arduino\_cli\_output, it’s hard to provide a thorough answer. However, based on my experience, using the set\_fact from loop may be unneccessary. You should be able to retrieve that data using selectattr or select filters depending on the data structure without looping and without having to set\_fact. Even if you use set\_fact to save keystrokes, you can still probably get away without having to loop.
