Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Safely limiting Ansible playbooks to a single machine?

People also ask

How we can limit the execution of Playbook in Ansible?

Using the --limit parameter of the ansible-playbook command is the easiest option to limit the execution of the code to only one host. The advantage is that you don't need to edit the Ansible Playbook code before executing to only one host.

Where should I store Ansible playbooks?

By default, Ansible assumes your playbooks are stored in one directory with roles stored in a sub-directory called roles/ . As you use Ansible to automate more tasks, you may want to move your playbooks into a sub-directory called playbooks/ .

What does the limit flag do with the Ansible playbook command?

You can change the behavior of the patterns defined in ad-hoc commands using command-line options. You can also limit the hosts you target on a particular run with the --limit flag.

Can I run multiple Ansible playbooks in parallel?

Ansible is not designed to run multiple playbooks at the same time in one process - for example, because the tasks differ from playbook to playbook and there is no step "taskA" in playbook1 and playbook2 at the same time. You need to run every playbook in one separate process (like with ansible-playbook ... & ).


Turns out it is possible to enter a host name directly into the playbook, so running the playbook with hosts: imac-2.local will work fine. But it's kind of clunky.

A better solution might be defining the playbook's hosts using a variable, then passing in a specific host address via --extra-vars:

# file: user.yml  (playbook)
---
- hosts: '{{ target }}'
  user: ...

Running the playbook:

ansible-playbook user.yml --extra-vars "target=imac-2.local"

If {{ target }} isn't defined, the playbook does nothing. A group from the hosts file can also be passed through if need be. Overall, this seems like a much safer way to construct a potentially destructive playbook.

Playbook targeting a single host:

$ ansible-playbook user.yml --extra-vars "target=imac-2.local" --list-hosts

playbook: user.yml

  play #1 (imac-2.local): host count=1
    imac-2.local

Playbook with a group of hosts:

$ ansible-playbook user.yml --extra-vars "target=office" --list-hosts

playbook: user.yml

  play #1 (office): host count=3
    imac-1.local
    imac-2.local
    imac-3.local

Forgetting to define hosts is safe!

$ ansible-playbook user.yml --list-hosts

playbook: user.yml

  play #1 ({{target}}): host count=0

There's also a cute little trick that lets you specify a single host on the command line (or multiple hosts, I guess), without an intermediary inventory:

ansible-playbook -i "imac1-local," user.yml

Note the comma (,) at the end; this signals that it's a list, not a file.

Now, this won't protect you if you accidentally pass a real inventory file in, so it may not be a good solution to this specific problem. But it's a handy trick to know!


This approach will exit if more than a single host is provided by checking the play_hosts variable. The fail module is used to exit if the single host condition is not met. The examples below use a hosts file with two hosts alice and bob.

user.yml (playbook)

---
- hosts: all
  tasks:
    - name: Check for single host
      fail: msg="Single host check failed."
      when: "{{ play_hosts|length }} != 1"
    - debug: msg='I got executed!'

Run playbook with no host filters

$ ansible-playbook user.yml
PLAY [all] ****************************************************************
TASK: [Check for single host] *********************************************
failed: [alice] => {"failed": true}
msg: Single host check failed.
failed: [bob] => {"failed": true}
msg: Single host check failed.
FATAL: all hosts have already failed -- aborting

Run playbook on single host

$ ansible-playbook user.yml --limit=alice

PLAY [all] ****************************************************************

TASK: [Check for single host] *********************************************
skipping: [alice]

TASK: [debug msg='I got executed!'] ***************************************
ok: [alice] => {
    "msg": "I got executed!"
}

There's IMHO a more convenient way. You can indeed interactively prompt the user for the machine(s) he wants to apply the playbook to thanks to vars_prompt:

---

- hosts: "{{ setupHosts }}"
  vars_prompt:
    - name: "setupHosts"
      prompt: "Which hosts would you like to setup?"
      private: no
  tasks:
    […]

To expand on joemailer's answer, if you want to have the pattern-matching ability to match any subset of remote machines (just as the ansible command does), but still want to make it very difficult to accidentally run the playbook on all machines, this is what I've come up with:

Same playbook as the in other answer:

# file: user.yml  (playbook)
---
- hosts: '{{ target }}'
  user: ...

Let's have the following hosts:

imac-10.local
imac-11.local
imac-22.local

Now, to run the command on all devices, you have to explicty set the target variable to "all"

ansible-playbook user.yml --extra-vars "target=all"

And to limit it down to a specific pattern, you can set target=pattern_here

or, alternatively, you can leave target=all and append the --limit argument, eg:

--limit imac-1*

ie. ansible-playbook user.yml --extra-vars "target=all" --limit imac-1* --list-hosts

which results in:

playbook: user.yml

  play #1 (office): host count=2
    imac-10.local
    imac-11.local