# Run once with when statement causes only skipped action to run

**URL:** <https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230>\
**Category:** Ansible Project\
**Created:** [December 8, 2017, 6:52pm UTC](https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230 "2017-12-08T18:52:46Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![ddrake2012](https://avatars.discourse-cdn.com/v4/letter/d/9fc348/32.png) [@ddrake2012](https://forum.ansible.com/u/ddrake2012)\
**Post date:** [December 8, 2017, 6:52pm UTC](https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230/1 "2017-12-08T18:52:46Z")

</div>

Hey,

I’m seeing some weird behavior that does not seem intuitive when trying to use run\_once with when. Intuitively I think this should run an action one time when the conditional is met, however what I’m seeing is that a skipped action is ran (because the conditional is false) and nothing actually changes. Is there anyway to work around this? Should I file a bug?

Thanks,  
Dave

---

<div class="post-metadata">

**Author:** ![sivel](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/sivel/32/1198_2.png) [@sivel](https://forum.ansible.com/u/sivel)\
**Post date:** [December 8, 2017, 6:58pm UTC](https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230/2 "2017-12-08T18:58:42Z")

</div>

By default `run_once` only executes the task against the 1st host in the list of hosts in the play batch. As such, the `when` statement only uses the variables from that 1 host.

Effectively it is doing:

run\_once: true  
delegate\_to: “{{ ansible\_play\_batch[0] }}”

So you will either need to ensure that your `when` statement applies for that first host, or potentially use `delegate_to` to specify another host.

---

<div class="post-metadata">

**Author:** ![Josh\_Smift1](https://avatars.discourse-cdn.com/v4/letter/j/bcef8e/32.png) [@Josh\_Smift1](https://forum.ansible.com/u/Josh_Smift1)\
**Post date:** [December 8, 2017, 7:09pm UTC](https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230/3 "2017-12-08T19:09:50Z")

</div>

There's a bunch of tickets about run\_once and skipped hosts;  
[https://github.com/ansible/ansible/issues/19966](https://github.com/ansible/ansible/issues/19966) is my personal favorite.

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-Josh (jbs@care.com)

(apologies for the automatic corporate disclaimer that follows)

This email is intended for the person(s) to whom it is addressed and may contain information that is PRIVILEGED or CONFIDENTIAL. Any unauthorized use, distribution, copying, or disclosure by any person other than the addressee(s) is strictly prohibited. If you have received this email in error, please notify the sender immediately by return email and delete the message and any attachments from your system.

---

<div class="post-metadata">

**Author:** ![ddrake2012](https://avatars.discourse-cdn.com/v4/letter/d/9fc348/32.png) [@ddrake2012](https://forum.ansible.com/u/ddrake2012)\
**Post date:** [December 8, 2017, 7:21pm UTC](https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230/4 "2017-12-08T19:21:55Z")

</div>

Thanks for the replies. That’s an interesting idea about using delegate\_to, perhaps I can use a jinja2 filter to only delegate\_to a host matching the criteria in the when.

I was considering creating an in-memory group of only the hosts that matched the when but that sounds a bit verbose and a pain every time I want to run something once on a specific set of hosts.

Dave

---

<div class="post-metadata">

**Author:** ![ddrake2012](https://avatars.discourse-cdn.com/v4/letter/d/9fc348/32.png) [@ddrake2012](https://forum.ansible.com/u/ddrake2012)\
**Post date:** [December 8, 2017, 8:09pm UTC](https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230/5 "2017-12-08T20:09:13Z")

</div>

I can’t think of a clever way to use delegate\_to, I really need the run\_once functionality since these are all running in parallel.

Weirdly, when I try to create an in-memory host group using when and add\_host the task only gets executed one time (on a skipped host). I don’t have run\_once on this task so I’m not sure why all my nodes aren’t executing it. Any ideas?

Anyone have a good workaround for this issue?

Thanks

---

<div class="post-metadata">

**Author:** ![Josh\_Smift1](https://avatars.discourse-cdn.com/v4/letter/j/bcef8e/32.png) [@Josh\_Smift1](https://forum.ansible.com/u/Josh_Smift1)\
**Post date:** [December 8, 2017, 8:13pm UTC](https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230/6 "2017-12-08T20:13:50Z")

</div>

> Weirdly, when I try to create an in-memory host group using when and  
> add\_host the task only gets executed one time (on a skipped host). I  
> don't have run\_once on this task so I'm not sure why all my nodes aren't  
> executing it. Any ideas?

add\_host is implicitly run\_once; there's a note about it at the bottom of  
[http://docs.ansible.com/ansible/latest/add\_host\_module.html](http://docs.ansible.com/ansible/latest/add_host_module.html) saying "This  
module bypasses the play host loop and only runs once for all the hosts in  
the play, if you need it to iterate use a with\_ directive." Something like

&nbsp;&nbsp;- add\_host:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;groups: cool\_new\_group  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: "{{ item }}"  
&nbsp;&nbsp;&nbsp;&nbsp;changed\_when: False  
&nbsp;&nbsp;&nbsp;&nbsp;with\_items: "{{ play\_hosts }}"

might do what you need, depending what you need. :^)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-Josh (jbs@care.com)

(apologies for the automatic corporate disclaimer that follows)

This email is intended for the person(s) to whom it is addressed and may contain information that is PRIVILEGED or CONFIDENTIAL. Any unauthorized use, distribution, copying, or disclosure by any person other than the addressee(s) is strictly prohibited. If you have received this email in error, please notify the sender immediately by return email and delete the message and any attachments from your system.

---

<div class="post-metadata">

**Author:** ![ddrake2012](https://avatars.discourse-cdn.com/v4/letter/d/9fc348/32.png) [@ddrake2012](https://forum.ansible.com/u/ddrake2012)\
**Post date:** [December 8, 2017, 10:31pm UTC](https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230/7 "2017-12-08T22:31:20Z")

</div>

Thanks for the help, Josh.

I actually am trying to run a command once on a certain set of EC2 instances I’m managing that match a specific tag. This seems like a very reasonable thing to do and yet the run\_once skipping functionality makes this quite difficult. I managed to find two similar but different ways to do this (though not exactly elegant), which I’ll post for future reference. I was inspired by [https://groups.google.com/forum/#!topic/ansible-project/cpnrBRxLy0E](https://groups.google.com/forum/#!topic/ansible-project/cpnrBRxLy0E) and [https://imil.net/blog/2016/08/05/Ansible\_and\_AWS\_ASG](https://imil.net/blog/2016/08/05/Ansible_and_AWS_ASG)

**Method 1** (serial 1 is important due to the add\_host behavior Josh pointed out). Run the playbook with the AWS dynamic inventory script (-i ec2.py)

- hosts: ec2  
user: ubuntu  
serial: 1  
gather\_facts: false  
tasks:

- name: add only nodes that don’t match your\_tag\_name to in-memory host group  
add\_host:  
name: “{{ inventory\_hostname }}”  
groups: your\_hosts  
when: ec2\_tag\_Name != your\_tag\_name

and then run the rest of your playbook not serially and perform run\_once actions like this:

- hosts: ec2  
user: ubuntu  
gather\_facts: true  
tasks:

- name: Run once on your\_tag\_name  
shell: echo yes \>\> yes.txt  
run\_once: true  
delegate\_to: “{{ groups[‘your\_hosts’][0] }}”

**Method 2:** use the ec2\_instance\_facts module to create the in-memory group. This requires a tag name variable or constant you can reference to match instances with

- hosts: localhost  
user: ubuntu  
connection: local  
gather\_facts: true  
tasks:

- name: get ec2 remote facts

ec2\_instance\_facts:  
region: “{{ aws\_region }}”  
register: ec2

- name: create an in memory group of only nodes with your\_tag\_name  
add\_host:  
name: “{{ item }}”  
groups: your\_hosts  
with\_items: “{{ ec2.instances | selectattr(‘[state.name](http://state.name/)’, ‘equalto’, ‘running’) | selectattr(‘tags.Name’, ‘equalto’, your\_tag\_name ) | map(attribute=‘public\_ip\_address’)|list }}”

Then performing the run\_once is the same.

Either way, if you want to use the information from ec2.py then you’ll end up having two hosts sections and some verbosity. I suppose with the ec2\_instance\_facts one could potentially create groups of EC2 hosts and run the playbook all with hosts: localhost and delegate tasks to certain groups? I haven’t tried this as I’ve spend enough time getting run\_once to work for now.

Dave

---

<div class="post-metadata">

**Author:** ![Pshem\_Kowalczyk](https://avatars.discourse-cdn.com/v4/letter/p/e480ec/32.png) [@Pshem\_Kowalczyk](https://forum.ansible.com/u/Pshem_Kowalczyk)\
**Post date:** [December 9, 2017, 9:27am UTC](https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230/8 "2017-12-09T09:27:55Z")

</div>

If you’re using ec2.py to get your dynamic inventory you might use the dynamic groups created from tags (tag\_NAME\_VALUE). If you dynamically create the host before having to access it you can use:

```auto
- meta: refresh_inventory

```

to re-run the inventory script (which should discover newly created EC2 instances).

in that case you simply run the new play in the playbook against the hosts matching the tag:  
hosts: tag\_NAME\_VALUE

kind regards  
Pshem

---

<div class="post-metadata">

**Author:** ![ddrake2012](https://avatars.discourse-cdn.com/v4/letter/d/9fc348/32.png) [@ddrake2012](https://forum.ansible.com/u/ddrake2012)\
**Post date:** [December 11, 2017, 4:47pm UTC](https://forum.ansible.com/t/run-once-with-when-statement-causes-only-skipped-action-to-run/26230/9 "2017-12-11T16:47:57Z")

</div>

Thanks for the suggestion. This would be nice except I am using variable tag names defined in a config file, furthermore the tag names can’t have underscores (a requirement of the AWS ELB module) only dashes so I don’t even think I could map things even if it was possible to do something like the below although I’m not even sure you can use variables in hosts definitions like this?

- hosts: “‘tag\_Name\_’ + {{ my\_tag\_variable }}”

Even if this worked I’d be writing another whole hosts section every time I wanted to run once on a given tag. The ‘run once on a tag’ commands are interspersed throughout the playbook so this would get annoying. At least with the way above I have at most one extra hosts section.

Thanks,  
Dave
