Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ansible conditionally delegate_to local or remote?

Tags:

ansible

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.

like image 254
NarūnasK Avatar asked Feb 06 '23 22:02

NarūnasK


1 Answers

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.

like image 96
udondan Avatar answered May 20 '23 19:05

udondan