Search code examples
ansibleansible-facts

how to update a value of a dict withing a loop into a list of dict


As I am stuck, I need some help. I want to update a value of a dict in a list of dict (ideally, I don't want to create another list)

Under an Ansible playbook, I have a variable declared:

a:
  - id: 'test1id'
    name: 'test1name'
    description: ''
  - id: 'test2id'
    name: 'test2name'
    description: 'init'
  - id: 'test3id'
    name: 'test3name'
    description: 'init3'

I have a task that loop into "a" should update the description.

- name: Update description in a
  when: a is defined
  set_fact:
    a: "{{ item | combine({'description': new_description}) }}"
  vars:
    new_description: "{{ item.description + "test" + item.id }}"
  loop: a

Expected result:

a:
  - id: 'test1id'
    name: 'test1name'
    description: 'test test1id'
  - id: 'test2id'
    name: 'test2name'
    description: 'init test test2id'
  - id: 'test3id'
    name: 'test3name'
    description: 'init3 test test3id'

Actual result:

a:
  - id: 'test3id'
    name: 'test3name'
    description: 'init3 test test3id'

Looks like I'm missing something, I just have the last item. I also tried other format, but that never give me the expected result.


Solution

  • What you've done is right.
    There were some errors, but I think it was caused by a typo, like loop: "{{ a }}" and spaces around the word test here new_description: '{{ item.description + " test " + item.id }}'.

    If you run it in debug mode:

    ok: [localhost] => (item={'id': 'test1id', 'name': 'test1name', 'description': ''}) => {
        "ansible_facts": {
            "a": {
                "description": " test test1id",
                "id": "test1id",
                "name": "test1name"
            }
        },
        "ansible_loop_var": "item",
        "changed": false,
        "item": {
            "description": "",
            "id": "test1id",
            "name": "test1name"
        }
    }
    ok: [localhost] => (item={'id': 'test2id', 'name': 'test2name', 'description': 'init'}) => {
        "ansible_facts": {
            "a": {
                "description": "init test test2id",
                "id": "test2id",
                "name": "test2name"
            }
        },
        "ansible_loop_var": "item",
        "changed": false,
        "item": {
            "description": "init",
            "id": "test2id",
            "name": "test2name"
        }
    }
    ok: [localhost] => (item={'id': 'test3id', 'name': 'test3name', 'description': 'init3'}) => {
        "ansible_facts": {
            "a": {
                "description": "init3 test test3id",
                "id": "test3id",
                "name": "test3name"
            }
        },
        "ansible_loop_var": "item",
        "changed": false,
        "item": {
            "description": "init3",
            "id": "test3id",
            "name": "test3name"
        }
    }
    

    You just need to read the values back from ansible_facts you set.
    The result should be registered temporarily and then convert the results to the original format with the updated values.

    - hosts: localhost
      gather_facts: no
      vars:
        a:
        - id: 'test1id'
          name: 'test1name'
          description: ''
        - id: 'test2id'
          name: 'test2name'
          description: 'init'
        - id: 'test3id'
          name: 'test3name'
          description: 'init3'
      tasks:
      - name: Update description in a
        when: a is defined
        set_fact:
          a: "{{ item | combine({'description': new_description}) }}"
        vars:
          new_description: '{{ item.description + " test " + item.id }}'
        loop: "{{ a }}"
        register: temporary_a
    
      - name: Results
        set_fact:
          a: "{{ temporary_a.results | map(attribute='ansible_facts.a') | list }}" 
    
      - debug:
          var: a
    

    Results:

    ok: [localhost] => {
        "a": [
            {
                "description": " test test1id",
                "id": "test1id",
                "name": "test1name"
            },
            {
                "description": "init test test2id",
                "id": "test2id",
                "name": "test2name"
            },
            {
                "description": "init3 test test3id",
                "id": "test3id",
                "name": "test3name"
            }
        ]
    }