Search code examples
ansiblejmespath

Ansible jinja test list if contains valid IP address and generate template


I have the following variables in Ansible:

my_allowed_hosts2:
  - {host: patch, address: '202.167.24.50'}
  - {host:localhost, address: '::1/128', extra_field: trust}

my_ferm_groups:
  - firewall_whitelist1
  - firewall_whitelist2

I have a task that checks if there are valid IP addresses in my_allowed_hosts2 and if so, generate a template for it (in this case, the host I'm running the playbook against is in firewall_whitelist2 group in my inventory). This template is then used to generate ferm/iptable rules to allow the IP addresses in my_allowed_hosts2 to access the host.

However, if there is no valid address in my_allowed_hosts2, I don't want to generate a template for it (firewall_whitelist_2) even though myhost is a valid member of that group - because if it has no valid IP addresses that the template will become invalid. Hope I'm making sense so far.

The relevant tasks :

- set_fact:
    valid_addresses: "{{ my_allowed_hosts2|json_query('[*].address')| map('ipaddr')|reject('match','^127.0.0.1|::1/128') | list }}"

- debug:
    msg: "{{ valid_addresses }}"


- name: Loop dictionary and copy group configs in place
  template: src=etc/ferm/ferm.d/{{ item }}.j2 dest=/etc/ferm/ferm.d/{{ item }}.conf owner=root group=root mode=0640
  when: item in my_ferm_groups
  with_items: "{{ group_names | reject ('match','^firewall_whitelist2$') if valid_addresses is not any }}"

What I was trying to do above is trying to remove firewall_whitelist2 from the loop if there is no true value tested in my_allowed_hosts2 list - thus preventing it to create that particular template.

However, I receive the following error :

fatal: [myhost]: FAILED! => {
   "msg": "the inline if-expression on line 1 evaluated to false and no else section was defined."
}

What I am expecting (IF valid_addresses is not empty) is something like the below. The template gets copied accordingly:

ok: [myhost] => (item=firewall_whitelist1) => {"ansible_loop_var": "item", "changed": false, "checksum": "21f42490d91da92c6f404a30df6e34373266b72f", "dest": "/etc/ferm/ferm.d/firewall_whitelist1.j2", "gid": 0, "group": "root", "item": "firewall_whitelist1", "mode": "0640", "owner": "root", "path": "/etc/ferm/ferm.d/firewall_whitelist1.conf", "secontext": "system_u:object_r:etc_t:s0", "size": 570, "state": "file", "uid": 0}

changed: [myhost] => (item=firewall_whitelist2) => {"ansible_loop_var": "item", "changed": false, "checksum": "985d207faa196b285c20c7f60f6aa69b23f908b9", "dest": "/etc/ferm/ferm.d/firewall_whitelist2.j2, "gid": 0, "group": "root", "item": "firewall_whitelist2", "mode": "0640", "owner": "root", "path": "/etc/ferm/ferm.d/firewall_whitelist2.conf", "secontext": "system_u:object_r:etc_t:s0", "size": 705, "state": "file", "uid": 0}

I tried this too in an effort to add firewall_whitelist2 to the list so the template only gets generated when valid_addresses is not empty:

- name: Loop dictionary and copy group configs in place
  template: src=etc/ferm/ferm.d/{{ item }}.j2 dest=/etc/ferm/ferm.d/{{ item }}.conf owner=root group=root mode=0640
  when: item in my_ferm_groups
  loop: "{{ group_names + 'firewall_whitelist2' if valid_addresses|length > 0 else group_names }}"

But this errors out with

FAILED! => {"msg": "Unexpected templating type error occurred on ({{ group_names + 'firewall_whitelist2' if valid_addresses|length > 0 else group_names }}): can only concatenate list (not \"str\") to list"}`

Any idea how can I make this work?
Basically I'm trying to find a way to only generate that template for firewall_whitelist_2 ONLY IF the condition matches (valid IP addresses detected in my_allowed_hosts2).
Any nudge in the right direction would help.


Solution

  • Makes me feel like you are trying to fit in the loop what should be in the when.

    So in the ultimate end you will probably loop on an empty array, when you could as well loop on a populated list and skip all items.

    When moving all those conditions in the when, we could end up with:

    - template: 
        src: /etc/ferm/ferm.d/{{ item }}.j2 
        dest: /etc/ferm/ferm.d/{{ item }}.conf 
        owner: root 
        group: root 
        mode: 0640
      when:
        - item in my_ferm_groups
        - item != 'firewall_whitelist2' or valid_addresses|length > 0
      loop: "{{ group_names }}"
    

    Here is are two examples of this:

    • When there is no matching IP
      - hosts: all
        gather_facts: no
      
        tasks:
          - debug:
              msg: "{{ item }}"
            when:
              - item in my_ferm_groups
              - item != 'firewall_whitelist2' or valid_addresses|length > 0
            loop: "{{ group_names }}"
            vars:
              valid_addresses: []
              group_names:
                - firewall_whitelist1
                - firewall_whitelist2
                - firewall_whitelist3 
                - firewall_whitelist4
              my_ferm_groups:
                - firewall_whitelist1
                - firewall_whitelist2
                - firewall_whitelist3 
      
      This gives the recap:
      PLAY [all] *******************************************************************************************************
      
      TASK [debug] *****************************************************************************************************
      ok: [localhost] => (item=firewall_whitelist1) => {
          "msg": "firewall_whitelist1"
      }
      skipping: [localhost] => (item=firewall_whitelist2) 
      ok: [localhost] => (item=firewall_whitelist3) => {
          "msg": "firewall_whitelist3"
      }
      skipping: [localhost] => (item=firewall_whitelist4) 
      
      PLAY RECAP *******************************************************************************************************
      localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
      
    • When there is any matching IP
      - hosts: all
        gather_facts: no
      
        tasks:
          - debug:
              msg: "{{ item }}"
            when:
              - item in my_ferm_groups 
              - item != 'firewall_whitelist2' or valid_addresses|length > 0
            loop: "{{ group_names }}"
            vars:
              valid_addresses:
                - 10.1.1.1
              group_names:
                - firewall_whitelist1
                - firewall_whitelist2
                - firewall_whitelist3 
                - firewall_whitelist4
              my_ferm_groups:
                - firewall_whitelist1
                - firewall_whitelist2
                - firewall_whitelist3 
      
      This gives the recap:
      PLAY [all] *******************************************************************************************************
      
      TASK [debug] *****************************************************************************************************
      ok: [localhost] => (item=firewall_whitelist1) => {
          "msg": "firewall_whitelist1"
      }
      ok: [localhost] => (item=firewall_whitelist2) => {
          "msg": "firewall_whitelist2"
      } 
      ok: [localhost] => (item=firewall_whitelist3) => {
          "msg": "firewall_whitelist3"
      }
      skipping: [localhost] => (item=firewall_whitelist4) 
      
      PLAY RECAP *******************************************************************************************************
      localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
      

    With all this, the valid_addresses you are populating stays as you have it right now.