Override hosts variable of Ansible playbook from the command line

AnsibleAnsible Playbook

Ansible Problem Overview


This is a fragment of a playbook that I'm using (server.yml):

- name: Determine Remote User
  hosts: web
  gather_facts: false
  roles:
    - { role: remote-user, tags: [remote-user, always] }

My hosts file has different groups of servers, e.g.

[web]
x.x.x.x

[droplets]
x.x.x.x

Now I want to execute ansible-playbook -i hosts/<env> server.yml and override hosts: web from server.yml to run this playbook for [droplets].

Can I just override as a one time off thing, without editing server.yml directly?

Thanks.

Ansible Solutions


Solution 1 - Ansible

I don't think Ansible provides this feature, which it should. Here's something that you can do:

hosts: "{{ variable_host | default('web') }}"

and you can pass variable_host from either command-line or from a vars file, e.g.:

ansible-playbook server.yml --extra-vars "variable_host=newtarget(s)"

Solution 2 - Ansible

For anyone who might come looking for the solution.
Play Book

- hosts: '{{ host }}'
  tasks:
  - debug: msg="Host is {{ ansible_fqdn }}"

Inventory

[web]
x.x.x.x

[droplets]
x.x.x.x

Command: ansible-playbook deplyment.yml -i hosts --extra-vars "host=droplets" So you can specify the group name in the extra-vars

Solution 3 - Ansible

We use a simple fail task to force the user to specify the Ansible limit option, so that we don't execute on all hosts by default/accident.

The easiest way I found is this:

---
- name: Force limit
  # 'all' is okay here, because the fail task will force the user to specify a limit on the command line, using -l or --limit
  hosts: 'all'

  tasks:
  - name: checking limit arg
	fail:
	  msg: "you must use -l or --limit - when you really want to use all hosts, use -l 'all'"
	when: ansible_limit is not defined
	run_once: true

Now we must use the -l (= --limit option) when we run the playbook, e.g.

ansible-playbook playbook.yml -l www.example.com

Limit option docs:

> Limit to one or more hosts This is required when one wants to run a > playbook against a host group, but only against one or more members of > that group. > > Limit to one host > > ansible-playbook playbooks/PLAYBOOK_NAME.yml --limit "host1" > > Limit to multiple hosts > > ansible-playbook playbooks/PLAYBOOK_NAME.yml --limit "host1,host2" > > Negated limit.
> NOTE: Single quotes MUST be used to prevent bash > interpolation. > > ansible-playbook playbooks/PLAYBOOK_NAME.yml --limit 'all:!host1' > > Limit to host group > > ansible-playbook playbooks/PLAYBOOK_NAME.yml --limit 'group1'

Solution 4 - Ansible

This is a bit late, but I think you could use the --limit or -l command to limit the pattern to more specific hosts. (version 2.3.2.0)

You could have

- hosts: all (or group)
  tasks:
  - some_task

and then ansible-playbook playbook.yml -l some_more_strict_host_or_pattern and use the --list-hosts flag to see on which hosts this configuration would be applied.

Solution 5 - Ansible

I'm using another approach that doesn't need any inventory and works with this simple command:

ansible-playbook site.yml -e working_host=myhost

To perform that, you need a playbook with two plays:

  • first play runs on localhost and add a host (from given variable) in a known group in inmemory inventory
  • second play runs on this known group

A working example (copy it and runs it with previous command):

- hosts: localhost
  connection: local
  tasks:
  - add_host:
      name: "{{ working_host }}"
      groups: working_group
    changed_when: false

- hosts: working_group
  gather_facts: false
  tasks:
  - debug:
      msg: "I'm on {{ ansible_host }}"

> I'm using ansible 2.4.3 and 2.3.3

Solution 6 - Ansible

I changed mine to default to no host and have a check to catch it. That way the user or cron is forced to provide a single host or group etc. I like the logic from the comment from @wallydrag. The empty_group contains no hosts in the inventory.

  • hosts: "{{ variable_host | default('empty_group') }}"

Then add the check in tasks:

tasks:

  • name: Fail script if required variable_host parameter is missing fail: msg: "You have to add the --extra-vars='variable_host='" when: (variable_host is not defined) or (variable_host == "")

Solution 7 - Ansible

An other solution is to use the special variable ansible_limit which is the contents of the --limit CLI option for the current execution of Ansible.

- hosts: "{{ ansible_limit | default(omit) }}"

If the --limit option is omitted, then Ansible issues a warning, but does nothing since no host matched.

[WARNING]: Could not match supplied host pattern, ignoring: None

PLAY ****************************************************************
skipping: no hosts matched

Solution 8 - Ansible

Just came across this googling for a solution. Actually, there is one in Ansible 2.5. You can specify your inventory file with --inventory, like this: ansible --inventory configs/hosts --list-hosts all

Solution 9 - Ansible

If you want to run a task that's associated with a host, but on different host, you should try delegate_to.

In your case, you should delegate to your localhost (ansible master) and calling ansible-playbook command

Solution 10 - Ansible

I am using ansible 2.5 (2.5.3 exactly), and it seems that the vars file is loaded before the hosts param is executed. So you can set the host in a vars.yml file and just write hosts: {{ host_var }} in your playbook

For example, in my playbook.yml:

---
- hosts: "{{ host_name }}"
  become: yes
  vars_files:
    - vars/project.yml
  tasks:
    ... 

And inside vars/project.yml:

---

# general
host_name: your-fancy-host-name

Solution 11 - Ansible

Here's a cool solution I came up to safely specify hosts via the --limit option. In this example, the play will end if the playbook was executed without any hosts specified via the --limit option.

This was tested on Ansible version 2.7.10

---
- name: Playbook will fail if hosts not specified via --limit option.
  # Hosts must be set via limit. 
  hosts: "{{ play_hosts }}"
  connection: local
  gather_facts: false
  tasks:
  - set_fact:
      inventory_hosts: []
  - set_fact:
      inventory_hosts: "{{inventory_hosts + [item]}}"
    with_items: "{{hostvars.keys()|list}}"
  
  - meta: end_play
    when: "(play_hosts|length) == (inventory_hosts|length)"

  - debug:
      msg: "About to execute tasks/roles for {{inventory_hostname}}"

Solution 12 - Ansible

This worked for me as I am using Azure devops to deploy an application using CICD pipelines. I had to make this hosts (in yml file) more dynamic so in release pipeline I can add it's value, for example:

--extra-vars "host=$(target_host)"

pipeline_variable

My ansible playbook looks like this

- name: Apply configuration to test nodes
  hosts: '{{ host }}'

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionluqo33View Question on Stackoverflow
Solution 1 - AnsiblewallydragView Answer on Stackoverflow
Solution 2 - AnsibleMrunal GosarView Answer on Stackoverflow
Solution 3 - AnsibleTmTronView Answer on Stackoverflow
Solution 4 - AnsibleJonathan HamelView Answer on Stackoverflow
Solution 5 - AnsibleNelson G.View Answer on Stackoverflow
Solution 6 - AnsibleTony-CaffeView Answer on Stackoverflow
Solution 7 - AnsibleManoloView Answer on Stackoverflow
Solution 8 - AnsibleBebefView Answer on Stackoverflow
Solution 9 - AnsiblenghnamView Answer on Stackoverflow
Solution 10 - AnsibleLightManView Answer on Stackoverflow
Solution 11 - Ansiblesudo soulView Answer on Stackoverflow
Solution 12 - AnsibleMuhammad ZeeshanView Answer on Stackoverflow