<h5 dir="auto">ISSUE TYPE</h5>
<p dir="auto">Feature Idea</p>
<h5 dir="auto">COMPONENT NAME</h5>
<p dir="auto">lib/ansible/playbook</p>
<h5 dir="auto">ANSIBLE VERSION</h5>
<p dir="auto">N/A</p>
<h5 dir="auto">SUMMARY</h5>
<p dir="auto">This feature request is quite simple : with so many issues linked to the crucial "any_errors_fatal" property on plays (even in Ansible 2.4), please add an option so that in extreme conditions a programmed exit can be used. This should set a "failed" status on all hosts of the playbook of course.</p>
<p dir="auto">In my case we use three playbooks:</p>
<ul dir="auto">
<li>stop</li>
<li>deploy</li>
<li>start</li>
</ul>
<p dir="auto">Each playbook runs against several hosts and is included as a play in a "stop-deploy-restart" playbook.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="# stop-deploy-restart.yml (2.3-style)
- include: &quot;stop.yml&quot;
- include: &quot;deploy.yml&quot;
- include: &quot;start.yml&quot;"><pre class="notranslate"><code class="notranslate"># stop-deploy-restart.yml (2.3-style)
- include: "stop.yml"
- include: "deploy.yml"
- include: "start.yml"
</code></pre></div>
<p dir="auto">But we want to deploy <em>only</em> if every host has stopped successfully. If any host fails the "stop" play, we'd like to fail the wrapping "stop-deploy-restart" playbook.</p>
<p dir="auto">In theory we could use "any_errors_fatal", but as I said its behaviour is clearly not bug-free.</p>
<p dir="auto">And for the moment we can touch a file in the "post_tasks" part of "stop" in order to keep track of which host has successfully stopped, but there seems to be no mechanism in Ansible that allows us to prevent the plays to continue. I'd like somthing like this:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="# stop-deploy-restart.yml (2.3-style)
- include: &quot;stop.yml&quot;
- meta: exit
  when: something_went_wrong # makes every hosts fail AND skips everything else in the playbook
- include: &quot;deploy.yml&quot;
- include: &quot;start.yml&quot;"><pre class="notranslate"><code class="notranslate"># stop-deploy-restart.yml (2.3-style)
- include: "stop.yml"
- meta: exit
  when: something_went_wrong # makes every hosts fail AND skips everything else in the playbook
- include: "deploy.yml"
- include: "start.yml"
</code></pre></div>
<p dir="auto">And of course when "any_errors_fatal" is repaired that won't be the nice way to write things. But it could still be usefull.</p>
<p dir="auto">Thanks in advance.</p>
<h5 dir="auto">ISSUE TYPE</h5>
<ul dir="auto">
<li>Bug Report</li>
</ul>
<h5 dir="auto">COMPONENT NAME</h5>
<p dir="auto">task</p>
<h5 dir="auto">ANSIBLE VERSION</h5>
<p dir="auto">2.2.1.0</p>
<h5 dir="auto">CONFIGURATION</h5>
<div class="highlight highlight-source-ini notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="[defaults]
host_key_checking       = False
host_key_checking       = False
record_host_keys        = False
remote_user		        = &quot;ubuntu&quot;
timeout			        = 600
nocows			        = 1
forks                   = 25
[privilege_escalation]
become                  = True
become_method           = sudo
become_user             = root
become_ask_pass         = False
[ssh_connection]
pipelining              = True"><pre class="notranslate"><span class="pl-en">[defaults]</span>
<span class="pl-k">host_key_checking</span>       = False
<span class="pl-k">host_key_checking</span>       = False
<span class="pl-k">record_host_keys</span>        = False
<span class="pl-k">remote_user</span>		        = <span class="pl-s"><span class="pl-pds">"</span>ubuntu<span class="pl-pds">"</span></span>
<span class="pl-k">timeout</span>			        = 600
<span class="pl-k">nocows</span>			        = 1
<span class="pl-k">forks</span>                   = 25
<span class="pl-en">[privilege_escalation]</span>
<span class="pl-k">become</span>                  = True
<span class="pl-k">become_method</span>           = sudo
<span class="pl-k">become_user</span>             = root
<span class="pl-k">become_ask_pass</span>         = False
<span class="pl-en">[ssh_connection]</span>
<span class="pl-k">pipelining</span>              = True</pre></div>
<h5 dir="auto">OS / ENVIRONMENT</h5>
<p dir="auto">ubuntu 16.04</p>
<h5 dir="auto">SUMMARY</h5>
<p dir="auto">missing host in a task and duplicated on the next in the same playbook/role</p>
<h5 dir="auto">STEPS TO REPRODUCE</h5>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="- name: Check if one app have failed           &lt;- missing host
  set_fact: one_fail=true
  delegate_to: &quot;{{ groups.BASTION[0] }}&quot;
  delegate_facts: true
  when: item.rc != 0
  with_items: &quot;{{ custom_script.results | default([]) }}&quot;

- name: Delete all apps if one script fails     &lt;- dupliated host
  file: path={{ destpath }}/{{ item.item.stdout.split('@')[0] }}/releases/{{ item.item.stdout }} state=absent
  when: hostvars[groups.BASTION[0]].one_fail | default([]) and
        exit_fast
  with_items: &quot;{{ custom_script.results | default([]) }}&quot;"><pre class="notranslate">- <span class="pl-ent">name</span>: <span class="pl-s">Check if one app have failed           &lt;- missing host</span>
  <span class="pl-ent">set_fact</span>: <span class="pl-s">one_fail=true</span>
  <span class="pl-ent">delegate_to</span>: <span class="pl-s"><span class="pl-pds">"</span>{{ groups.BASTION[0] }}<span class="pl-pds">"</span></span>
  <span class="pl-ent">delegate_facts</span>: <span class="pl-c1">true</span>
  <span class="pl-ent">when</span>: <span class="pl-s">item.rc != 0</span>
  <span class="pl-ent">with_items</span>: <span class="pl-s"><span class="pl-pds">"</span>{{ custom_script.results | default([]) }}<span class="pl-pds">"</span></span>

- <span class="pl-ent">name</span>: <span class="pl-s">Delete all apps if one script fails     &lt;- dupliated host</span>
  <span class="pl-ent">file</span>: <span class="pl-s">path={{ destpath }}/{{ item.item.stdout.split('@')[0] }}/releases/{{ item.item.stdout }} state=absent</span>
  <span class="pl-ent">when</span>: <span class="pl-s">hostvars[groups.BASTION[0]].one_fail | default([]) and</span>
        <span class="pl-s">exit_fast</span>
  <span class="pl-ent">with_items</span>: <span class="pl-s"><span class="pl-pds">"</span>{{ custom_script.results | default([]) }}<span class="pl-pds">"</span></span></pre></div>
<h5 dir="auto">EXPECTED RESULTS</h5>
<p dir="auto">no missing or duplicated hosts</p>
<h5 dir="auto">ACTUAL RESULTS</h5>
<div class="highlight highlight-text-shell-session notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="TASK [deployer : Check if one app have failed] *********************************
ok: [172.16.16.128 -&gt; 172.16.17.1] =&gt; (item={u'_ansible_parsed': True, u'cmd': u'sudo -E ...
skipping: [172.16.16.135] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...
skipping: [172.16.16.138] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...
skipping: [172.16.16.139] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...
skipping: [172.16.16.102] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...
skipping: [172.16.16.140] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...
skipping: [172.16.16.149] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...
skipping: [172.16.16.150] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...
skipping: [172.16.16.146] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...

TASK [deployer : Delete all apps if one script fails] **************************
skipping: [172.16.16.148] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'std  &lt;- DUPLICATED
skipping: [172.16.16.145] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'std  &lt;- DUPLICATED
skipping: [172.16.16.128] =&gt; (item={u'_ansible_parsed': True, u'cmd': u'sudo -E -         ...
skipping: [172.16.16.139] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...
skipping: [172.16.16.138] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...
skipping: [172.16.16.135] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...
skipping: [172.16.16.140] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...
skipping: [172.16.16.102] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...
skipping: [172.16.16.145] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'std &lt;- DUPLICATED
skipping: [172.16.16.146] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...
skipping: [172.16.16.149] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...
skipping: [172.16.16.150] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...
skipping: [172.16.16.148] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'std  &lt;- DUPLICATED"><pre class="notranslate"><span class="pl-c1">TASK [deployer : Check if one app have failed] *********************************</span>
<span class="pl-c1">ok: [172.16.16.128 -&gt; 172.16.17.1] =&gt; (item={u'_ansible_parsed': True, u'cmd': u'sudo -E ...</span>
<span class="pl-c1">skipping: [172.16.16.135] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...</span>
<span class="pl-c1">skipping: [172.16.16.138] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...</span>
<span class="pl-c1">skipping: [172.16.16.139] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...</span>
<span class="pl-c1">skipping: [172.16.16.102] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...</span>
<span class="pl-c1">skipping: [172.16.16.140] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...</span>
<span class="pl-c1">skipping: [172.16.16.149] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...</span>
<span class="pl-c1">skipping: [172.16.16.150] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...</span>
<span class="pl-c1">skipping: [172.16.16.146] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout': ...</span>

<span class="pl-c1">TASK [deployer : Delete all apps if one script fails] **************************</span>
<span class="pl-c1">skipping: [172.16.16.148] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'std  &lt;- DUPLICATED</span>
<span class="pl-c1">skipping: [172.16.16.145] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'std  &lt;- DUPLICATED</span>
<span class="pl-c1">skipping: [172.16.16.128] =&gt; (item={u'_ansible_parsed': True, u'cmd': u'sudo -E -         ...</span>
<span class="pl-c1">skipping: [172.16.16.139] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...</span>
<span class="pl-c1">skipping: [172.16.16.138] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...</span>
<span class="pl-c1">skipping: [172.16.16.135] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...</span>
<span class="pl-c1">skipping: [172.16.16.140] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...</span>
<span class="pl-c1">skipping: [172.16.16.102] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...</span>
<span class="pl-c1">skipping: [172.16.16.145] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'std &lt;- DUPLICATED</span>
<span class="pl-c1">skipping: [172.16.16.146] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...</span>
<span class="pl-c1">skipping: [172.16.16.149] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...</span>
<span class="pl-c1">skipping: [172.16.16.150] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'stdout' ...</span>
<span class="pl-c1">skipping: [172.16.16.148] =&gt; (item={u'_ansible_parsed': True, u'changed': True, u'std  &lt;- DUPLICATED</span></pre></div>