# A smart way to determine whether playbooks should only run for localhost?

**URL:** <https://forum.ansible.com/t/a-smart-way-to-determine-whether-playbooks-should-only-run-for-localhost/4946>\
**Category:** Get Help\
**Tags:** playbook, ansible-core\
**Created:** [April 15, 2024, 6:08am UTC](https://forum.ansible.com/t/a-smart-way-to-determine-whether-playbooks-should-only-run-for-localhost/4946 "2024-04-15T06:08:43Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![ShanDesai](https://avatars.discourse-cdn.com/v4/letter/s/838e76/32.png) [@ShanDesai](https://forum.ansible.com/u/ShanDesai)\
**Post date:** [April 15, 2024, 6:08am UTC](https://forum.ansible.com/t/a-smart-way-to-determine-whether-playbooks-should-only-run-for-localhost/4946/1 "2024-04-15T06:08:43Z")

</div>

I am working on a set of Ansible Playbooks which need to be a bit more versatile. The playbooks need to execute on `localhost` where it is not setup as a Controller node. If it is a Controller Node it performs the duties of a controller node in a network (playbook executed on each device it needs to manage).

I do not wish to programatically change the playbooks from `hosts: all` to `hosts: localhost` so I currently have `localhost` in my inventory file:

```yaml
all:
   children:
      ungrouped:
        localhost:
          ansible_connection: local
          ansible_host: 127.0.0.1

```

The repercussion is that based on whether the playbook is executed on `localhost` or as a control node some templates need to be adapted or some tasks need to be skipped. e.g. if controller node is setup → setup a file-server etc. but if standalone then perform some other tasks.

What would be an optimal way to achieve this task? Should I have to setup conditionals to check for `ungrouped` group and check if localhost exists or is there some smart way to achieve some distinction for standalone vs controller logic?

---

<div class="post-metadata">

**Author:** ![Denney-tech](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/denney-tech/32/1556_2.png) [@Denney-tech](https://forum.ansible.com/u/Denney-tech)\
**Post date:** [April 15, 2024, 2:18pm UTC](https://forum.ansible.com/t/a-smart-way-to-determine-whether-playbooks-should-only-run-for-localhost/4946/2 "2024-04-15T14:18:49Z")

</div>

As a general rule of thumb, I _ **never** _ define localhost in inventory. I always use the implicit localhost or else I will shoot myself in the foot. Secondly, the “control node” or “controller” is always the `localhost` you run Ansible from. There is no “localhost that is not setup as a Controller node”.

If you have playbooks where you need to do something on the controller before it can do something for the remote inventory, like install pip packages, do something like this:

```yaml
---
- hosts: all
  gather_facts: true
  pre_tasks:
    - name: Install Controller Pre-requisites
      ansible.builtin.pip:
        name: pypsrp[kerberos]
        state: present
      delegate_to: localhost
      run_once: true
  tasks:
     - name: Verify windows target connection
       ansible.windows.win_ping:
       when: ansible_connection == 'psrp'

```

Or, you can define multiple plays in the same playbook:

```yaml
---
- name: Configure Controller
  hosts: localhost
  tasks:
    - name: Install Controller Pre-requisites
      ansible.builtin.pip:
        name: pypsrp[kerberos]
        state: present

- name: Configure Inventory Hosts
  hosts: all
  tasks:
     - name: Verify windows target connection
       ansible.windows.win_ping:
       when: ansible_connection == 'psrp'

```

I would not recommend trying to define localhost, include it in the `all` group, and then try to use conditionals to decide whether something should or shouldn’t run on the localhost. That’s what `delegate_to` (and depending on the task, `run_once`) are for.

---

<div class="post-metadata">

**Author:** ![ShanDesai](https://avatars.discourse-cdn.com/v4/letter/s/838e76/32.png) [@ShanDesai](https://forum.ansible.com/u/ShanDesai)\
**Post date:** [April 16, 2024, 12:15pm UTC](https://forum.ansible.com/t/a-smart-way-to-determine-whether-playbooks-should-only-run-for-localhost/4946/3 "2024-04-16T12:15:12Z")

</div>

> Secondly, the “control node” or “controller” is always the `localhost` you run Ansible from. There is no “localhost that is not setup as a Controller node”.

I believe this will not work in general but only when `delegate_to` is explicitly mentioned. As an example:

```yaml
---
- name: Test pinging on localhost when nothing in inventory
  hosts: all
  tasks:
    - ping:

```

will not ping the localhost because `all` does not have any explicit mention of `localhost`

```auto
PLAY [all] *********************************************************************************************************************************************************************************
skipping: no hosts matched

PLAY RECAP *******************************************************************************************************************************************************************************

```

## scenario

I have an offline system. I start with a single device and want to execute a set of playbooks on it to configure the device with some offline artifacts. In this case, having a playbook with `hosts: localhost` would make sense. But once I introduce more devices into this offline network and set the device, previously mentioned to a controller, and wish to do the same playbook execution on the other devices I would need to set the playbook `hosts: all` given that I have an inventory of the new devices added.

> [@Denney-tech](#):
>
> Or, you can define multiple plays in the same playbook

This implies that the plays will be executed sequentially, once on the localhost and then on the inventory devices. There is no way of execution logic based on whether the playbook should perform task set A if it is only meant for localhost and task set B if it is meant for remote devices.

As an example, I have some docker image tarballs that I wish to deploy. On a single device I would just use the equivalent of `docker load` from the Docker collection. Now if this device is responsible for distributing the image tarballs to multiple devices a sane way would be for the controller to push these images to a docker registry and then changing things on the remote devices (docker pull etc.)

---

<div class="post-metadata">

**Author:** ![Denney-tech](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/denney-tech/32/1556_2.png) [@Denney-tech](https://forum.ansible.com/u/Denney-tech)\
**Post date:** [April 16, 2024, 1:30pm UTC](https://forum.ansible.com/t/a-smart-way-to-determine-whether-playbooks-should-only-run-for-localhost/4946/4 "2024-04-16T13:30:08Z")

</div>

Keeping `localhost` and `all` separate is something I would really consider best-practice.

That said, if you _really_ want to include `localhost` as an inventory target together with `all` in the same play, then I would recommend simply mentioning them both in `hosts:`

```yaml
- name: Configure docker
  hosts: all,localhost
  gather_facts: true
  tasks:
...

```

Then if need be, you can use `--limit` to restrict your inventory targets when necessary.

As an aside, if you’re wanting to configure the localhost identically to all of your remotes, you might benefit from using `ansible-pull` instead. If each of your “offline” devices have access to the same local-network resource for offline artifacts, then you can store your ansible playbook(s) there, and use `ansible-pull` on each device to run the exact same play in the exact same context (against only `hosts: localhost`)

---

<div class="post-metadata">

**Author:** ![bcoca](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bcoca/32/9269_2.png) [@bcoca](https://forum.ansible.com/u/bcoca)\
**Post date:** [April 16, 2024, 1:36pm UTC](https://forum.ansible.com/t/a-smart-way-to-determine-whether-playbooks-should-only-run-for-localhost/4946/5 "2024-04-16T13:36:31Z")

</div>

As others have mentioned, I would advise against adding localhost to the inventory, but if you really have to, please follow the docs:

[https://docs.ansible.com/ansible/latest/inventory/implicit\_localhost.html](https://docs.ansible.com/ansible/latest/inventory/implicit_localhost.html)

---

<div class="post-metadata">

**Author:** ![ShanDesai](https://avatars.discourse-cdn.com/v4/letter/s/838e76/32.png) [@ShanDesai](https://forum.ansible.com/u/ShanDesai)\
**Post date:** [April 16, 2024, 1:39pm UTC](https://forum.ansible.com/t/a-smart-way-to-determine-whether-playbooks-should-only-run-for-localhost/4946/6 "2024-04-16T13:39:29Z")

</div>

You are correct. I have a project based on `ansible-runner` which currently doesn’t have `--pull` functionality but I am assuming performing `ansible-pull` will work in this case too. I think I would consider your tips. appreciate it.

---

<div class="post-metadata">

**Author:** ![Denney-tech](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/denney-tech/32/1556_2.png) [@Denney-tech](https://forum.ansible.com/u/Denney-tech)\
**Post date:** [April 16, 2024, 2:00pm UTC](https://forum.ansible.com/t/a-smart-way-to-determine-whether-playbooks-should-only-run-for-localhost/4946/7 "2024-04-16T14:00:22Z")

</div>

> [@ShanDesai](#):
>
> I have a project based on `ansible-runner`

Oh? Now _that’s_ interesting. I can’t say I have any real experience with using `ansible-runner` myself, so I don’t know if that works well together with `ansible-pull`.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/ansible/original/1X/1882d9a01da5d8996a862359b0913cc568a39b84.svg) [@system](https://forum.ansible.com/u/system)\
**Post date:** [May 16, 2024, 2:00pm UTC](https://forum.ansible.com/t/a-smart-way-to-determine-whether-playbooks-should-only-run-for-localhost/4946/8 "2024-05-16T14:00:56Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
