Checking if a value exists in a list of dictionaries - ansible

I have a dictionary with a list of dictionaries. How do I do a lookup to check if a certain name exists in key 'firstname'?
---
- hosts: localhost
vars:
Persons:
- firstname: Bob
- firstname: Sarah
name: Sarah
tasks:
- debug: msg="{{ name in Persons }}" - ????

Found the answer on another post -
- debug: msg="{{ name in Persons | map(attribute='firstname') | list }}"

Related

(solved) Ansible loop (subelements) in users>name, users>authorized_key_file and users>authorized_keyS

i tried this test code :
- hosts: localhost
gather_facts: no
vars:
users:
- name: user1
authorized_keys_file: authorized_keys_file_1.log
authorized_keys:
- key1
- key2
- name: user2
authorized_keys_file: authorized_keys_file_2.log
authorized_keys:
- key1
tasks:
- name: List keys to add to authorized_key_file
debug:
msg: "User={{ item.0.name }} File={{ item.0.authorized_keys_file }} key={{ item.1 }}"
loop:
- "{{ users | subelements('authorized_keys') }}"
but i got the error :
The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'name'
expected result (to be used in ansible.posix.authorized_key) :
User=user1 File=authorized_keys_file_1 key=key1
User=user1 File=authorized_keys_file_1 key=key2
User=user2 File=authorized_keys_file_2 key=key1
Do you have an idea of what to do !?
Regards.
I found the solution ! "loop" must be on one line.
This is because i did a conversion from the old "with_subelements"
old version :
with_subelements:
- "{{ users }}"
- authorized_keys
New version (oneline...) :
loop: "{{ users | subelements('authorized_keys') }}"

Ansible: Retrieve Data from inventory

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

"search" a string in "when:" conditional statement?

In the following playbook, I have to use search in a when: statement. Can you please tell me what is wrong with the statement when: item.name is search (domain_list)? domain_list is an array variable defined in files.yml as shown below.
---
- hosts: localhost
gather_facts: False
connection: local
vars_files:
- files.yml
vars:
infra:
- name: ironman.vcloud-lab.com
connection_state: CONNECTED
- name: hulk.vcloud-lab.com
connection_state: CONNECTED
- name: captain.vcloud-lab.com
connection_state: DISCONNECTED
- name: hawkeye.vcloud-lab.com
connection_state: DISCONNECTED
tasks:
- name: Filter list of only connected esxi
set_fact:
esxilist: "{{esxilist | default([]) + [item]}}"
with_items: "{{infra}}"
when: item.name is search (domain_list) ## <= please correct me here, it doesn't work
- name: Show connected esxi list information
debug:
var: esxilist
files.yml
---
domain_list:
- ironman
- captain
Select the short name and test the presence in the list. For example, use regex_replace
- name: Filter list of only connected esxi
set_fact:
esxilist: "{{ esxilist|default([]) + [item] }}"
loop: "{{ infra }}"
when: _short in domain_list
vars:
_short: "{{ item.name|regex_replace('^(.*?)\\.(.*)$', '\\1') }}"
gives
esxilist:
- connection_state: CONNECTED
name: ironman.vcloud-lab.com
- connection_state: DISCONNECTED
name: captain.vcloud-lab.com
The next option is to split the string and select the first item. For example
vars:
_short: "{{ item.name.split('.')|first }}"
gives the same result.
See How to create a Minimal, Reproducible Example. For example
- hosts: localhost
vars:
domain_list: [a, b]
infra:
- {name: a.vcloud-lab.com, state: CONNECTED}
- {name: b.vcloud-lab.com, state: DISCONNECTED}
- {name: c.vcloud-lab.com, state: CONNECTED}
tasks:
- set_fact:
l1: "{{ l1|default([]) + [item] }}"
with_items: "{{ infra }}"
when: _short in domain_list
vars:
_short: "{{ item.name.split('.')|first }}"
- debug:
var: l1

Nested loop with value comparison in Ansible

in the example below, how do I retrieve the grade of students listed in selected_students_list? I need to process the grade of the student. In other programming languages I can use nested loops but I'm a bit confused how to implement this in Ansible
---
- name: Test
hosts: localhost
vars:
- all_students_list:
- {
name: "Charlie",
grade: 9,
}
- {
name: "Alice",
grade: 8,
}
- {
name: "Bob",
grade: 7,
}
- {
name: "Darla",
grade: 10,
}
- {
name: "Edith",
grade: 11,
}
- selected_students_list:
- Alice
- Bob
tasks:
- name: Process selected students
debug:
msg: "<PROCESS THE GRADE OF SELECTED STUDENTS i.e. Alice and Bob>"
with_nested:
- "{{ all_students_list }}"
- "{{ selected_students_list }}"
It's possible either to list selected students item by item. For example
- name: Process selected students
debug:
msg: "PROCESS THE GRADES [{{ item.grade }}]
OF SELECTED STUDENT {{ item.name }}"
loop: "{{ all_students_list|
selectattr('name', 'in', selected_students_list)|
list }}"
gives
msg: PROCESS THE GRADES [8] OF SELECTED STUDENT Alice
msg: PROCESS THE GRADES [7] OF SELECTED STUDENT Bob
Or, to display the whole list at once. For example
- name: Process selected students
debug:
msg: "{{ msg.split('\n')[:-1] }}"
vars:
my_list: "{{ all_students_list|
selectattr('name', 'in', selected_students_list)|
list }}"
msg: |
PROCESS THE GRADES OF SELECTED STUDENTS
{{ my_list|to_yaml }}
gives
msg:
- PROCESS THE GRADES OF SELECTED STUDENTS
- '- {grade: 8, name: Alice}'
- '- {grade: 7, name: Bob}'
If the data is in a dictionary, instead of a list, the listing item by item is very simple. For example
- name: Test
hosts: localhost
vars:
- all_students_dict:
Charlie:
grade: 9
Alice:
grade: 8
Bob:
grade: 7
Darla:
grade: 10
Edith:
grade: 11
- selected_students_list:
- Alice
- Bob
tasks:
- name: Process selected students
debug:
msg: "PROCESS THE GRADES [{{ all_students_dict[item].grade }}]
OF SELECTED STUDENT {{ item }}"
loop: "{{ selected_students_list }}"
gives
msg: PROCESS THE GRADES [8] OF SELECTED STUDENT Alice
msg: PROCESS THE GRADES [7] OF SELECTED STUDENT Bob
It's possible to use filters dict2items and items2dict to create a dictionary of selected students. For example
- name: Process selected students
debug:
msg: "{{ msg.split('\n')[:-1] }}"
vars:
my_dict: "{{ all_students_dict|
dict2items|
selectattr('key', 'in', selected_students_list)|
list|
items2dict }}"
msg: |
PROCESS THE GRADES OF SELECTED STUDENTS
{{ my_dict|to_yaml }}
gives
msg:
- PROCESS THE GRADES OF SELECTED STUDENTS
- 'Alice: {grade: 8}'
- 'Bob: {grade: 7}
This should do
vars:
...
query: "[?name == `{{ item }}`].grade"
tasks:
- name: Process selected students
debug:
msg: "{{ all_students_list | json_query(query) }}"
loop: "{{ selected_students_list }}"
Check json-query-filter for more details.

Ansible with_subelements

I am having a hard time understanding the logic of ansible with_subelements syntax, what exactly does with_subelements do? i took a look at ansible documentation on with_subelements here https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#with-subelements and was not very helpful. I also saw a playbook with with_subelements example on a blog
---
- hosts: cent
vars:
users:
- name: jagadish
comments:
- 'Jagadish is Good'
- name: srini
comments:
- 'Srini is Bad'
tasks:
- name: User Creation
shell: useradd -c "{{ item.1 }}" "{{ item.0.name }}"
with_subelements:
- users
- comments
what do item.1 and item.0 refer to?
This is really bad example of how subelements lookup works. (And has old, unsupported, syntax as well).
Look at this one:
---
- hosts: localhost
gather_facts: no
vars:
families:
- surname: Smith
children:
- name: Mike
age: 4
- name: Kate
age: 7
- surname: Sanders
children:
- name: Pete
age: 12
- name: Sara
age: 17
tasks:
- name: List children
debug:
msg: "Family={{ item.0.surname }} Child={{ item.1.name }} Age={{ item.1.age }}"
with_subelements:
- "{{ families }}"
- children
Task List children is like a nested loop over families list (outer loop) and over children subelement in each family (inner loop).
So you should provide a list of dicts as first argument to subelements and name of subelement you want to iterate inside each outer item.
This way item.0 (family in my example) is an outer item and item.1 (child in my example) is an inner item.
In Ansible docs example subelements is used to loop over users (outer) and add several public keys (inner).

Resources