{"id":1212,"date":"2026-05-20T09:09:07","date_gmt":"2026-05-20T09:09:07","guid":{"rendered":"https:\/\/techno.slomka.biz\/?p=1212"},"modified":"2026-05-20T09:09:37","modified_gmt":"2026-05-20T09:09:37","slug":"deep-dive-mastering-the-missing-file-trap","status":"publish","type":"post","link":"https:\/\/techno.slomka.biz\/?p=1212","title":{"rendered":"Deep Dive: Mastering the &#8220;Missing File Trap&#8221;"},"content":{"rendered":"\n<p>This is a follow up to my previous post <a href=\"https:\/\/techno.slomka.biz\/?p=1208\">Mastering Dynamic Task Includes in Ansible<\/a>.<\/p>\n\n\n\n<p>Because <code>include_tasks<\/code> evaluates variables at <strong>runtime<\/strong> (right when the play reaches that specific step), Ansible has no idea whether the target file actually exists when the playbook first starts.<\/p>\n\n\n\n<p>If a user passes a typo like <code>action=instal<\/code> instead of <code>install<\/code>, Ansible will execute every task right up to your include step, and then crash with a fatal &#8220;file not found&#8221; error.<\/p>\n\n\n\n<p>To prevent this, we use the <code>with_first_found<\/code> lookup plugin. It scans a list of files sequentially and includes the <strong>first one<\/strong> it actually finds on disk. By designing a deliberate fallback strategy, <code>default_action.yml<\/code> can become an active operational tool.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Scenario 1: The &#8220;Always Run&#8221; Setup<\/h2>\n\n\n\n<p>If there are foundational tasks that <em>must<\/em> happen regardless of the specific action, you can use the fallback file to execute them, ensuring your playbook remains stable.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#EEFFFF;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#304047;color:#d5ffff\">YAML<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#EEFFFF;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>- name: Dynamic action router\n  include_tasks: \"{{ item }}\"\n  with_first_found:\n    - files:\n        - \"{{ action }}.yml\"\n        - \"default_action.yml\"<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki material-theme\" style=\"background-color: #263238\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #89DDFF\">-<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #F07178\">name<\/span><span style=\"color: #89DDFF\">:<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #C3E88D\">Dynamic action router<\/span><\/span>\n<span class=\"line\"><span style=\"color: #EEFFFF\">  <\/span><span style=\"color: #F07178\">include_tasks<\/span><span style=\"color: #89DDFF\">:<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #89DDFF\">&quot;<\/span><span style=\"color: #C3E88D\">{{ item }}<\/span><span style=\"color: #89DDFF\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #EEFFFF\">  <\/span><span style=\"color: #F07178\">with_first_found<\/span><span style=\"color: #89DDFF\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #EEFFFF\">    <\/span><span style=\"color: #89DDFF\">-<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #F07178\">files<\/span><span style=\"color: #89DDFF\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #EEFFFF\">        <\/span><span style=\"color: #89DDFF\">-<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #89DDFF\">&quot;<\/span><span style=\"color: #C3E88D\">{{ action }}.yml<\/span><span style=\"color: #89DDFF\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #EEFFFF\">        <\/span><span style=\"color: #89DDFF\">-<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #89DDFF\">&quot;<\/span><span style=\"color: #C3E88D\">default_action.yml<\/span><span style=\"color: #89DDFF\">&quot;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Writing the <code>default_action.yml<\/code><\/h2>\n\n\n\n<p>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:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Approach A: The Active Warning &amp; Safe Fallback<\/h3>\n\n\n\n<p>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.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#EEFFFF;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#304047;color:#d5ffff\">YAML<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#EEFFFF;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly># tasks\/default_action.yml\n---\n- name: Log a warning about the unhandled action\n  ansible.builtin.debug:\n    msg: \"WARNING: Action '{{ action }}' was requested, but no specific task file was found. Falling back to baseline configuration.\"\n\n- name: Enforce baseline security compliance\n  ansible.builtin.include_tasks: baseline_hardening.yml\n\n...\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki material-theme\" style=\"background-color: #263238\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #546E7A; font-style: italic\"># tasks\/default_action.yml<\/span><\/span>\n<span class=\"line\"><span style=\"color: #FFCB6B\">---<\/span><\/span>\n<span class=\"line\"><span style=\"color: #89DDFF\">-<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #F07178\">name<\/span><span style=\"color: #89DDFF\">:<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #C3E88D\">Log a warning about the unhandled action<\/span><\/span>\n<span class=\"line\"><span style=\"color: #EEFFFF\">  <\/span><span style=\"color: #F07178\">ansible.builtin.debug<\/span><span style=\"color: #89DDFF\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #EEFFFF\">    <\/span><span style=\"color: #F07178\">msg<\/span><span style=\"color: #89DDFF\">:<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #89DDFF\">&quot;<\/span><span style=\"color: #C3E88D\">WARNING: Action &#39;{{ action }}&#39; was requested, but no specific task file was found. Falling back to baseline configuration.<\/span><span style=\"color: #89DDFF\">&quot;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #89DDFF\">-<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #F07178\">name<\/span><span style=\"color: #89DDFF\">:<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #C3E88D\">Enforce baseline security compliance<\/span><\/span>\n<span class=\"line\"><span style=\"color: #EEFFFF\">  <\/span><span style=\"color: #F07178\">ansible.builtin.include_tasks<\/span><span style=\"color: #89DDFF\">:<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #C3E88D\">baseline_hardening.yml<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #FFCB6B\">...<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Approach B: The Graceful, Informative Failure<\/h3>\n\n\n\n<p>Sometimes, a missing file <em>should<\/em> stop the playbook, but Ansible&#8217;s default error message is ugly and unhelpful to end-users. You can use the default file to fail cleanly with an explicit explanation.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#EEFFFF;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#304047;color:#d5ffff\">YAML<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#EEFFFF;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly># tasks\/default_action.yml\n---\n\n- name: Fail cleanly with an actionable message\n  ansible.builtin.fail:\n    msg: >\n      The requested action '{{ action }}' is not supported by this role. \n      Please ensure tasks\/{{ action }}.yml exists, or choose a valid action \n      like 'install', 'configure', or 'backup'.\n\n...\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki material-theme\" style=\"background-color: #263238\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #546E7A; font-style: italic\"># tasks\/default_action.yml<\/span><\/span>\n<span class=\"line\"><span style=\"color: #FFCB6B\">---<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #89DDFF\">-<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #F07178\">name<\/span><span style=\"color: #89DDFF\">:<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #C3E88D\">Fail cleanly with an actionable message<\/span><\/span>\n<span class=\"line\"><span style=\"color: #EEFFFF\">  <\/span><span style=\"color: #F07178\">ansible.builtin.fail<\/span><span style=\"color: #89DDFF\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #EEFFFF\">    <\/span><span style=\"color: #F07178\">msg<\/span><span style=\"color: #89DDFF\">:<\/span><span style=\"color: #EEFFFF\"> <\/span><span style=\"color: #89DDFF; font-style: italic\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C3E88D\">      The requested action &#39;{{ action }}&#39; is not supported by this role. <\/span><\/span>\n<span class=\"line\"><span style=\"color: #C3E88D\">      Please ensure tasks\/{{ action }}.yml exists, or choose a valid action <\/span><\/span>\n<span class=\"line\"><span style=\"color: #C3E88D\">      like &#39;install&#39;, &#39;configure&#39;, or &#39;backup&#39;.<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #FFCB6B\">...<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"<p>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 &#8220;file not found&#8221; 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 &#8220;Always Run&#8221; 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. 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 &amp; 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. Approach B: The Graceful, Informative Failure Sometimes, a missing file should stop the playbook, but Ansible&#8217;s default error message is ugly and unhelpful to end-users. You can use the default file to fail cleanly with an explicit explanation.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[30,8],"tags":[40],"class_list":["post-1212","post","type-post","status-publish","format-standard","hentry","category-ansible","category-konzepte","tag-ansible"],"_links":{"self":[{"href":"https:\/\/techno.slomka.biz\/index.php?rest_route=\/wp\/v2\/posts\/1212","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/techno.slomka.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/techno.slomka.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/techno.slomka.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/techno.slomka.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1212"}],"version-history":[{"count":1,"href":"https:\/\/techno.slomka.biz\/index.php?rest_route=\/wp\/v2\/posts\/1212\/revisions"}],"predecessor-version":[{"id":1213,"href":"https:\/\/techno.slomka.biz\/index.php?rest_route=\/wp\/v2\/posts\/1212\/revisions\/1213"}],"wp:attachment":[{"href":"https:\/\/techno.slomka.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1212"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/techno.slomka.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1212"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/techno.slomka.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1212"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}