Override hosts variable of Ansible playbook from the command line
AnsibleAnsible PlaybookAnsible 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 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)"
My ansible playbook looks like this
- name: Apply configuration to test nodes
hosts: '{{ host }}'