If you really want to append to content, you will need to use the set_fact module. But if you just want to use the merged lists it is as easy as this:

{{ list1 + list2 }}

With set_fact it would look like this:

- set_fact:
    list_merged: "{{ list1 + list2 }}"

NOTE: If you need to do additional operations on the concatenated lists be sure to group them like so:

- set_fact:
    list_merged: "{{ (list1 + list2) | ... }}"
Answer from udondan on Stack Overflow
Discussions

Combine Lists of Objects in Ansible - DevOps Stack Exchange
I'm really trying to merge lists of objects along with defaults so I can loop over them and create resources in kubernetes. I've gotten it down to a simple(ish) playbook and will post it here. - h... More on devops.stackexchange.com
🌐 devops.stackexchange.com
Combining lists - Ansible Project - Ansible
I’d like to be able to do the following, hopefully someone can point me to a simple way to do it. I have two lists, one contains vars for all hosts another vars for a specific host. I’d like to join them into a single list that can be iterated over in a template. More on forum.ansible.com
🌐 forum.ansible.com
0
February 20, 2014
merge - Ansible - How to combine multiple disparate lists into to use in tasks loop - Stack Overflow
Can someone suggest what module should I use or look at to combine the lists which are lists of lists in the format that can be used to generate and execute the kafka-acl command ? More on stackoverflow.com
🌐 stackoverflow.com
ansible - How to combine two lists together? - Stack Overflow
I think I've found a cleaner, easier way to deal with these kind of things. Ansible runs all strings through jinja and then tries to load the result as yaml. More on stackoverflow.com
🌐 stackoverflow.com
January 27, 2016
🌐
Reddit
reddit.com › r/ansible › combine and merge lists... how?
r/ansible on Reddit: Combine and Merge Lists... How?
February 2, 2021 -

Hi all,

Looking for some assistance please...

I am attempting to not only append two lists together ( varCombined: "{{ var1 + var2}}" ) but also merge the lists so that there are no duplicates.

My playbook is as follows:

- name: testing
  hosts: localhost
  tasks:
    - name: set vars
      set_fact:
        paths_blah:
          - includeFilePath: "/path1"
            excludeFilePaths:
              - "/path1/aaa"
              - "/path1/bbb"
          - includeFilePath: "/path2"
            excludeFilePaths:
              - "/path2/aaa"
        paths_blob:
          - includeFilePath: "/path3"
            excludeFilePaths:
              - "/path3/aaa"
              - "/path3/bbb"
          - includeFilePath: "/path1"
            excludeFilePaths:
              - "/path1/zzz"
        paths_berry:
          - includeFilePath: "/path4"
            excludeFilePaths:
              - "/path4/aaa"
              - "/path4/bbb"
          - includeFilePath: "/path2"
            excludeFilePaths:
              - "/path1/zzz"

    - set_fact:
        paths_combined: "{{ paths_blah + paths_blob + paths_berry }}"

    - name: print vars paths_combined
      debug:
        var: paths_combined

.. when run, the output of which is:

PLAY [print a message to screen] *****************************************************************************************************************

<snip>

TASK [print vars paths_combined] *****************************************************************************************************************
ok: [localhost] => {
    "paths_combined": [
        {
            "excludeFilePaths": [
                "/path1/aaa", 
                "/path1/bbb"
            ], 
            "includeFilePath": "/path1"
        }, 
        {
            "excludeFilePaths": [
                "/path2/aaa"
            ], 
            "includeFilePath": "/path2"
        }, 
        {
            "excludeFilePaths": [
                "/path3/aaa", 
                "/path3/bbb"
            ], 
            "includeFilePath": "/path3"
        }, 
        {
            "excludeFilePaths": [
                "/path1/zzz"
            ], 
            "includeFilePath": "/path1"
        }, 
        {
            "excludeFilePaths": [
                "/path4/aaa", 
                "/path4/bbb"
            ], 
            "includeFilePath": "/path4"
        }, 
        {
            "excludeFilePaths": [
                "/path1/zzz"
            ], 
            "includeFilePath": "/path2"
        }
    ]
}

.. you can see the lists are only appended, not merged: path1 and path2 appear twice in the list.

The desired output is a list with only unique items, merged rather than appended. So taking the above example the desired output would be:

paths_combined:
  - includeFilePath: "/path1"
    excludeFilePaths:
      - "/path1/aaa"
      - "/path1/bbb"
      - "/path1/zzz"
  - includeFilePath: "/path2"
    excludeFilePaths:
      - "/path2/aaa"
      - "/path2/zzz"
  - includeFilePath: "/path3"
    excludeFilePaths:
      - "/path3/aaa"
      - "/path3/bbb"
  - includeFilePath: "/path4"
    excludeFilePaths:
      - "/path4/aaa"
      - "/path4/bbb"

Can anybody suggest how I may accomplish this?

Thanks!

🌐
Ansible
docs.ansible.com › ansible › latest › collections › ansible › builtin › together_lookup.html
ansible.builtin.together lookup – merges lists into synchronized list — Ansible Community Documentation
- name: item.0 returns from the 'a' list, item.1 returns from the '1' list ansible.builtin.debug: msg: "{{ item.0 }} and {{ item.1 }}" with_together: - ['a', 'b', 'c', 'd'] - [1, 2, 3, 4]
Top answer
1 of 2
1

loop requires a valid list. But we got here is,

[({'i_am': 'sam', 'eggs_ham': '456'}, AnsibleUndefined),
 ({'i_am': 'sam', 'eggs_ham': 'ham'}, {'transport': 'train'}),
 ({'i_am': 'eggs', 'eggs_ham': '456'}, AnsibleUndefined)]

Since no value is assigned to transport_fact_results when common_thing_2 is not defined.

if you remove the when condition it will work.

CODE:

- hosts: localhost
  vars:
    default_thing_1: 123
    default_thing_2: 456
    default_transport: train
    list_of_things:
      - name: foo
        common_thing_1: "sam"
      - name: bar
        common_thing_1: "sam"
        common_thing_2: "ham"
      - name: biz
        common_thing_1: "eggs"
  tasks:
    - name: Set the Things Facts
      set_fact:
        thing:
          i_am: "{{ item.common_thing_1 | default(default_thing_1) }}"
          eggs_ham: "{{ item.common_thing_2 | default(default_thing_2) }}"
      loop: "{{ list_of_things }}"
      register: things_fact_results
    - name: Set Optional Things Facts
      set_fact:
        thing:
          transport: "{{ item.transport | default(default_transport) }}"
      when: "item.common_thing_2 is defined and item.common_thing_2 == 'ham'"
      loop: "{{ list_of_things }}"
      register: transport_fact_results
    - name: Debug the Facts
      debug:
        msg: "{{ item.0 | combine(item.1|default({})) }}"
      loop: "{{
        things_fact_results.results |
        map(attribute='ansible_facts.thing') | list |
        zip(
          transport_fact_results.results |
          map(attribute='ansible_facts.thing') | list
        ) | list
      }}"

OUTPUT:

PLAY [localhost] ************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************************************************************************
ok: [localhost]

TASK [Set the Things Facts] *************************************************************************************************************************************************************************************
ok: [localhost] => (item={u'common_thing_1': u'sam', u'name': u'foo'})
ok: [localhost] => (item={u'common_thing_1': u'sam', u'common_thing_2': u'ham', u'name': u'bar'})
ok: [localhost] => (item={u'common_thing_1': u'eggs', u'name': u'biz'})

TASK [Set Optional Things Facts] ********************************************************************************************************************************************************************************
ok: [localhost] => (item={u'common_thing_1': u'sam', u'name': u'foo'})
ok: [localhost] => (item={u'common_thing_1': u'sam', u'common_thing_2': u'ham', u'name': u'bar'})
ok: [localhost] => (item={u'common_thing_1': u'eggs', u'name': u'biz'})

TASK [Debug the Facts] ******************************************************************************************************************************************************************************************
ok: [localhost] => (item=[{u'eggs_ham': u'456', u'i_am': u'sam'}, {u'transport': u'train'}]) => {
    "msg": {
        "eggs_ham": "456", 
        "i_am": "sam", 
        "transport": "train"
    }
}
ok: [localhost] => (item=[{u'eggs_ham': u'ham', u'i_am': u'sam'}, {u'transport': u'train'}]) => {
    "msg": {
        "eggs_ham": "ham", 
        "i_am": "sam", 
        "transport": "train"
    }
}
ok: [localhost] => (item=[{u'eggs_ham': u'456', u'i_am': u'eggs'}, {u'transport': u'train'}]) => {
    "msg": {
        "eggs_ham": "456", 
        "i_am": "eggs", 
        "transport": "train"
    }
}

PLAY RECAP ******************************************************************************************************************************************************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
2 of 2
0

So the way I figured it out was to put the when condition into Jinja.

- hosts: localhost
  vars:
    default_thing_1: 123
    default_thing_2: 456
    default_transport: train
    list_of_things:
      - name: foo
        common_thing_1: "sam"
      - name: bar
        common_thing_1: "sam"
        common_thing_2: "ham"
      - name: biz
        common_thing_1: "eggs"
  tasks:
    - name: Set the Things Facts
      set_fact:
        thing:
          i_am: "{{ item.common_thing_1 | default(default_thing_1) }}"
          eggs_ham: "{{ item.common_thing_2 | default(default_thing_2) }}"
      loop: "{{ list_of_things }}"
      register: things_fact_results
    - name: Set Optional Things Facts
      set_fact:
        thing: |
          {% if item.common_thing_2 is defined and item.common_thing_2 == 'ham' %}
          { "transport": "{{ item.transport | default(default_transport) }}" }
          {% else %}
          {}
          {% endif %}
      loop: "{{ list_of_things }}"
      register: transport_fact_results
    - name: Debug the Facts
      debug:
        msg: "{{ item.0 | combine(item.1) }}"
      loop: "{{
        things_fact_results.results |
        map(attribute='ansible_facts.thing') | list |
        zip(
          transport_fact_results.results |
          map(attribute='ansible_facts.thing') | list
        ) | list
      }}"

This is kinda sloppy but does allow me to specify the else condition to set the result over every iteration to something instead of AnsibleUndefined.

I don't really like this solution but it works.

$ ansible-playbook playbook.yml 
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] *******************************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************
ok: [localhost]

TASK [Set the Things Facts] ********************************************************************************************************************************************************
ok: [localhost] => (item={'name': 'foo', 'common_thing_1': 'sam'})
ok: [localhost] => (item={'name': 'bar', 'common_thing_1': 'sam', 'common_thing_2': 'ham'})
ok: [localhost] => (item={'name': 'biz', 'common_thing_1': 'eggs'})

TASK [Set Optional Things Facts] ***************************************************************************************************************************************************
ok: [localhost] => (item={'name': 'foo', 'common_thing_1': 'sam'})
ok: [localhost] => (item={'name': 'bar', 'common_thing_1': 'sam', 'common_thing_2': 'ham'})
ok: [localhost] => (item={'name': 'biz', 'common_thing_1': 'eggs'})

TASK [Debug the Facts] *************************************************************************************************************************************************************
ok: [localhost] => (item=[{'i_am': 'sam', 'eggs_ham': '456'}, {}]) => {
    "msg": {
        "eggs_ham": "456",
        "i_am": "sam"
    }
}
ok: [localhost] => (item=[{'i_am': 'sam', 'eggs_ham': 'ham'}, {'transport': 'train'}]) => {
    "msg": {
        "eggs_ham": "ham",
        "i_am": "sam",
        "transport": "train"
    }
}
ok: [localhost] => (item=[{'i_am': 'eggs', 'eggs_ham': '456'}, {}]) => {
    "msg": {
        "eggs_ham": "456",
        "i_am": "eggs"
    }
}

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

Now the transport key is defined in the one object but not the other. So I can pass the item off to a complex object that's part of an API call.

🌐
Ansible
docs.ansible.com › projects › ansible › latest › collections › community › general › lists_mergeby_filter.html
community.general.lists_mergeby filter – Merge two or more lists of dictionaries by a given attribute — Ansible Community Documentation
Merge two or more lists by attribute index. Optional parameters recursive and list_merge control the merging of the nested dictionaries and lists. The function merge_hash from ansible.utils.vars is used.
🌐
FreeKB
freekb.net › Article
Ansible - Combine or Merge a List using plus or zip
--- - hosts: localhost tasks: - set_fact: fruit: [ apple, banana, orange, grapes ] veggy: [ onion, pepper, tomato, carrot ] - set_fact: food: "{{ fruit | zip(veggy) | list}}" - debug: var: food ...
🌐
Ansible
docs.ansible.com › projects › ansible › latest › collections › community › general › docsite › filter_guide_abstract_informations_merging_lists_of_dictionaries.html
Merging lists of dictionaries — Ansible Community Documentation
If you have two or more lists of dictionaries and want to combine them into a list of merged dictionaries, where the dictionaries are merged by an attribute, you can use the community.general.lists_mergeby filter. ... The output of the examples in this section use the YAML callback plugin.
Find elsewhere
🌐
Ansible
forum.ansible.com › archives › ansible project
Combining lists - Ansible Project - Ansible
February 20, 2014 - I’d like to be able to do the following, hopefully someone can point me to a simple way to do it. I have two lists, one contains vars for all hosts another vars for a specific host. I’d like to join them into a single l…
🌐
Ansible
docs.ansible.com › projects › ansible › latest › collections › ansible › builtin › combine_filter.html
ansible.builtin.combine filter – combine two dictionaries — Ansible Community Documentation
These are the values key1=value1, key2=value2 and so on in the following example: input | ansible.builtin.combine(key1=value1, key2=value2, ...) Note · When keyword and positional parameters are used together, positional parameters must be listed before keyword parameters: input | ansible.builtin.combine(positional1, positional2, key1=value1, key2=value2) # ab => {'a':1, 'b':3, 'c': 4} ab: {{ {'a':1, 'b':2} | ansible.builtin.combine({'b':3, 'c':4}) }} many: "{{ dict1 | ansible.builtin.combine(dict2, dict3, dict4) }}" # defaults => {'a':{'b':3, 'c':4}, 'd': 5} # customization => {'a':{'c':20}} # final => {'a':{'b':3, 'c':20}, 'd': 5} final: "{{ defaults | ansible.builtin.combine(customization, recursive=true) }}" Issue Tracker ·
🌐
Ansible
docs.ansible.com › projects › ansible › devel › collections › ansible › builtin › zip_filter.html
ansible.builtin.zip filter – combine list elements — Ansible Community Documentation
These are the values key1=value1, key2=value2 and so on in the following example: input | ansible.builtin.zip(key1=value1, key2=value2, ...) ... When keyword and positional parameters are used together, positional parameters must be listed before keyword parameters: input | ansible.builtin.zip(positional1, positional2, key1=value1, key2=value2)
🌐
Google Groups
groups.google.com › g › ansible-project › c › sFI4klf0EwY
HowTo: combine two lists in vars section/file
March 30, 2016 - to Ansible Project · Hi John Buxton, thanks for your reply. Indeed that worked. -- - name: combine lists hosts: localhost vars: - level1keyA: - hostname: foo state: present path: - left - hostname: bar state: present path: - righthere - "{{ ['righthere'] |union (level1keyB) }}" - level1keyB: - right - middle - top tasks: - name: result of level1keyA debug: msg: "{{ level1keyA }}" - name: result of level1keyB debug: msg: "{{ level1keyB }}" :~/ansible ansible-playbook test.yml PLAY [combine lists] *********************************************************** TASK [setup] *************************
🌐
Ansiblemiddleware
collections.ansiblemiddleware.com › amq › main › plugins › lists_mergeby.html
lists_mergeby – Merge two or more lists of dictionaries by a given attribute — AMQ Ansible Collection documentation
- name: Merge two lists ansible.builtin.debug: msg: >- {{ list1 | middleware_automation.amq.lists_mergeby( list2, 'index', recursive=True, list_merge='append' ) }}" vars: list1: - index: a value: 123 - index: b value: 42 list2: - index: a foo: bar - index: c foo: baz # Produces the following list of dictionaries: # { # "index": "a", # "foo": "bar", # "value": 123 # }, # { # "index": "b", # "value": 42 # }, # { # "index": "c", # "foo": "baz" # }
🌐
Crisp's Blog
blog.crisp.se › 2016 › 10 › 20 › maxwenzin › how-to-append-to-lists-in-ansible
How to append to lists in Ansible - Crisp's Blog
February 8, 2019 - The other approach would be to take the json file, parse it into a list, probably using a jinja2 filter, and then iterate on that list using `with_items` and call whatever module you want (like command or ping). ... Hi again Hemant! I decided to try coding it. This is the latter approach above. See: https://github.com/betrcode/ansible-parse-json-and-loop
🌐
Stack Overflow
stackoverflow.com › questions › 68796480 › ansible-how-to-combine-multiple-disparate-lists-into-to-use-in-tasks-loop
merge - Ansible - How to combine multiple disparate lists into to use in tasks loop - Stack Overflow
combined_lists: [ {'user1', 'Read','Topic1'}, {'user1', 'Read','Topic2'}, {'user2', 'Read','Topic3'}, {'user2', 'Read','Topic4'}, {'user2', 'Read','Topic5'}, {'user2', 'Write','Topic3'}, {'user2', 'Write','Topic4'}, {'user2', 'Write','Topic5'} ]
🌐
Ansible
docs.ansible.com › ansible › latest › playbook_guide › playbooks_filters.html
Using filters to manipulate data — Ansible Community Documentation
If list_merge='append_rp', arrays from the right hash will be appended to the ones in the left hash. Elements of arrays in the left hash that are also in the corresponding array of the right hash will be removed (“rp” stands for “remove present”). Duplicate elements that aren’t in ...
🌐
Ansible
docs.ansible.com › ansible › 2.9 › plugins › lookup › together.html
together – merges lists into synchronized list — Ansible Documentation
May 27, 2022 - See the latest Ansible community documentation . For Red Hat customers, see the Red Hat AAP platform lifecycle. ... To clarify with an example, [ ‘a’, ‘b’ ] and [ 1, 2 ] turn into [ (‘a’,1), (‘b’, 2) ] This is basically the same as the ‘zip_longest’ filter and Python function · Any ‘unbalanced’ elements will be substituted with ‘None’ · - name: item.0 returns from the 'a' list, item.1 returns from the '1' list debug: msg: "{{ item.0 }} and {{ item.1 }}" with_together: - ['a', 'b', 'c', 'd'] - [1, 2, 3, 4]
Top answer
1 of 3
2

I wrote two ansible filters to tackle this problem: zip and todict which are available in my repo at https://github.com/ahes/ansible-filter-plugins

Sample ansible playbook:

- hosts: localhost
  vars:
    users:
      - { name: user1 }
      - { name: user2 }
  tasks:
    - name: Get uids for users
      command: id -u {{ item.name }}
      register: uid_results
      with_items: users

    - set_fact:
        uids: "{{ uid_results.results | map(attribute='stdout') | todict('uid') }}"

    - set_fact:
        users: "{{ users | zip(uids) }}"

    - name: Show users with uids
      debug: var=users

Result would be:

TASK [Show users with uids] ****************************************************
ok: [localhost] => {
    "users": [
        {
            "name": "user1",
            "uid": "1000"
        },
        {
            "name": "user2",
            "uid": "2000"
        }
    ]
}
2 of 3
2

I think I've found a cleaner, easier way to deal with these kind of things. Ansible runs all strings through jinja and then tries to load the result as yaml. This is because jinja only outputs strings so that allows it to load a data structure from a variable if there is one.

So any valid yaml in a string is loaded as a data structure -- so if you template valid yaml it will get loaded as data.

Trying to template correct yaml in the conventional, human form is tricky. But yaml loads all json. So, json is easier because there is no need to worry about whitespace. One bonus though, yaml does not care about extra commas, so that makes templating it easier.

In this case here is the playbook from the top answer rewritten to use this method.

- hosts: localhost
  vars:
    users:
      - { name: "user1" }
      - { name: "user2" }
  tasks:
    - name: Get uids for users
      command: id -u {{ item.name }}
      register: uid_results
      loop: "{{ users }}"

    - name: Show users with uids
      debug: var=users_with_uids
      vars:
        users_with_uids: |
          [
            {% for user_dict, uid in users | zip(uids) %}
              {
                "name": {{ user_dict['name'] | to_json }},
                "uid": {{ uid | to_json }},
              },
            {% endfor %}
          ]
        uids: "{{ uid_results.results | map(attribute='stdout') }}"

Notes

The | character tells yaml to load a multi-line string. Instead of putting the variables in quotes I use the to_json filter which will quote it and, more importantly, automatically escape anything in the variable that needs escaping. Also, remember commas after list or dictionary elements.

The results should be the same:

TASK [Show users with uids] ************************************************************
ok: [localhost] => {
    "users_with_uids": [
        {
            "name": "user1",
            "uid": "1000"
        },
        {
            "name": "user2",
            "uid": "1001"
        }
    ]
}

One more thing

I like to use the yaml callback especially for testing this. That way if my json-looking yaml doesn't get loaded I'll see a json-like structure. Otherwise it will come back in normal looking yaml if it was loaded. You can enable this by environment variable -- export ANSIBLE_STDOUT_CALLBACK=community.general.yaml.