ansiblejinja2

Ansible filters logic to process two dicts and add a new dict property based on key value


I have

- name: Replace group_ids with group_names in var_users
  hosts: localhost
  gather_facts: no
  vars:
    var_users:
      - group_ids: ["e40c1163-269f-41f7-5555-57e1788a1999", "2742113c-44a6-59e3-48de-60b72e40ed83"]
        name: "marko"
      - group_ids: ["e40c1163-269f-41f7-5555-57e1788a1999"]
        name: "user"
    var_groups:
      - id: "2742113c-44a6-59e3-48de-60b72e40ed83"
        name: "admins"
      - id: "e40c1163-269f-41f7-5555-57e1788a1999"
        name: "users"

What is the approach to corelate var_groups id with var_users group_ids to have resulting var_users have group nameintstead of group_ids?

The expected results should be

var_users:
  - group_ids: ["users", "admins"]
    name: "marko"
  - group_ids: ["users"]
    name: "user"

Solution

  • Convert var_groups to a dictionary

      var_groups_dict: "{{ var_groups |
                           items2dict(key_name='id', value_name='name') }}"
    

    gives

      var_groups_dict:
        2742113c-44a6-59e3-48de-60b72e40ed83: admin
        e40c1163-269f-41f7-5555-57e1788a1999: users
    

    Use it to substitute id by name

      group_ids: |
        {% filter from_yaml %}
        {% for ids in var_users | map(attribute='group_ids' )%}
        - {group_ids: {{ ids | map('extract', var_groups_dict) }}}
        {% endfor %}
        {% endfilter %}
    

    gives

      group_ids:
        - group_ids: [users, admin]
        - group_ids: [users]
    

    Then, zip the lists and combine the items

      result: "{{ var_users | zip(group_ids) | map('combine') }}"
    

    gives what you want

      result:
        - group_ids: [users, admin]
          name: marko
        - group_ids: [users]
          name: user
    

    Example of a complete playbook for testing

    - hosts: localhost
    
      vars:
    
        var_users:
          - group_ids: [e40c1163-269f-41f7-5555-57e1788a1999, 2742113c-44a6-59e3-48de-60b72e40ed83]
            name: marko
          - group_ids: [e40c1163-269f-41f7-5555-57e1788a1999]
            name: user
        var_groups:
          - id: 2742113c-44a6-59e3-48de-60b72e40ed83
            name: admin
          - id: e40c1163-269f-41f7-5555-57e1788a1999
            name: users
    
        var_groups_dict: "{{ var_groups |
                         items2dict(key_name='id', value_name='name') }}"
        group_ids: |
          {% filter from_yaml %}
          {% for ids in var_users | map(attribute='group_ids' )%}
          - {group_ids: {{ ids | map('extract', var_groups_dict) }}}
          {% endfor %}
          {% endfilter %}
        result: "{{ var_users | zip(group_ids) | map('combine') }}"
    
      tasks:
    
        - debug:
            var: var_groups_dict
    
        - debug:
            var: group_ids | to_yaml
    
        - debug:
            var: result | to_yaml
    

    Optionally, create the group_names dictionary

      group_names: |
        {% filter from_yaml %}
        {% for i in var_users %}
        {{ i.name }}: {{ i.group_ids | map('extract', var_groups_dict) }}
        {% endfor %}
        {% endfilter %}
    

    In a minimal form, this gives what you need

      group_names:
        marko: [users, admin]
        user: [users]
    

    Then, you can convert it to any list if you want to. For example, the below declaration gives the same result

      result: "{{ group_names |
                  dict2items(key_name='name', value_name='group_ids') }}"
    

    Or, you can use the dictionary in a loop. For example,

        - debug:
            msg: "group: {{ item.0.key }} name: {{ item.1 }}"
          loop: "{{ group_names | dict2items | subelements('value') }}"
    

    gives (abridged)

      msg: 'group: marko name: users'
      msg: 'group: marko name: admin'
      msg: 'group: user name: users'
    

    Optionally, if you don't want to use Jinja, the below filters give the same result

      result: "{{ var_users |
                  zip(var_users | map(attribute='group_ids')
                                | map('map', 'extract', var_groups_dict)
                                | map('community.general.dict_kv', 'group_ids')) |
                  map('combine') }}"
    

    , or group_names

      group_names: "{{ dict(var_users | map(attribute='name') |
                            zip(var_users | map(attribute='group_ids')
                                          | map('map', 'extract', var_groups_dict))) }}"