Search code examples
ansiblejinja2

Define list of dictionaries based on jinja conditionals in an ansible role


I'm creating an ansible role and I want to define my defaults in the following manner as an example:

include_mas_mount: false
include_log_mount: false

mounts:
  - name: 1st default mount
    mountdir: /data/mount1
    nasdir: /vol1/mount1
  - name: 2nd default mount
    mountdir: /data/mount2
    nasdir: /vol1/mount2
{% if include_mas_mount %}
  - name: mas mount
    mountdir: /data/mas_mount
    nasdir: /vol1/mas_mount
{% endif %}
{% if include_log_mount %}
  - name: log mount
    mountdir: /data/log_mount
    nasdir: /vol1/log_mount
{% endif %}

In testing this role I get a jinja templating error. Is this sort of thing possible? Or does it have be done differently. The idea with the role is that the include_mas_mount and include_log_mount variables would be overridden by a host or group override.

The role is expected to loop over the mounts: list of dictionaries and create the defined mounts on the target device.


Solution

  • You can quickfix the above by slightly changing your mounts definition:

    mounts: |-
      {% filter from_yaml %}
      - name: 1st default mount
        mountdir: /data/mount1
        nasdir: /vol1/mount1
      - name: 2nd default mount
        mountdir: /data/mount2
        nasdir: /vol1/mount2
      {% if include_mas_mount %}
      - name: mas mount
        mountdir: /data/mas_mount
        nasdir: /vol1/mas_mount
      {% endif %}
      {% if include_log_mount %}
      - name: log mount
        mountdir: /data/log_mount
        nasdir: /vol1/log_mount
      {% endif %}
      {% endfilter %}
    

    Meanwhile, this is not the best approach for at least 2 reasons:

    • You are using a 'complex' template for a variable definition when it is not needed nor justified and we tend to avoid this in ansible when possible.
    • You define mounts in defaults/main.yml although (from your description) you do not want users to override that variable directly.

    The following will give the exact same result fixing the above problems. I used the name my_role for the role

    First, we keep in roles/my_role/defaults/main.yml only the variables which will be user overridable:

    ---
    include_mas_mount: false
    include_log_mount: false
    

    Then we create the following roles/my_role/vars/main.yml which contains roles variables we do not want users to play with:

    ---
    mas_mount:
      name: mas mount
      mountdir: /data/mas_mount
      nasdir: /vol1/mas_mount
    
    log_mount:
      name: log mount
      mountdir: /data/log_mount
      nasdir: /vol1/log_mount
    
    default_mounts:
      - name: 1st default mount
        mountdir: /data/mount1
        nasdir: /vol1/mount1
      - name: 2nd default mount
        mountdir: /data/mount2
        nasdir: /vol1/mount2
    
    mounts: "{{ default_mounts
      + ([log_mount] if include_log_mount | bool else [])
      + ([mas_mount] if include_mas_mount | bool else []) }}"
    

    In the above, the use of bool is a best practice to make sure the value is interpreted as a boolean as users may override your variable with content that otherwise translates to a string (like e.g. when using a vars_prompt)

    For the tests, I created the following dummy roles/my_role/tasks/main.yml

    ---
    - name: Debug mounts var
      ansible.builtin.debug:
        var: mounts
    

    and ran the role from the following playbook.yml:

    ---
    - name: Test my conditional list in role
      hosts: localhost
      gather_facts: false
    
      roles:
        - name: my_role
        - name: my_role
          vars:
            include_mas_mount: true
        - name: my_role
          vars:
            include_log_mount: true
        - name: my_role
          vars:
            include_mas_mount: true
            include_log_mount: true
    

    Which gives:

    $ ansible-playbook playbook.yml 
    
    PLAY [Test my conditional list in role] ****************************************************************************************************************************************************************************************
    
    TASK [my_role : Debug mounts var] **********************************************************************************************************************************************************************************************
    ok: [localhost] => {
        "mounts": [
            {
                "mountdir": "/data/mount1",
                "name": "1st default mount",
                "nasdir": "/vol1/mount1"
            },
            {
                "mountdir": "/data/mount2",
                "name": "2nd default mount",
                "nasdir": "/vol1/mount2"
            }
        ]
    }
    
    TASK [my_role : Debug mounts var] **********************************************************************************************************************************************************************************************
    ok: [localhost] => {
        "mounts": [
            {
                "mountdir": "/data/mount1",
                "name": "1st default mount",
                "nasdir": "/vol1/mount1"
            },
            {
                "mountdir": "/data/mount2",
                "name": "2nd default mount",
                "nasdir": "/vol1/mount2"
            },
            {
                "mountdir": "/data/mas_mount",
                "name": "mas mount",
                "nasdir": "/vol1/mas_mount"
            }
        ]
    }
    
    TASK [my_role : Debug mounts var] **********************************************************************************************************************************************************************************************
    ok: [localhost] => {
        "mounts": [
            {
                "mountdir": "/data/mount1",
                "name": "1st default mount",
                "nasdir": "/vol1/mount1"
            },
            {
                "mountdir": "/data/mount2",
                "name": "2nd default mount",
                "nasdir": "/vol1/mount2"
            },
            {
                "mountdir": "/data/log_mount",
                "name": "log mount",
                "nasdir": "/vol1/log_mount"
            }
        ]
    }
    
    TASK [my_role : Debug mounts var] **********************************************************************************************************************************************************************************************
    ok: [localhost] => {
        "mounts": [
            {
                "mountdir": "/data/mount1",
                "name": "1st default mount",
                "nasdir": "/vol1/mount1"
            },
            {
                "mountdir": "/data/mount2",
                "name": "2nd default mount",
                "nasdir": "/vol1/mount2"
            },
            {
                "mountdir": "/data/log_mount",
                "name": "log mount",
                "nasdir": "/vol1/log_mount"
            },
            {
                "mountdir": "/data/mas_mount",
                "name": "mas mount",
                "nasdir": "/vol1/mas_mount"
            }
        ]
    }
    
    PLAY RECAP *********************************************************************************************************************************************************************************************************************
    localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0