Loop through hosts with ansible [duplicate] - ansible

This question already has answers here:
How to use Ansible's with_item with a variable?
(2 answers)
Closed 5 years ago.
I have a problem to find a working solution to loop over my inventory.
I start my playbook with linking a intentory file:
ansible-playbook -i inventory/dev.yml playbook.yml
My playbook looks like this:
---
- hosts: localhost
tasks:
- name: Create VM if enviro == true
include_role:
name: local_vm_creator
when: enviro == 'dev'
So when loading the playbook the variable enviro is read from host_vars and sets the when condition to dev. The inventory file dev.yml looks like this:
[local_vm]
192.168.99.100
192.168.99.101
192.168.99.102
[local_vm_manager_1]
192.168.99.103
[local_vm_manager_2]
192.168.99.104
[local-all:children]
local_vm
local_vm_manager_1
local_vm_manager_2
My main.yml in my role local_vm_creator looks like this:
---
- name: Create test host
local_action: shell docker-machine create -d virtualbox {{ item }}
with_items:
- node-1
- node-2
- node-3
- node-4
- node-5
- debug: msg="host is {{item}}"
with_items: groups['local_vm']
And the problem is that i can't get the listed servers from the dev.yml inventory file.
it just returns:
ok: [localhost] => (item=groups['local_vm']) => {
"item": "groups['local_vm']",
"msg": "host is groups['local_vm']" }

If the only problem is with_items loop, replace it with:
with_items: "{{ groups['local_vm'] }}"
and you are good to go. Bare variables are not supported in with_ any more.

Related

Passing hostname to ansible playbook through extravars

I have to pass the host on which the Ansible command will be executed through extra vars.
I don't know in advance to which hosts the tasks will be applied to, and, therefore, my inventory file is currently missing the hosts: variable.
If I understood from the article "How to pass extra variables to an Ansible playbook" correctly, overwriting hosts is only possible by having already composed groups of hosts.
From the post Ansible issuing warning about localhost I gathered that referencing hosts to be managed in an Ansible inventory is a must, however, I still have doubts about it since the usage of extra vars was not mentioned in the given question.
So my question is: What can i do in order to make this playbook work?
- hosts: "{{ host }}"
tasks:
- name: KLIST COMMAND
command: klist
register: klist_result
- name: TEST COMMAND
ansible.builtin.shell: echo hi > /tmp/test_result.txt
... referencing hosts to be managed in an Ansible inventory is a must
Yes, that's the case. Regarding your question
What can I do in order to make this playbook work? (annot. without a "valid" inventory file)
you could try with the following workaround.
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- add_host:
hostname: "{{ target_hosts }}"
group: dynamic
- hosts: dynamic
become: true
gather_facts: true
tasks:
- name: Show hostname
shell:
cmd: "hostname && who am i"
register: result
- name: Show result
debug:
var: result
A call with
ansible-playbook hosts.yml --extra-vars="target_hosts=test.example.com"
resulting into execution on
TASK [add_host] ***********
changed: [localhost]
PLAY [dynamic] ************
TASK [Show hostname] ******
changed: [test.example.com]
In any case it is recommended to check how to build your inventory.
Further Documentation
add_host module – Add a host (and alternatively a group) to the ansible-playbook in-memory inventory

Is there a way to get a list of unreachable hosts in an ansible playbook

I have the ansible role coded below.
---
- name: Get host facts
set_fact:
serverdomain: "{{ansible_domain}}"
server_ip: "{{ansible_ip_addresses[1]}}"
- name: Host Ping Check
failed_when: false
win_ping:
register: var_ping
- name: Get Host name
debug: msg="{{the_host_name}}"
- name: Set Execution File and parameters
set_fact:
scriptfile: "{{ansible_user_dir}}\\scripts\\host_check.ps1"
params: "-servername '{{the_host_name}}' -response var_ping.failed"
- name: Execute script
win_command: powershell.exe "{{scriptfile}}" "{{params}}"
It works the way it should do but of course unreachable hosts are not touched at all. I would like to generate a list or is there a variable which contains all the unreachable hosts. Is it possible to have this in a comma delimmeted list and stored in a variable ?
Lastly, how/where do i need to set gather_facts: no. I tried several places to no avail.
EDIT 1
- name Unreachable servers
set_fact:
down: "{{ ansible_play_hosts_all | difference(ansible_play_hosts)}}"
- name: Set Execution File and parameters
set_fact:
scriptfile: "{{ansible_user_dir}}\\scripts\\host_check.ps1"
params: "-servername '{{the_host_name}}' -response var_ping.failed -unreachable_hosts {{ down }}"
- name: Execute script
win_command: powershell.exe "{{scriptfile}}" "{{params}}"
when: inventory_hostname == {{ db_server_host }}
Thanks for the answers, I have now been able to use thesame login in my ansible role, and it appears to work.
I do however have some questions. My playbook runs against hosts defined in my inventory, in this case what I want to achieve is a situation where the unreachable hosts is passed onto a powershell script right at the end and at once. So for example, 100 hosts, 10 were unreachable. The playbook should gather the 10 unreachable hosts, and pass the list of hosts in an array format to a powershell script or as a json data type.
All I want to do is be able to process the list of unreachable servers from my powershell script.
At the moment, I get
[
"server1",
"server2"
]
MY script will work with a format like this "server1","server2"
Lastly.The process of logging the unreachable servers at the end, only needs to happen once to a specific server which does the logging to a database. I created a task to do this as seen above.
db_server_host is being passed as a extra variables from ansible tower. The reason i added the when is that I only want it to run on the DB server and not on every host. I get the error The conditional check inventory_hostname == {{ db_server_host }} failed. The error was error while evaluating conditional inventory_hostname == {{ db_server_host }} 'mydatabaseServerName' is undefined
Q: "Get a list of unreachable hosts in an Ansible playbook."
Short answer: Create the difference between the lists below
down: "{{ ansible_play_hosts_all|difference(ansible_play_hosts) }}"
Details: Use Special Variables. Quoting:
ansible_play_hosts_all:
List of all the hosts that were targeted by the play.
ansible_play_hosts:
List of hosts in the current play run, not limited by the serial. Failed/Unreachable hosts are excluded from this list.
For example, given the inventory
shell> cat hosts
alpha
beta
charlie
delta
The playbook
- hosts: alpha,beta,charlie
gather_facts: true
tasks:
- block:
- debug:
var: ansible_play_hosts_all
- debug:
var: ansible_play_hosts
- set_fact:
down: "{{ ansible_play_hosts_all|difference(ansible_play_hosts) }}"
- debug:
var: down
run_once: true
gives (bridged) if alpha is unreachable (see below)
ansible_play_hosts_all:
- alpha
- beta
- charlie
ansible_play_hosts:
- beta
- charlie
down:
- alpha
Host alpha was unreachable
PLAY [alpha,beta,charlie] *************************************************
TASK [Gathering Facts] ****************************************************
fatal: [alpha]: UNREACHABLE! => changed=false
msg: 'Failed to connect to the host via ssh: ssh: connect to host test_14 port 22: No route to host'
unreachable: true
ok: [charlie]
ok: [beta]
...
Note
The dictionary hostvars keeps all hosts from the inventory, e.g.
- hosts: alpha,beta,charlie
gather_facts: true
tasks:
- debug:
var: hostvars.keys()
run_once: true
gives (abridged)
hostvars.keys():
- alpha
- beta
- charlie
- delta
tasks:
- ping:
register: ping_out
# one can include any kind of timeout or other stall related config here
- debug:
msg: |
{% for k in hostvars.keys() %}
{{ k }}: {{ hostvars[k].ping_out.unreachable|default(False) }}
{% endfor %}
yields (when an inventory consisting of only an alive alpha host):
msg: |-
alpha: False
beta: True
charlie: True
Lastly, how/where do i need to set gather_facts: no. I tried several places to no avail.
It only appears one time in the playbook keywords and thus it is a play keyword:
- hosts: all
gather_facts: no
tasks:
- name: now run "setup" for those who you wish to gather_facts: yes
setup:
when: inventory_host is awesome

Ansible - dynamic variable with host_vars and string in the name

I'm trying to create a job role in Ansible to run yum install/update of packages, which will be provided by a 3rd party system as a .yml file to vars directory in a role with following convention: server01.yml, server02.yml, serverX.yml with variable in form packageList_serverNumber: 'list of packages'.
This variable will be read using a task:
- name: server update packages from host_vars
yum:
name: "{{ install_pkgs }}"
state: latest
This should point to host_vars file for specific host:
install_pkgs: "{{ packageList_server01 }}"
As this task should only run when the variable is defined, I was trying to use when clause with variable which will point to packageList_serverNumber. When I hardcode it, like below it is working:
when: packageList_server01 is defined
Can you please advise how to make it dynamic?
I was trying with:
when: packageList_{{hostvars[inventory_hostname]}} is defined
But unfortunately this is not working.
Use lookup plugin vars. Run the command below to see the details
shell> ansible-doc -t lookup vars
Given the vars files
shell> cat roles/test4/vars/server01.yml
packageList_server01: [pkg1, pkg2, pkg3]
shell> cat roles/test4/vars/server02.yml
packageList_server02: [pkg4, pkg5, pkg6]
shell> cat roles/test4/vars/server03.yml
packageList_server03: [pkg7, pkg8, pkg9]
read the vars, declare the variable install_pkgs, and use it
shell> cat roles/test4/tasks/main.yml
- include_vars: "vars/{{ inventory_hostname }}.yml"
- set_fact:
install_pkgs: "{{ lookup('vars', 'packageList_' ~ inventory_hostname) }}"
- debug:
msg: "Install {{ install_pkgs }}"
For example the playbook
- hosts: server01,server02,server03
gather_facts: false
roles:
- test4
gives (abridged)
TASK [test4 : debug] ****
ok: [server01] =>
msg: Install ['pkg1', 'pkg2', 'pkg3']
ok: [server03] =>
msg: Install ['pkg7', 'pkg8', 'pkg9']
ok: [server02] =>
msg: Install ['pkg4', 'pkg5', 'pkg6']

How to run ansible playbook on different hosts or set of hosts based on supplied variables?

I have a requirement to setup environment on multiple hosts. I need to read variables from a file and I want them to execute on a particular hosts or a set of hosts. I am unable to make out how can I use single task to achieve this?
Assuming I have a file which contains variables in a following format:
container_name: java1
container_hostname: java-mc1
script_location: /tmp
execute_on_hosts: host1,host2,host3
container_name: java2
container_hostname: java-mc2
script_location: /tmp
execute_on_hosts: host1
I want my task to read from this file and execute the task on specified hosts only by matching hostname in hosts section (provided in playbook).
You should read the Ansible Variables docs. Whilst you can probably get something to work, you are not really using Ansible the way it was intended.
Ansible fundamentally is built around the concept of 'plays'. Each play represents one or more tasks, that should be targetted at a host or group of hosts. Those plays are then packaged up into a 'playbook'. Ansible provides methods to organise variables in the 'Inventory'. A common approach would be to do something like:
/some/ansible/dir/hosts
[mc1_hosts]
host1
host2
host3
[mc2_hosts]
host1
/some/ansible/dir/group_vars/mc1_hosts
---
mc1_container_data:
container_name: java1
container_hostname: java-mc1
script_location: /tmp
/some/ansible/dir/group_vars/mc2_hosts
---
mc2_container_data:
container_name: java2
container_hostname: java-mc2
script_location: /tmp
/some/ansible/dir/playbook.yml
---
- hosts: mc1_hosts
tasks:
- name: Display variables
debug:
msg: "{{ mc1_container_data }}"
- name: Display container_name
msg: "{{ mc1_container_data.container_name }}"
- hosts: mc2_hosts
tasks:
- name: Display variables
debug:
msg: "{{ mc2_container_data }}"
- name: Display container_name
msg: "{{ mc2_container_data.container_name }}"
And finally ansible-playbook playbook.yml to run those tasks.

Ansible, how to define a list in host inventory?

I have a playbook and I want to define a list of strings in my hosts file.
Here's my host file:
[dashboard]
1.2.3.4 dashboard_domain=test site_domain=['one','two','foo', 'bar']
Here's my playbook that I attempted to write using the list documentation:
---
- hosts: dashboard
gather_facts: False
remote_user: ubuntu
become: yes
tasks:
- name: ping
ping:
- debug:
msg: "Domain: {{dashboard_domain}}"
- debug:
msg: "Site: {{ item }}"
with_items: "{{site_domain}}"
However running this playbook with ansible-playbook -i hosts ping.yml causes this error:
TASK: [debug ] ****************************************************************
fatal: [1.2.3.4] => with_items expects a list or a set
This seems to be an issue of transferring the defined list from the host file to the playbook because defining the list directly in the playbook works:
---
- hosts: dashboard
gather_facts: False
remote_user: ubuntu
become: yes
vars:
site_domain: ['one','two','foo', 'bar']
tasks:
#### APPLY HTTP-AUTH ####
- name: ping
ping:
- debug:
msg: "Domain: {{dashboard_domain}}"
- debug:
msg: "Site: {{ item }}"
with_items: "{{site_domain}}"
Just quote the variable value:
[dashboard]
1.2.3.4 dashboard_domain=test site_domain="['one','two','foo', 'bar']"
It seems in case of INI-formatted inventory files, Ansible does not parse the variable value if it starts with an unquoted [ and passes it as a string.
Regarding your example: I'm not sure why you're not getting an expected key=value error on reading the inventory file, if you really have a space inside.
#techraf does answer your question and their solution is perfect if every host in the dashboard group has a site_domain list with different values.
Looking at your playbook, though, it seems that site_domain is constant across the whole dashboard group. If you had 10 hosts in dashboard, you would have to copy the list into each host's line. To avoid the repetition, you could have a dashboard:vars section in your inventory, where you can define variables that have the same value for all the hosts in the group:
[dashboard:vars]
site_domain="['one','two','foo', 'bar']"
[dashboard]
1.2.3.4 dashboard_domain=test
1.2.3.5 dashboard_domain=uat
1.2.3.6 dashboard_domain=integ
https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#assigning-a-variable-to-many-machines-group-variables
If your inventory folder is more structured, you could also define variables for the dashboard group in a separate file, in YAML. Your inventory folder tree could be:
inventories
|
+-- group_vars
| \-- dashboard.yml
|
+-- hosts.ini
In that configuration, dashboard.yml could simply be:
site_domain: ['one', 'two', 'foo', 'bar']
...or:
site_domain:
- one
- two
- foo
- bar
https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#organizing-host-and-group-variables

Resources