Question

Is it possible to set a fact containing a list in Ansible using set_fact? What's the correct syntax for it?

Was it helpful?

Solution 2

Indeed it is. You need to quote the entire list though:

- name: set fact
  set_fact: foo="[ 'one', 'two', 'three' ]"

- name: debug
  debug: msg={{ item }}
  with_items: foo

The above tasks should generate the following output:

TASK: [set fact] **************************************************************
ok: [localhost]

TASK: [debug] *****************************************************************
ok: [localhost] => (item=one) => {
    "item": "one",
    "msg": "one"
}
ok: [localhost] => (item=two) => {
    "item": "two",
    "msg": "two"
}
ok: [localhost] => (item=three) => {
    "item": "three",
    "msg": "three"
}

OTHER TIPS

Yes, this is possible. As mentioned in another answer, you can set an array using double quotes, like so:

- name: set foo fact to a list
  set_fact: 
    foo: "[ 'one', 'two', 'three' ]"

However, I thought I'd create another answer to indicate that it's also possible to add to an existing array, like so:

- name: add items to foo list fact
  set_fact: 
    foo: "{{foo}} + [ 'four' ]"

Combining these and adding debugging as a playbook (which I'm calling facts.yml) like so:

---
- name: test playbook
  gather_facts: false
  hosts: localhost
  tasks:
    - name: set foo fact to an array
      set_fact: 
        foo: "[ 'one', 'two', 'three' ]"
    - debug: 
        var: foo
    - name: add items to foo array fact
      set_fact: 
        foo: "{{foo}} + [ 'four' ]"
    - debug: 
        var: foo

Produces (via ansible-playbook facts.yml) the following:

PLAY [test playbook] ******************************************************** 

TASK: [set foo fact to an array] ********************************************
ok: [localhost]

TASK: [debug var=foo] *******************************************************
ok: [localhost] => {
    "foo": [
        "one", 
        "two", 
        "three"
    ]
}

TASK: [add items to foo array fact] *****************************************
ok: [localhost]

TASK: [debug var=foo] *******************************************************
ok: [localhost] => {
    "foo": [
        "one", 
        "two", 
        "three", 
        "four"
    ]
}

PLAY RECAP ****************************************************************** 
localhost                  : ok=4    changed=0    unreachable=0    failed=0   

Adding to the already given answers I would like to show you a different way to define a list with set_fact using the regular YAML syntax instead of the custom format which Ansible core folks like to use in their docs. This custom format, like in this case, has shown lead to confusion.

Consider this example:

- name: 'Set the foo variable to a static list using the YAML syntax'
  set_fact:
    foo:
      - 'one'
      - 'two'
      - 'three'

Straight forward, right? Just as you would do in any normal YAML document. So why not use it in Ansible YAML tasks file too?

About the combination of lists mentioned by @lindes-hw. There is more than one way to do this. The following examples use Jinja2 syntax to define the list:

- name: 'Set the foo variable to a combined static list using the Jinja2 syntax'
  set_fact:
    foo: '{{ [ "one" ] + [ "two", "three" ] }}'

- name: 'Set the foo variable to a combined static list using the Jinja2 syntax and Jinja2 filters'
  set_fact:
    foo: '{{ [ "one" ] | union([ "two", "three" ]) }}'

The second example uses the union filter. Refer to the set theory filters in the Ansible docs.

I had a similar requirement to create list of server objects based on list of IP addresses.

vars:
  server_ips: one,two,three
tasks:
  - name: build items list
    set_fact: 
      foo: "{{foo|default([]) + [{'ip': {'addr': item, 'type': 'V4'}}] }}"
    with_items: "{{server_ips.split(',')}}"
  - debug:
      msg: "{{ foo }}"

Gives following output

TASK [setup] *******************************************************************
ok: [localhost]

TASK [build items list] ********************************************************
ok: [localhost] => (item=one)
ok: [localhost] => (item=two)
ok: [localhost] => (item=three)

TASK [debug] *******************************************************************
ok: [localhost] => {
    "msg": [
        {
            "ip": {
                "addr": "one", 
                "type": "V4"
            }
        }, 
        {
            "ip": {
                "addr": "two", 
                "type": "V4"
            }
        }, 
        {
            "ip": {
                "addr": "three", 
                "type": "V4"
            }
        }
    ]
}

I don't know if the functionality changed --- it possibly did in 2.5 --- but it looks to me like this code

with_items: foo

in the example doesn't work any more you have to use

with_items: "{{ foo }}"

I'm using Ansible 2.5.2. I was getting this (incorrect) output:

ok:[localhost] => (item=None) => {
    "msg":"foo"
}

I found the updated syntax in the docs here:
https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#with-items

like this

- hosts: all
  tasks:
  - set_fact:
      redis_list: |
        {%- set list = [] -%}
        {%- for index in range(1, 5 ) -%}
          {{ list.append(index)  }}
        {%- endfor -%}
        {{ list }}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top