Deep Dive: Mastering the “Missing File Trap”
This is a follow up to my previous post Mastering Dynamic Task Includes in Ansible.
Because include_tasks evaluates variables at runtime (right when the play reaches that specific step), Ansible has no idea whether the target file actually exists when the playbook first starts.
If a user passes a typo like action=instal instead of install, Ansible will execute every task right up to your include step, and then crash with a fatal “file not found” error.
To prevent this, we use the with_first_found lookup plugin. It scans a list of files sequentially and includes the first one it actually finds on disk. By designing a deliberate fallback strategy, default_action.yml can become an active operational tool.
Scenario 1: The “Always Run” Setup
If there are foundational tasks that must happen regardless of the specific action, you can use the fallback file to execute them, ensuring your playbook remains stable.
- name: Dynamic action router
include_tasks: "{{ item }}"
with_first_found:
- files:
- "{{ action }}.yml"
- "default_action.yml"Writing the default_action.yml
Instead of leaving the default file empty, you can use it to log warnings, trigger alerts, or run safe baseline configurations. Here are two ways to write it depending on your operational goals:
Approach A: The Active Warning & Safe Fallback
Use this if a missing file means the user requested an unsupported action, and you want to warn them without crashing the entire infrastructure run.
# tasks/default_action.yml
---
- name: Log a warning about the unhandled action
ansible.builtin.debug:
msg: "WARNING: Action '{{ action }}' was requested, but no specific task file was found. Falling back to baseline configuration."
- name: Enforce baseline security compliance
ansible.builtin.include_tasks: baseline_hardening.yml
...
Approach B: The Graceful, Informative Failure
Sometimes, a missing file should stop the playbook, but Ansible’s default error message is ugly and unhelpful to end-users. You can use the default file to fail cleanly with an explicit explanation.
# tasks/default_action.yml
---
- name: Fail cleanly with an actionable message
ansible.builtin.fail:
msg: >
The requested action '{{ action }}' is not supported by this role.
Please ensure tasks/{{ action }}.yml exists, or choose a valid action
like 'install', 'configure', or 'backup'.
...