How to Iterate Over Ansible Mounts Array in Jinja2 Templates: A Complete Guide


3 views

When working with Ansible facts, the ansible_mounts variable contains valuable information about the host's filesystem. This data is structured as a JSON array of dictionaries, where each dictionary represents a mount point with various attributes like device, mount location, filesystem type, and usage statistics.

The issue with the original template code stems from incorrect Jinja2 syntax and variable reference. Here's the proper way to iterate through the mounts:

{% for mount in ansible_mounts %}
Mountpoint: {{ mount.mount }}
Device: {{ mount.device }}
Filesystem: {{ mount.fstype }}
Available Space: {{ mount.size_available }}
{% endfor %}

Let's create a more comprehensive template that generates a useful report:

<html>
<body>
<h1>Mount Points Report for {{ ansible_hostname }}</h1>
<table border="1">
    <tr>
        <th>Mount Point</th>
        <th>Device</th>
        <th>Filesystem</th>
        <th>Total Size</th>
        <th>Available</th>
        <th>Usage %</th>
    </tr>
    {% for mount in ansible_mounts %}
    <tr>
        <td>{{ mount.mount }}</td>
        <td>{{ mount.device }}</td>
        <td>{{ mount.fstype }}</td>
        <td>{{ (mount.size_total / 1073741824)|round(2) }} GB</td>
        <td>{{ (mount.size_available / 1073741824)|round(2) }} GB</td>
        <td>{{ (100 - (mount.size_available / mount.size_total * 100))|round(1) }}%</td>
    </tr>
    {% endfor %}
</table>
</body>
</html>

You can add conditional logic to filter specific mount points:

{% for mount in ansible_mounts if mount.mount == "/" or mount.mount == "/boot" %}
Important Mount: {{ mount.mount }} ({{ mount.device }})
Total Blocks: {{ mount.block_total }}
{% endfor %}

For more robust templates, consider adding checks for missing data:

{% if ansible_mounts is defined and ansible_mounts %}
{% for mount in ansible_mounts %}
Mountpoint: {{ mount.mount|default('N/A') }}
{% endfor %}
{% else %}
<p>No mount information available</p>
{% endif %}

When working with large systems, you might want to limit the output:

{% for mount in ansible_mounts[:5] %}
{{ mount.mount }} (showing first 5 mounts only)
{% endfor %}

When Ansible gathers facts about a host's filesystem, it stores mount information in the ansible_mounts variable as an array of dictionaries. Each dictionary contains detailed information about a mounted filesystem:

{
    "ansible_mounts": [
        {
            "device": "/dev/mapper/foobar",
            "mount": "/",
            "fstype": "xfs",
            "options": "rw,seclabel,relatime,attr2,inode64,noquota",
            "size_total": 33691066368,
            "size_available": 31949991936,
            "block_size": 4096,
            "block_total": 8225358,
            "block_available": 7800291,
            "block_used": 425067,
            "inode_total": 16458752,
            "inode_available": 16403366,
            "inode_used": 55386,
            "uuid": "2ebc82cb-5bc2-4db9-9914-33d65ba350b8"
        },
        {
            "device": "/dev/sda1",
            "mount": "/boot",
            "fstype": "xfs",
            "options": "rw,seclabel,relatime,attr2,inode64,noquota",
            "size_total": 520785920,
            "size_available": 182878208,
            "block_size": 4096,
            "block_total": 127145,
            "block_available": 44648,
            "block_used": 82497,
            "inode_total": 256000,
            "inode_available": 255595,
            "inode_used": 405,
            "uuid": "c5f7eaf2-5b70-4f74-8189-a63bb4bee5f8"
        }
    ]
}

The original attempt had several syntax issues. Here's the correct way to iterate through mount points:

{% for mount in ansible_mounts %}
Mountpoint: {{ mount.mount }}
{% endfor %}

Key points to notice:

  • Use {% %} instead of (% %) for control blocks
  • No need for {{ }} when referring to variables in control statements
  • Use the loop variable (mount) to access item properties

Creating a Markdown Table

| Device | Mount Point | FS Type | Size Available |
|--------|-------------|---------|----------------|
{% for m in ansible_mounts %}
| {{ m.device }} | {{ m.mount }} | {{ m.fstype }} | {{ (m.size_available / 1073741824)|round(2) }} GB |
{% endfor %}

Conditional Filtering

{% for m in ansible_mounts if m.size_available < 1073741824 %}
WARNING: Low disk space on {{ m.mount }} ({{ (m.size_available / 1048576)|int }} MB free)
{% endfor %}

Creating a CSV Output

device,mount_point,size_available,fstype
{% for m in ansible_mounts %}{{ m.device }},{{ m.mount }},{{ m.size_available }},{{ m.fstype }}
{% endfor %}

If you're having issues with your template:

  1. Check the exact variable name with ansible -m setup hostname
  2. Use debug module to verify the variable exists:
- name: Debug mounts
  ansible.builtin.debug:
    var: ansible_mounts

Remember that Jinja2 is whitespace-sensitive, so proper indentation matters for both readability and functionality.