Module_defaults no longer accept values as templates

I am looking into our upgrade path to ansible-core 2.19 (which is /o\ so far), and stumbled across changed behavior which I haven’t found was documented in the upgrade guide (Ansible-core 2.19 Porting Guide — Ansible Community Documentation)

So we are having configurable gather_subset since ansible 2.4 or smth. Recently, in 2.18, we moved from ANSIBLE_GATHER_SUBSET to module_defaults as was suggested by the prior guide. In our playbooks we have smth like that today:

- name: Gather host facts
  hosts: "hosts"
  gather_facts: true
  vars:
    default_gather_subset:
      - '!all'
      - min
  module_defaults:
    ansible.builtin.setup:
      gather_subset: "{{ default_gather_subset }}"
  tags:
    - always

However, starting with ansible-core 2.19 this starts failing:

[ERROR]: Task failed: Action failed: The following modules failed to execute: ansible.legacy.setup.

Task failed: Action failed.

<<< caused by >>>

The following modules failed to execute: ansible.legacy.setup.

+--[ Sub-Event 1 of 1 ]---
| 
| Bad subset '{{ default_gather_subset }}' given to Ansible. gather_subset options allowed: all, all_ipv4_addresses, all_ipv6_addresses, apparmor, architecture, caps, chroot, cmdline, date_time, default_ipv4, default_ipv6, devices, distribution, distribution_major_version, distribution_release, distribution_version, dns, effective_group_ids, effective_user_id, env, facter, fibre_channel_wwn, fips, hardware, interfaces, is_chroot, iscsi, kernel, kernel_version, loadavg, local, lsb, machine, machine_id, mounts, network, nvme, ohai, os_family, pkg_mgr, platform, processor, processor_cores, processor_count, python, python_version, real_user_id, selinux, service_mgr, ssh_host_key_dsa_public, ssh_host_key_ecdsa_public, ssh_host_key_ed25519_public, ssh_host_key_rsa_public, ssh_host_pub_keys, ssh_pub_keys, system, system_capabilities, system_capabilities_enforced, systemd, user, user_dir, user_gecos, user_gid, user_id, user_shell, user_uid, virtual, virtualization_role, virtualization_tech_guest, virtualization_tech_host, virtualization_type
| 
+--[ End Sub-Event ]---

fatal: [aio1]: FAILED! => 
    ansible_facts: {}
    changed: false
    failed_modules:
        ansible.legacy.setup:
            ansible_facts:
                discovered_interpreter_python: /usr/bin/python3.12
            exception: (traceback unavailable)
            failed: true
            invocation:
                module_args:
                    fact_path: /etc/ansible/facts.d
                    filter: []
                    gather_subset:
                    - '{{ default_gather_subset }}'
                    gather_timeout: 10
            msg: 'Bad subset ''{{ default_gather_subset }}'' given to Ansible. gather_subset
                options allowed: all, all_ipv4_addresses, all_ipv6_addresses, apparmor,
                architecture, caps, chroot, cmdline, date_time, default_ipv4, default_ipv6,
                devices, distribution, distribution_major_version, distribution_release,
                distribution_version, dns, effective_group_ids, effective_user_id, env,
                facter, fibre_channel_wwn, fips, hardware, interfaces, is_chroot, iscsi,
                kernel, kernel_version, loadavg, local, lsb, machine, machine_id, mounts,
                network, nvme, ohai, os_family, pkg_mgr, platform, processor, processor_cores,
                processor_count, python, python_version, real_user_id, selinux, service_mgr,
                ssh_host_key_dsa_public, ssh_host_key_ecdsa_public, ssh_host_key_ed25519_public,
                ssh_host_key_rsa_public, ssh_host_pub_keys, ssh_pub_keys, system, system_capabilities,
                system_capabilities_enforced, systemd, user, user_dir, user_gecos, user_gid,
                user_id, user_shell, user_uid, virtual, virtualization_role, virtualization_tech_guest,
                virtualization_tech_host, virtualization_type'
    msg: 'The following modules failed to execute: ansible.legacy.setup.'

Apparently, the root cause is that templates are no longer accepted as values. However, according to ansible/changelogs/fragments/74039_enable_module_defaults_for_collections.yml at 3b861abce13c541c1fc41e59d631e76d2564582a · ansible/ansible · GitHub this supposed to work,

So am I missing smth?
Is that expected behavior or a bug? As tests for the feature ansible/test/integration/targets/gathering_facts/test_module_defaults.yml at 5e10a9160c96b238d788b1b196a4c3e80ba6a8bd · ansible/ansible · GitHub does not seem to ever cover this.

I would assume this is a bug.

Replacing

  gather_facts: true

by

  gather_facts: false
  pre_tasks:
    - ansible.builtin.setup:

fixes the issue for me. I would expect that gather_facts: true basically adds ansible.builtin.setup as a pre-task (the code in lib/ansible/executor/play_iterator.py looks like it’s basically doing that).

1 Like

So, implicit setup runs “conditionally” - it won’t be launched if cached facts are fresh, so it will quickly pass.

Which I believe is not true for explicit setup in pre_tasks, unless there is some possibility to write a condition for that (not aware how though).

I can report a bug if current behavior is not intentional, was not sure if I am missing smth or it’s intended (with amount of things in 2.19 it’s always a possibility)