'Append a string to a List in Ansible

I'm trying to append a string to a list in ansible , so basically i'll be building a payload to delete few of the topology records in the F5 GTM network gear.

I was able to create one single list which includes all the output of the respective topology record. For the each line of the output i need to append with a string 'delete'.

- name: Lookup Topology Records  
  bigip_command:
    user: admin
    password: password
    server: gtm.abc.com
    commands: "list gtm topology | grep -i '{{ item }}'"
    warn: no
    validate_certs: no
  register: topology_info
  delegate_to: localhost
  loop: "{{ gtm_pool }}"
  
- debug: var=topology_info
 
- name: Sanitize the Topology records of the Pool
  set_fact:
    clean_topology_info: "{{ clean_topology_info | default ([]) + item.stdout_lines  }}"
  loop: "{{ topology_info.results }}"
  
- debug: var=clean_topology_info
 
 
- name: Sanitized Topology Info
  vars:
    topology_item: "{{ item }}"
   set_fact:
     sanitized_topology_info: "{{ sanitized_topology_info | default ([]) + topology_item }}"
  loop: "{{ clean_topology_info }}"
  
- name: Build payload to delete the Topology Record
  set_fact:
    topology_payload: "{{ topology_payload | default([]) + ['delete'] + item }}"
  loop: "{{ clean_topology_info }}"
  
- debug: var=topology_payload

------------------------------------------------------------
Debug outputs(stdout_lines) as below :-

"gtm_pool": [
        "test-poo1", 
        "test-pool2"
    ]

debug of "topology_info" :-

"stdout_lines": [
                    [
                        "gtm topology ldns: subnet 10.10.10.0/24 server: pool /Common/test-pool1 {", 
                        "gtm topology ldns: subnet 10.8.22.0/24 server: pool /Common/test-pool1 {"
                    ]
                ]

"stdout_lines": [
                    [
                        "gtm topology ldns: subnet 0.0.0.0/0 server: pool /Common/test-pool2 {"
                    ]


debug of "clean_topology_info":-

"clean_topology_info": [
        [
                        "gtm topology ldns: subnet 10.10.10.0/24 server: pool /Common/test-pool1 {", 
                        "gtm topology ldns: subnet 10.8.22.0/24 server: pool /Common/test-pool1 {", 
        ], 
        [
            "gtm topology ldns: subnet 0.0.0.0/0 server: pool /Common/test-pool2 {",
        ]
    ]

debug of "sanitized_topology_info":-

"sanitized_topology_info": [
             "gtm topology ldns: subnet 10.10.10.0/24 server: pool /Common/test-pool1 {", 
                        "gtm topology ldns: subnet 10.8.22.0/24 server: pool /Common/test-pool1 {", 
           "gtm topology ldns: subnet 0.0.0.0/0 server: pool /Common/test-pool2 {"
        ]



debug of "topology_payload":-

"topology_payload": [
        "delete", 
        "gtm topology ldns: subnet 10.10.10.0/24 server: pool /Common/test-pool1 {", 
                        "gtm topology ldns: subnet 10.8.22.0/24 server: pool /Common/test-pool1 {",  
        "delete", 
       "gtm topology ldns: subnet 0.0.0.0/0 server: pool /Common/test-pool2 {"
    ]


Expected output of topology_payload should be like :-

Basically i need to append a string 'delete' infront of the each output.

"topology_payload": [ 
        "delete gtm topology ldns: subnet 10.10.10.0/24 server: pool /Common/test-pool1 {", 
                        "delete gtm topology ldns: subnet 10.8.22.0/24 server: pool /Common/test-pool1 {",  
       "delete gtm topology ldns: subnet 0.0.0.0/0 server: pool /Common/test-pool2 {"
    ]

Expected output of topology_payload should be like :-

Basically i need to append a string 'delete' infront of the each output.

topology_payload:
  - "delete gtm topology ldns: subnet 10.10.10.0/24 server: pool /Common/test-pool1"
  - "delete gtm topology ldns: subnet 10.8.22.0/24 server: pool /Common/test-pool1"
  - "delete gtm topology ldns: subnet 0.0.0.0/0 server: pool /Common/test-pool2"


Solution 1:[1]

Q: "Prepend a string 'delete' in front of each item."

A: Is this what you're looking for?

- hosts: localhost
  vars:
    info: ['a', 'b', 'c']
  tasks:
    - set_fact:
        payload: "{{ payload|default([]) + ['delete ' ~ item] }}"
      loop: "{{ info }}"
    - debug:
        var: payload

gives (abridged)

payload:
  - delete a
  - delete b
  - delete c

The same result can be achieved without iteration (credit @Romain DEQUIDT)

- hosts: localhost
  vars:
    info: ['a', 'b', 'c']
    payload: "{{ ['delete']|product(info)|map('join', ' ')|list }}"
  tasks:
    - debug:
        var: payload

Q: "Prepend a string 'delete' in front of each output that starts with 'gtm topology'."

A: Use the test search

- hosts: localhost
  gather_facts: false
  vars:
    info: ['a', 'gtm topology', 'c']
  tasks:
    - set_fact:
        payload: "{{ payload|default([]) + ['delete ' ~ item] }}"
      loop: "{{ info }}"
      when: item is search('^gtm topology')
    - debug:
        var: payload

gives (abridged)

payload:
  - delete gtm topology

The same result can be achieved without iteration

- hosts: localhost
  vars:
    info: ['a', 'gtm topology', 'c']
    info_gtm_topology: "{{ info|select('search', '^gtm topology') }}"
    payload: "{{ ['delete']|product(info_gtm_topology)|map('join', ' ')|list }}"
  tasks:
    - debug:
        var: payload

Q: "In addition to the conditions above, process the list till 'test-pool1' is found."

A: Use the test search also to find the index of the last item

  hosts: localhost
  vars:
    info: ['a', 'gtm topology 1', 'c', 'test-pool1', 'gtm topology 2', 'd']
    stop_regex: '.*pool1.*'
    stop_items: "{{ info|select('search', stop_regex) }}"
    stop_index: "{{ info.index(stop_items.0) }}"
  tasks:
    - set_fact:
        payload: "{{ payload|default([]) + ['delete ' ~ item] }}"
      loop: "{{ info[:stop_index|int] }}"
      when: item is search('^gtm topology')
    - debug:
        var: payload

gives (abridged)

payload:
  - delete gtm topology 1

The same result can be achieved without iteration

- hosts: localhost
  vars:
    info: ['a', 'gtm topology 1', 'c', 'test-pool1', 'gtm topology 2', 'd']
    stop_regex: '.*pool1.*'
    stop_items: "{{ info|select('search', stop_regex) }}"
    stop_index: "{{ info.index(stop_items.0) }}"
    info_gtm_topology: "{{ info[:stop_index|int]|select('search', '^gtm topology') }}"
    payload: "{{ ['delete']|product(info_gtm_topology)|map('join', ' ')|list }}"
  tasks:
    - debug:
        var: payload

As a side note, you can create custom filters. See filter_plugins

shell> cat filter_plugins/list_search.py 
import re


def list_search(l, x):
    r = re.compile(x)
    return list(filter(r.match, l))


def list_index(l, x, *i):
    if len(i) == 0:
        return l.index(x) if x in l else -1
    elif len(i) == 1:
        return l.index(x, i[0]) if x in l[i[0]:] else -1
    else:
        return l.index(x, i[0], i[1]) if x in l[i[0]:i[1]] else -1


class FilterModule(object):
    ''' Ansible filters for operating on lists '''

    def filters(self):
        return {
            'list_index': list_index,
            'list_search': list_search,
        }

The play below

- hosts: localhost
  vars:
    info: ['a', 'gtm topology 1', 'c', 'test-pool1', 'gtm topology 2', 'd']
    stop_regex: '.*pool1.*'
    stop_items: "{{ info|list_search(stop_regex) }}"
    stop_index: "{{ info|list_index(stop_items.0) }}"
    info_gtm_topology: "{{ info[:stop_index|int]|select('search', '^gtm topology') }}"
    payload: "{{ ['delete']|product(info_gtm_topology)|map('join', ' ')|list }}"
  tasks:
    - debug:
        var: payload

gives the same result

payload:
  - delete gtm topology 1

Solution 2:[2]

You can use the product filter to prefix or suffix list of item:

---
- hosts: localhost
  gather_facts: false
  vars:
      string: "prepend "
      list: ["value1", "value2", "value3"]
  tasks:
    - name: "prefix"
      set_fact:
          prefix_list: "{{ ["prefix_"] | product(list) | map('join') }}"
    - debug:
          msg: "{{ prefix_list }}"
    - name: "suffix"
      set_fact:
          suffix_list: "{{ list | product(["_suffix"]) | map('join') }}"
    - debug:
          msg: "{{ suffix_list }}"

Solution 3:[3]

You can use the map filter to apply a function to each element in a list. Combining it with regex_replace you can achieve your desired result:

---
- hosts: localhost
  gather_facts: false
  vars:
      string: "prepend "
      list: ["value1", "value2", "value3"]
  tasks:
    - name: "append string to each element in a list"
      set_fact:
          list: "{{ list | map('regex_replace', '(.*)', '{{ string }}\\1') | list }}"
    - debug:
          msg: "{{ list }}"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3