Ansible with yaml synthaxe

Hi,
i am trying to get something as:

network:
  version: 2
  ethernets:
    eth0:
      dhcp4: no
      dhcp6: no
      addresses: [x.x.x.1/24]
      routes:
        - to: 10.1.1.0/24
          via: x.x.x.254
        - to: 10.1.2.0/24
          via: x.x.x.254

Here is my code:

    - name: network | ubuntu | configure | update {{ etc_netplan_adm }} file
      ansible.builtin.copy:
        dest: "{{ etc_netplan_adm }}"
        content: |
          {{ ansible_managed_message }}
          network:
            version: 2
            ethernets:
              {{ iface_adm }}:
                dhcp4: no
                dhcp6: no
                addresses: [{{ addr_adm[0].address }}/{{ addr_adm[0].cidr }}]
                routes:
                  {% for route in adm_default_routes | flatten | map('string') | list %}
                  - to: {{ route }}
                    via: {{ addr_adm[0].gateway }}
                {% endfor %}

But with this code, i get something that is not matching to yaml format:

network:
  version: 2
  ethernets:
    ens32:
      dhcp4: no
      dhcp6: no
      addresses: [x.x.x.232/24]
      routes:
              - to: 10.1.1.0/24
          via: x.x.x.254
              - to: 10.1.2.0/24
          via: x.x.x.254

So it fails when running ‘netplan apply’.

Any help would be appreciated.

Sorry, not fully understanding … is it just the indentation of the - to: lines that is not matching to yaml format?

    - name: network | ubuntu | configure | update {{ etc_netplan_adm }} file
      ansible.builtin.copy:
        dest: "{{ etc_netplan_adm }}"
        content: |
          {{ ansible_managed_message }}
          network:
            version: 2
            ethernets:
              {{ iface_adm }}:
                dhcp4: no
                dhcp6: no
                addresses: [{{ addr_adm[0].address }}/{{ addr_adm[0].cidr }}]
                routes:
          {% for route in adm_default_routes | flatten | map('string') | list %}
                  - to: {{ route }}
                    via: {{ addr_adm[0].gateway }}
          {% endfor %}

I was able to get this to work doing by adjusting the spacing. It looks awful, but it seems to produce the correct results.

---
- name: Write File
  hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - name: "network | ubuntu | configure | update {{ etc_netplan_adm }} file"
      ansible.builtin.copy:
        dest: "{{ etc_netplan_adm }}"
        content: |
          {{ ansible_managed_message }}
          network:
            version: 2
            ethernets:
              {{ iface_adm }}:
                dhcp4: no
                dhcp6: no
                addresses: [{{ addr_adm[0].address }}/{{ addr_adm[0].cidr }}]
                routes:
                {% for route in adm_default_routes | flatten | map('string') | list %}
            - to: {{ route }}
                    via: {{ addr_adm[0].gateway }}
                {% endfor %}
      vars:
        iface_adm: eth0
        addr_adm:
          - address: 10.1.1.1
            cidr: 24
            gateway: x.x.x.254
        adm_default_routes:
          - 10.1.1.0/24
          - 10.1.2.0/24
          - 10.1.3.0/24
        etc_netplan_adm: ./netplan.yml
        ansible_managed_message: "## Block Managed via Ansbile"

I was also able to get this to work using ansible.builtin.template

templates/netplan.yml.j2

{{ ansible_managed_message }}
network:
  version: 2
  ethernets:
    {{ iface_adm }}:
      dhcp4: no
      dhcp6: no
      addresses: [{{ addr_adm[0].address }}/{{ addr_adm[0].cidr }}]
      routes:
      {% for route in adm_default_routes | flatten | map('string') | list %}
  - to: {{ route }}
          via: {{ addr_adm[0].gateway }}
      {% endfor %}

and a playbook template_test.yml

---
- name: Write File - Template
  hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - name: "network | ubuntu | configure | update {{ etc_netplan_adm }} file"
      ansible.builtin.template:
        src: "{{ template_file }}"
        dest: "{{ etc_netplan_adm }}"
      vars:
        iface_adm: eth0
        addr_adm:
          - address: 10.1.1.1
            cidr: 24
            gateway: x.x.x.254
        adm_default_routes:
          - 10.1.1.0/24
          - 10.1.2.0/24
          - 10.1.3.0/24
        etc_netplan_adm: ./netplan.yml
        ansible_managed_message: "## Block Managed via Ansbile"
        template_file: netplan.yml.j2

Neither of them have spacing that looks reasonable, but the both seem to produce a file that looks like it matches your desired state.

Hope that helps.

Turn off Jinja2 trim_blocks by putting a plus symbol at the end of the block:

{% endfor +%}

Everything in the block will start at the same indentation depth as the block itself:

routes:
  {% for route in adm_default_routes | flatten | map('string') | list %}
  - to: {{ route }}
    via: {{ addr_adm[0].gateway }}
  {% endfor +%}

EDIT: I Put too many spaces in the for loop. corrected

Using the code I provided above (adjusting to match your suggestions) this didn’t work as expected. I ended up with results mangled in the same way as the initial posting.

Is there a setting in ansible.cfg (or elsewhere) that might need enabled for this too? If it work working as described, I much prefer your solution to the much harder to read “working” examples I provided.

Try put the Plus at the end of the block opening statement instead:
{% for route in adm_default_routes | flatten | map(‘string’) | list +%}

routes:
  {% for route in adm_default_routes | flatten | map('string') | list +%}
  - to: {{ route }}
    via: {{ addr_adm[0].gateway }}
  {% endfor %}

That’s much closer. The only thing that I’d consider still sub-optimal is additional newlines. The relevant block looks like this in both the inline and template file versions

      routes:
      
        - to: 10.1.1.0/24
          via: x.x.x.254
      
        - to: 10.1.2.0/24
          via: x.x.x.254
      
        - to: 10.1.3.0/24
          via: x.x.x.254

Kill the new lines with a minus sign in the block opening statement:
{%- for route in adm_default_routes | flatten | map(‘string’) | list +%}

May also try on the block closing statement:
{%- endfor %}

For both the inline version and the template version, I found that adding the - in both the beginning and block statements gave the desired results.

@chumi, this is what I ended up for the jinja block (either inline with content like you have, or in a separate template file) with after the help form @Narizz28

{{ ansible_managed_message }}
network:
  version: 2
  ethernets:
    {{ iface_adm }}:
      dhcp4: no
      dhcp6: no
      addresses: [{{ addr_adm[0].address }}/{{ addr_adm[0].cidr }}]
      routes:
      {%- for route in adm_default_routes | flatten | map('string') | list +%}
        - to: {{ route }}
          via: {{ addr_adm[0].gateway }}
      {%- endfor %}

For my preference, I like to put the loop block at the same indentation level as the outputs instead of the parent. It is easier to read to my eyes:

{{ ansible_managed_message }}
network:
  version: 2
  ethernets:
    {{ iface_adm }}:
      dhcp4: no
      dhcp6: no
      addresses: [{{ addr_adm[0].address }}/{{ addr_adm[0].cidr }}]
      routes:
        {%- for route in adm_default_routes | flatten | map('string') | list +%}
        - to: {{ route }}
          via: {{ addr_adm[0].gateway }}
        {%- endfor %}

+1.
I will remember that.

+1.
I like it because it is easier to read.

And many thanks for all your return.