Ansible: Retrieve Data from inventory - ansible

I need to retrieve the IP of bob_server from the inventory file. I am not clear as to what combination to use in filter, lookup, and when? Depending on the inventory filebob_server and alice_server names can change, but app_type won't change. My playbook logic is obviously wrong, Can someone guide me the correct way to fetch IP address when app_type = bob
My current Inventory file:
---
all:
hosts:
children:
bob_server:
hosts: 10.192.2.6
vars:
app_type: bob
alice_server:
hosts: 10.192.2.53
vars:
app_type: alice
My Playbook
---
- hosts: localhost
name: Retrive data
tasks:
- name: Set Ambari IP
set_fact:
ambariIP: "{{ lookup('hosts', children) }}"
when: "hostvars[app_type] == 'bob'"

Given the inventory
shell> cat hosts-01
---
all:
hosts:
children:
bob_server:
hosts: 10.192.2.6
vars:
app_type: bob
alice_server:
hosts: 10.192.2.53
vars:
app_type: alice
The simple option is using ansible-inventory, e.g.
- hosts: localhost
tasks:
- command: ansible-inventory -i hosts-01 --list
register: result
- set_fact:
my_inventory: "{{ result.stdout|from_yaml }}"
- debug:
var: my_inventory.bob_server.hosts
gives
my_inventory.bob_server.hosts:
- 10.192.2.6
If you want to parse the file on your own read it into a dictionary and flatten the paths, e.g. (install ansible.utils ansible-galaxy collection install ansible.utils)
- include_vars:
file: hosts-01
name: my_hosts
- set_fact:
my_paths: "{{ lookup('ansible.utils.to_paths', my_hosts) }}"
- debug:
var: my_paths
gives
my_paths:
all.children.alice_server.hosts: 10.192.2.53
all.children.alice_server.vars.app_type: alice
all.children.bob_server.hosts: 10.192.2.6
all.children.bob_server.vars.app_type: bob
all.hosts: null
Now select the keys ending bob_server.hosts
- set_fact:
bob_server_hosts: "{{ my_paths|
dict2items|
selectattr('key', 'match', '^.*bob_server\\.hosts$')|
items2dict }}"
gives
bob_server_hosts:
all.children.bob_server.hosts: 10.192.2.6
and select the IPs
- set_fact:
bob_server_ips: "{{ bob_server_hosts.values()|list }}"
gives
bob_server_ips:
- 10.192.2.6
The inventory is missing the concept of groups. See Inventory basics: formats, hosts, and groups. Usually, the value of children is a group of hosts. In this inventory, the value of children is the single host. This is conceptually wrong but still valid, .e.g
- hosts: bob_server
gather_facts: false
tasks:
- debug:
var: inventory_hostname
gives
shell> ansible-playbook -i hosts-01 playbook.ym
...
TASK [debug] ****************************************************
ok: [10.192.2.6] =>
inventory_hostname: 10.192.2.6

Related

Ansible: Get Variable with inventory_hostname

I have the following passwords file vault.yml:
---
server1: "pass1"
server2: "pass2"
server3: "pass3"
I am loading these values in a variable called passwords:
- name: Get Secrets
set_fact:
passwords: "{{ lookup('template', './vault.yml')|from_yaml }}"
delegate_to: localhost
- name: debug it
debug:
var: passwords.{{ inventory_hostname }}
The result of the debugging task shows me the result I want to get: The password for the specific host.
But if I set the following in a variables file:
---
ansible_user: root
ansible_password: passwords.{{ inventory_hostname }}
This will not give me the desired result. The ansible_password takes "passwords" literally and not as a variable.
How can I achieve the same result I got when debugging the passwords.{{ inventory_hostname }}?
Regarding the part
... if I set the following in a variables file ...
I am not sure since I miss some information about your use case and data flow. However, in general the syntax ansible_password: "{{ PASSWORDS[inventory_hostname] }}" might work for you.
---
- hosts: localhost
become: false
gather_facts: false
vars:
PASSWORDS:
SERVER1: "pass1"
SERVER2: "pass2"
SERVER3: "pass3"
localhost: "pass_local"
tasks:
- name: Debug var
debug:
var: PASSWORDS
- name: Set Fact 'ansible_password'
set_fact:
ansible_password: "{{ PASSWORDS[inventory_hostname] }}"
- name: Debug var
debug:
var: ansible_password
In that way you can access a element by name.

Ansible how to set_fact with condition or var base on condition

I have 3 types of servers: dev, qa and prod. I need to send files to server specific home directories, i.e:
Dev Home Directory: /dev/home
QA Home Directory:/qa/home
PROD Home Directory: /prod/home
I have var set as boolean to determine the server type and think of using set_fact with condition to assign home directories for the servers. My playbook looks like this:
---
- hosts: localhost
var:
dev: "{{ True if <hostname matches dev> | else False }}"
qa: "{{ True if <hostname matches qa> | else False }}"
prod: "{{ True if <hostname matches prod> | else False }}"
tasks:
- set_facts:
home_dir: "{{'/dev/home/' if dev | '/qa/home' if qa | default('prod')}}"
However, then I ran the playbook, I was getting error about 'template expected token 'name', got string> Anyone know what I did wrong? Thanks!
Use match test. For example, the playbook
shell> cat pb.yml
- hosts: localhost
vars:
dev_pattern: '^dev_.+$'
qa_pattern: '^qa_.+$'
prod_pattern: '^prod_.+$'
dev: "{{ hostname is match(dev_pattern) }}"
qa: "{{ hostname is match(qa_pattern) }}"
prod: "{{ hostname is match(prod_pattern) }}"
tasks:
- set_fact:
home_dir: /prod/home
- set_fact:
home_dir: /dev/home
when: dev|bool
- set_fact:
home_dir: /qa/home
when: qa|bool
- debug:
var: home_dir
gives (abridged)
shell> ansible-playbook pb.yml -e hostname=dev_007
home_dir: /dev/home
Notes:
The variable prod is not used because /prod/home is default.
prod/home is assigned to home_dir first because it's the default. Next tasks can conditionally overwrite home_dirs.
Without the variable hostname defined the playbook will crash.
A simpler solution that gives the same result is creating a dictionary of 'pattern: home_dir'. For example
- hosts: localhost
vars:
home_dirs:
dev_: /dev/home
qa_: /qa/home
prod_: /prod/home
tasks:
- set_fact:
home_dir: "{{ home_dirs|
dict2items|
selectattr('key', 'in' , hostname)|
map(attribute='value')|
list|
first|default('/prod/home') }}"
- debug:
var: home_dir
Adding an alternative method to achieve this. This kind of extends the group_vars suggestion given by #mdaniel in his comment.
Ansible has a ready-made mechanism to build the variables based on the inventory hosts and groups. If you organize your inventory, you can avoid a lot of complication in trying to match host patterns.
Below is a simplified example, please go through the link above for more options.
Consider an inventory file /home/user/ansible/hosts:
[dev]
srv01.dev.example
srv02.dev.example
[qa]
srv01.qa.example
srv02.qa.example
[prod]
srv01.prod.example
srv02.prod.example
Using group_vars:
Then you can have below group_var files in /home/user/ansible/group_vars/ (matching inventory group names):
dev.yml
qa.yml
prod.yml
In dev.yml:
home_dir: "/dev/home"
In qa.yml:
home_dir: "/qa/home"
In prod.yml:
home_dir: "/prod/home"
Using host_vars:
Or you can have variables specific to hosts in host_vars directory /home/user/ansible/host_vars/:
srv01.dev.example.yml
srv01.prod.example.yml
# and so on
In srv01.dev.example.yml:
home_dir: "/dev/home"
In srv01.prod.example.yml:
home_dir: "/prod/home"
These variables will be picked based on which hosts you run the playbook, for example the below playbook:
---
- hosts: dev
tasks:
- debug:
var: home_dir
# will be "/dev/home"
- hosts: prod
tasks:
- debug:
var: home_dir
# will be "/prod/home"
- hosts: srv01.dev.example
tasks:
- debug:
var: home_dir
# will be "/dev/home"
- hosts: srv01.prod.example
tasks:
- debug:
var: home_dir
# will be "/prod/home"

Filter hostvar by special propery

I have host.yml like this
---
all:
hosts:
server-a:
server_dc: "Hetzner"
ansible_host: 192.168.1.1
server-b:
server_dc: "OVH"
ansible_host: 192.168.1.2
And play book debug is:
- name: sample
debug:
var: hostvars
And all hostvars debug success.
How to get same hostvars variable but filtered. Any of that server_dc is equal OVH
I dont want to iterate for template, i just one new filtered variable that contain all other properies.
I need another variable that i debug see this output:
['server-b']
This I believe meets your requirement (removing 'no_log: true', will result in the complete dictionary being printed in your playbook output):
- set_fact:
filtered_hosts: "{{ filtered_hosts | default({}) | combine({item.key: item.value}) }}"
when: "item.value.server_dc == 'OVH'"
with_dict: "{{ hostvars }}"
no_log: true
- debug:
var: filtered_hosts

ansible vars_files and extra_vars to read input

looking to pass the dict to read a set of key value pairs based on the location. When values are hardcoded to the playbook, it works fine but calling through extra_vars giving an error message. Not sure even if it supports. appreciate, your thoughts and inputs.
ansible-playbook play3.yml -e '{"var1":"loc2"}' -vv
play3.yml
---
- name: testing
hosts: localhost
connection: local
gather_facts: no
vars_files:
- var_file.yml
tasks:
- debug:
msg: "{{ var1['first'] }}"
var_file.yml
---
loc1:
first: name1
last: name2
loc2:
first: python
last: perl
...
"Anything's possible in an animated cartoon." -Bugs Bunny
This playook:
---
- name: testing
hosts: localhost
connection: local
gather_facts: no
vars_files:
- var_file.yml
tasks:
- debug:
var: "{{ item }}.first"
with_items: "{{ var1 }}"
Gave me this output:
TASK [debug] **********************************************************************************************************************************
task path: /home/jack/Ansible/CANES/PLAYBOOKS/play3.yml:9
ok: [localhost] => (item=None) => {
"loc2.first": "python"
}

Ansible Five random hosts from /etc/ansible/hosts

I have this playbook, it work using max_index but always takes the first 3 hosts from /etc/ansible/hosts , i need to take 3 random (and not repeated) hosts from that file.
playbook.yml
---
- hosts: ciscos
connection: local
gather_facts: false
tasks:
- group_by: key=limited_selection
when: play_hosts.index(inventory_hostname) < max_index | int
- hosts: limited_selection
gather_facts: no
/etc/ansible/hosts
[ciscos]
stagin ansible_host=10.xx.xx.1
stagin2 ansible_host=10.xx.xx.1
stagin3 ansible_host=10.xx.xx.1
stagin4 ansible_host=10.xx.xx.1
stagin5 ansible_host=10.xx.xx.1
Solution
You need to shuffle the elements of the group and choose three first. The Jinja2 expression for that is:
(groups['ciscos'] | shuffle)[0:3]
Implementation which should work, but has problems
You should be able to simply filter the group in the hosts declaration:
- hosts: "{{ (groups['ciscos'] | shuffle)[0:3] }}"
gather_facts: no
tasks:
- debug:
However the results are undeterministic - although the play shows as running against three randomly chosen hosts, the tasks are sometimes executed on 1, 2, 3, or 0:
PLAY [[u'stagin2', u'stagin4', u'stagin5']] *******************************************************************************
TASK [debug] **************************************************************************************************************
ok: [stagin2] => {
"msg": "Hello world!"
}
ok: [stagin5] => {
"msg": "Hello world!"
}
Workaround (implementation which works)
Use add_host module to create a filtered group:
- hosts: localhost
connection: local
gather_facts: no
tasks:
- add_host:
name: "{{ item }}"
groups: limited_selection
loop: "{{ (groups['ciscos'] | shuffle)[0:3] }}"
- hosts: limited_selection
gather_facts: no
tasks:
- debug:
What about something like this?
---
- hosts: ciscos
gather_facts: False
connection: local
tasks:
- name: Fact My Inventory
set_fact:
myinventory: "{{ ansible_play_batch | shuffle }}"
run_once: True
delegate_to: localhost
- name: Fact limited_selection
set_fact:
limited_selection: "{{ myinventory[0:max_index|int] }}"
run_once: True
delegate_to: localhost
- name: Create Inventory
add_host:
name: '{{ item }}'
groups: limited_selection
with_items: "{{ limited_selection }}"
delegate_to: localhost
- hosts: limited_selection
gather_facts: no
tasks:
- name: Debug
debug:
msg: "I'm in the limited selection group!"
Be careful with play_hosts as it is deprecated.
Note: I have kept the playbook with connection:ciscos instead of localhost for learning purposes and showing the ansible_play_batch and max_index variables. It is better to have a localhost play with groups instead of delegate_to:localhost

Resources