skip to Main Content

Let’s say I have a dictionary called packages in an Ansible playbook:

---
- name: Question
  hosts: localhost
  gather_facts: false
  vars:
    package_key: "ubuntu"
    packages:
        openssh-server:
            archlinux:
                - pkg1
                - pkg2
            ubuntu:
                - pkg3
        cowsay:
            archlinux:
                - pkg4
        app-armor:
            archlinux:
                - pkg5
            ubuntu:
                - pkg6
                - pkg7
  tasks:
    - name: Transformation
      debug:
        msg={{ packages | dict2items | <something> | items2dict }}

If I know the package_key to be ubuntu on such a system, I would like to transform the dictionary so that I get:

    transformed_packages:
        openssh-server:
            - pkg3
        app-armor:
            - pkg6
            - pkg7

What <something> do I need for this transformation? (Where the ubuntu packages are children of the applications, and when there is no ubuntu package, it’s not part of the transformed dict).

My reason for this transformation is that I find it more convenient to group per app when I edit what packages are needed for different distros. But at the same time, it’s more convenient to just keep what I need when I know the OS.

2

Answers


  1. Chosen as BEST ANSWER

    One way to transform the packages dictionary is by using a loop and combine:

    ---
    - name: Question
      hosts: localhost
      gather_facts: false
      vars:
          package_key: "ubuntu"
          packages:
              openssh-server:
                  archlinux:
                      - pkg1
                      - pkg2
                  ubuntu:
                      - pkg3
              cowsay:
                  archlinux:
                      - pkg4
              app-armor:
                  archlinux:
                      - pkg5
                  ubuntu:
                      - pkg6
                      - pkg7
      tasks:
          - set_fact:
                transformed_packages: "{{ transformed_packages |
                    default({}) |
                    combine({item.key:item.value[package_key]}) }}"
            loop: "{{ packages |
                dict2items |
                selectattr('value.' + package_key, 'defined') }}"
          - debug:
                msg: "{{ transformed_packages }}"
    

  2. Create a dictionary of all packages

      package_key: ubuntu
      pkg_attr: "value.{{ package_key }}"
      pkg_all: "{{ dict(packages.keys()|
                        zip(packages|
                            dict2items|
                            map(attribute=pkg_attr, default=[]))) }}"
    

    gives

      pkg_all:
        app-armor: [pkg6, pkg7]
        cowsay: []
        openssh-server: [pkg3]
    

    Optionally, you can use json_query

      pkg_query: '[].[key, {{ pkg_attr }}]'
      pkg_all: "{{ dict(packages|dict2items|json_query(pkg_query)) }}"
    


    Select non-empty lists

      transformed_packages: "{{ pkg_all|
                                dict2items|
                                selectattr('value')|
                                items2dict }}"
    

    gives what you want

      transformed_packages:
        app-armor: [pkg6, pkg7]
        openssh-server: [pkg3]
    

    Example of a complete playbook for testing

    - hosts: localhost
    
      vars:
    
        package_key: ubuntu
        packages:
          app-armor:
            archlinux: [pkg5]
            ubuntu: [pkg6, pkg7]
          cowsay:
            archlinux: [pkg4]
          openssh-server:
            archlinux: [pkg1, pkg2]
            ubuntu: [pkg3]
    
        pkg_attr: "value.{{ package_key }}"
        pkg_all: "{{ dict(packages.keys()|
                          zip(packages|
                              dict2items|
                              map(attribute=pkg_attr, default=[]))) }}"
        transformed_packages: "{{ pkg_all|
                                  dict2items|
                                  selectattr('value')|
                                  items2dict }}"
    
      tasks:
    
        - debug:
            var: pkg_all|to_yaml
        - debug:
            var: transformed_packages|to_yaml
    

    You can make the declaration more robust if you don’t trust the method keys() and the filter dict2items provides the same order of keys

      pkg_all: "{{ dict(packages|
                        dict2items|
                        map(attribute='key')|
                        zip(packages|
                            dict2items|
                            map(attribute=pkg_attr, default=[]))) }}"
    

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search