I have a few ansible
playbooks which sometimes make sense in the local context, otherwise they are executed remotely. In order to do that I use delegate_to
directive, but that also means that I have to double all my tasks, eg.:
---
- hosts: all
gather_facts: no
tasks:
- name: Local command
command: hostname
register: target_host
when: vhost is undefined
delegate_to: 127.0.0.1
# ---
- name: Remote command
command: hostname
register: target_host
when: vhost is defined
Exec locally:
$ ansible-playbook -i inv.d/test.ini play.d/delegate.yml
PLAY [all] ********************************************************************
TASK: [Local command] *********************************************************
changed: [new-server -> 127.0.0.1]
TASK: [Remote command] ********************************************************
skipping: [new-server]
PLAY RECAP ********************************************************************
new-server : ok=1 changed=1 unreachable=0 failed=0
Exec on remote:
$ ansible-playbook -i inv.d/test.ini play.d/delegate.yml -e vhost=y
PLAY [all] ********************************************************************
TASK: [Local command] *********************************************************
skipping: [new-server]
TASK: [Remote command] ********************************************************
changed: [new-server]
PLAY RECAP ********************************************************************
new-server : ok=1 changed=1 unreachable=0 failed=0
Is there a smarter way to tell ansible
when to fallback to the local environment? Currently I'm using ansible==1.9.2
.
Where tasks should be executed should not be defined in the tasks. Delegation makes sense when tasks always have to be run locally or on a related machine (e.g. database host or router) while the playbook itself and therefore most of the tasks run the hosts defined on playbook level.
But if your goal is to run the whole playbook either locally or on a set of remote hosts, you should work with different inventory files or groups.
If you have two different inventory files, in one you define localhost, in the other all the remote hosts, then apply the inventory you want when calling ansible, -i inv.d/local
or -i inv.d/remote
.
Or have it all in one inventory and dynamically pass the group. In your inventory you define two groups:
[local]
127.0.0.1
[remote]
host-1
host-2
host-N
And then pass the group as an extra-var to ansible: -e "run=local"
or -e "run=remote"
In your playbook you set the hosts
dynamically:
---
- hosts: "{{ run | mandatory }}"
gather_facts: no
tasks:
...
In your example it appears you only work with a single remote host defined per vhost
extra-var. In that case the best option appears to be to re-use this variable in the hosts section and default to localhost.
---
- hosts: "{{ vhost | default('127.0.0.1') }}"
gather_facts: no
tasks:
...
So if vhost
is defined the whole playbook will be executed on that host. If it is not defined the playbook runs locally.
Finally, you still could use the delegate_to
option on single tasks like so:
- name: Local AND remote command
command: hostname
delegate_to: "{{ '127.0.0.1' if vhost is undefined else omit }}"
omit
is a special variable to make Ansible ignore the option, as if it would not have been defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With