leedm777
leedm777

Reputation: 24032

How do I map list of strings into a list of objects in Ansible/Jinja2?

I'm trying to build security groups rule and routes from a list of CIDRs, and can't quite get the mapping syntax right.

I have an list of cidrs:

cidrs:
  - 10.12.0.0/16
  - 10.99.0.0/16
  - 192.168.128.0/24

And I'd like to transform it into a list of rules for a security group, such as:

rules:
  - proto: all
    cidr_ip: 10.12.0.0/16
  - proto: all
    cidr_ip: 10.99.0.0/16
  - proto: all
    cidr_ip: 192.168.128.0/24

What's the syntax for performing this sort of transformation using Jinja2/Ansible filters?

Upvotes: 4

Views: 4352

Answers (1)

Vladimir Botka
Vladimir Botka

Reputation: 68024

1. Filter combine in loop

For example, the playbook


shell> cat playbook.yml
- hosts: localhost
  vars:
    cidrs:
      - 10.12.0.0/16
      - 10.99.0.0/16
      - 192.168.128.0/24
    protos:
      proto: all
  tasks:
    - set_fact:
        rules: "{{ rules|default([]) +
                   [{'cidr_ip': item}|combine(protos)] }}"
      loop: "{{ cidrs }}"
    - debug:
        var: rules

gives (abridged)

shell> ansible-playbook playbook.yml

  rules:
  - cidr_ip: 10.12.0.0/16
    proto: all
  - cidr_ip: 10.99.0.0/16
    proto: all
  - cidr_ip: 192.168.128.0/24
    proto: all

2. Modified list and filter combine in map

If the input is a list of dictionaries, aka hashes, then simple map would do the job. For example, the playbook below gives the same result

shell> cat playbook.yml
- hosts: localhost
  gather_facts: false
  vars:
    cidrs:
      - cidr_ip: 10.12.0.0/16
      - cidr_ip: 10.99.0.0/16
      - cidr_ip: 192.168.128.0/24
    protos:
      proto: all
  tasks:
    - set_fact:
        rules: "{{ cidrs|map('combine', protos)|list }}"
    - debug:
        var: rules

3. Custom plugin and filter combine in map

The filter product seems a good candidate to start with the transformation of a simple list into a list of hashes. But, there is no filter in Ansible to transform a list of two items into a dictionary, AFAIK. Let's write such a filter. For example

shell> cat filter_plugins/dict.py
def item2dict(t):
    h = {t[0]:t[1]}
    return h

class FilterModule(object):
    ''' Ansible dict filters '''

    def filters(self):
        return {
            'item2dict': item2dict
        }

Then the playbook below gives the same result

shell> cat playbook.yml
- hosts: localhost
  vars:
    cidrs:
      - 10.12.0.0/16
      - 10.99.0.0/16
      - 192.168.128.0/24
    protos:
      proto: all
  tasks:
    - set_fact:
        rules: "{{ ['cidr_ip']|product(cidrs)|map('item2dict')|
                   map('combine', protos)|list }}"
    - debug:
        var: rules

Upvotes: 2

Related Questions