How to make Ansible run one certain task only on one host?

Viewed 50302

The playbook looks like:

- hosts: all
  tasks:
    - name: "run on all hosts,1"
      shell: something1
    - name: "run on all hosts,2"
      shell: something2
    - name: "run on one host, any host would do"
      shell: this_command_should_run_on_one_host
    - name: "run on all hosts,3"
      shell: something3

I know with command line option --limit, I can limit to one host, is it possible to do it in playbook?

3 Answers

For any host (with defaults it will match the first on the list):

- name: "run on first found host"
  shell: this_command_should_run_on_one_host
  run_once: true

For a specific host:

- name: "run on that_one_host host"
  shell: this_command_should_run_on_one_host
  when: ansible_hostname == 'that_one_host'

Or inventory_hostname (hostname as defined in the Ansible inventory) instead of ansible_hostname (hostname as defined on the target machine), depending on which name you want to use.

Techraf's first answer is the exact one for the OP's question.

I just wanted to show a better way to run a task on a specific host:

- name: "run on that_one_host host"
  shell: this_command_should_run_on_one_host
  run_once: true
  delegate_to: that_one_host

If the group the playbook is run on contains many hosts, using when: ansible_hostname == 'that_one_host' or when: ansible_hostname == play_hosts[0] will evaluate the when-clause on all the hosts (which could be long if the when-clause had other more complicated conditions) and result in a long list of skipped hosts in the playbook's output.

Combining run_once and delegate_to, the playbook's output will be cleaner, only showing the task being executed on the chosen host.

- hosts: all
  gather_facts: no
  tasks:
    - name: Run on one specific host | when-clause
      debug:
        msg: "Hello world"
      when: inventory_hostname == play_hosts[0]
    
    - name: Run on one specific host | run_once + delegate_to
      debug:
        msg: "Hello world"
      run_once: true
      delegate_to: play_hosts[0]
TASK [Run on one specific host | when-clause] **********************************************************************************
skipping: [host2]
skipping: [host3]
ok: [host1] => {
    "msg": "Hello world"
}
skipping: [host4]

TASK [Run on one specific host | run_once + delegate_to] ***********************************************************************
ok: [host2 -> play_hosts[0]] => {
    "msg": "Hello world"
}

Whichever solution you choose, never combine these, if you want a task to be run once on a precise host:

  • run_once: true and
  • when: inventory_hostname == 'that_one_host'

If you do, it will be impossible to know in advance if the task will be executed or not (I have learnt it the hard way). The reason is that run_once: true will select a random host in the play's group and only later apply the when-clause:

  • if run_once selected to run the task on 'that_one_host', the task will be executed
  • if run_once selected 'another_host', the task will be skipped.

True way

- include_tasks: custom-tasks.yml
  when: inventory_hostname == item
  with_items: "{{ ansible_play_hosts }}"
Related