We have been writing Ansible tasks wrong

Look at the following Ansible task:

- name: Prometheus Exporters
  - name: Extend caps for ping - File
    template:
      src:   "roles/server/files{{ item }}"
      dest:  "{{ item }}"
      owner: prometheus
      group: root
      mode:  0600
    loop:
      - /etc/systemd/system/prometheus-blackbox-exporter.service.d/override.conf
    when: "'blackbox' in exporters"
    register: foo
    tags: config, monitoring
    notify:
    # - Reload systemd
    - Reload Blackbox Exporter

For a while I have been wishing to be able to write this like this:

class PrometheusExporters:
    def extend_caps_for_ping_File(self):
        if 'blackbox' in exporters:
            foo = []
            for exporter in exporters:
                # tags: config, monitoring
                foo.append(template(f"roles/server/files{exporter}", exporter, 'prometheus', 'root', 0o600, 
                           notify=(reload_blackbox_exporter,)))

Notice that these are not 100% equivalent; the Python version uses exporter as the loop_variable, but in the Ansible code I never set that because it's so cumbersome.

Why do I prefer that notation? Because:

  • Even if Ansible aims to be a declarative language, it has many programming languages features like when (if), loop (for) and block/recover (try/except).
  • Yes, the register equivalent I wrote there is not really nice, but alternatives are, I think, worse.
  • When using certain modules I almost always use the same params. Notice that a proper translation would be template(src=f"roles/server/files{exporter}", dest=exporter, owner='prometheus', group='root', mode=0o600), and that this is very close to the inline module invocation a.k.a. free form arguments1: template: src="roles/server/files{{ exporter }}" dest="{{ exporter }}" owner=prometheus group=root mode=0600.
  • If the compiler would be clever enough, I could declare functions that would work as templates for tasks.
  • As a programmer, this order makes more sense.

But I don't have to wait to have enough energy to write such compiler myself; I can more or less already change the order:

- name: Prometheus Exporters
  - name: Extend caps for ping - File
    when: "'blackbox' in exporters"
    loop:
      - /etc/systemd/system/prometheus-blackbox-exporter.service.d/override.conf
    register: foo
    template:
      src:   "roles/server/files{{ item }}"
      dest:  "{{ item }}"
      owner: prometheus
      group: root
      mode:  0600
    notify:
    # - Reload systemd
    - Reload Blackbox Exporter
    tags: config, monitoring

Not 100% where I want it (setting loop_variable is still ugly), but in my head it's easier to read. I don't use free form arguments because it's not clear that I can split the line like I did on the Python code.


  1. Thanks oblikoamorale#ansible@libera.chat.