Error 'list object' has no attribute 'name' - ansible

I have some problems with understanding how i can get value from array
I declared variable names using this code
- name: 'Collecting values'
set_fact:
names:
- { name: 'Primary', host: '{{ hostname1 }}'}
- { name: 'Secondary', host: '{{ hostname1 }}'}
After it i successfully used this var in the loop
- include_tasks: ./sometask1.yml
vars:
hostname_name: '{{ item_outer.name }}'
hostname_host: '{{ item_outer.host}}'
loop: '{{ names}}'
loop_control:
loop_var: item_outer
But in the second task
- import_tasks: ./sometask2.yml
vars:
hostname: '{{ names.name }}'
I have an error
fatal: [example.domain.com]: FAILED! => {
"msg": "'list object' has no attribute 'name'"
}
What i'm doing wrong?

The answer was provided in the comments:
names is a list of dictionaries, all those dictionaries having a name attribute, but the list itself does not have a name attribute. What are you trying to access / achieve with that non-working task? – β.εηοιτ.βε 2023-01-30 09:03
I'm understand, thank you. In real life second task looks like names: '{{ names.name | join(',') }}'. I need to create string with all names separated by ',' – Po_temkin 2023-01-30 09:09
Then: {{ names | map(attribute='name') | join(',') }} – β.εηοιτ.βε
2023-01-30 09:15

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') }}"

How to generate a list with a range and save it in a var of a playbook?

How can I create a list like this in an Ansible playbook:
['user1', 'user2', 'user3', '...', 'user20']
and save it to a var in that same playbook?
I tried Using set_facts and with_items together in Ansible and modified the answer of #serge a little bit.
My code looks like the following:
- name: preparation for list
set_fact:
userItem: userItem= "{{ item }}"
with_sequence: start=1 end=20 format=user%d
register: userResult
- name: make a list
set_fact: users= "{{ userResult.results | map(attribute= 'ansible_facts.userItem) | list }}"
- name resulted list
debug: var=users
But I get the following error:
TASK [make a list] *****************************************************************
fatal: [10.0.2.11]: FAILED! => {"msg": "template error while templating string: unexpected end of template, expected ',' .. String: \"{{ userResult.results | map(attribute"}
I want to use that list in a task where I change a line in a configuration file like this:
- name: allow users to ssh into jail
lineinfile:
path: /etc/ssh/shd_config
regexp: '^AllowUsers '
line: 'AllowUsers ansible {{ users }}'
notify: "restart ssh"
where {{ users }} should be replaced with the desired list.
Is there a way I can achieve getting the desired list saved in some kind of variable (not a file) so I can use it in other tasks?
You can create a list in a set_fact right away, there is no need for any intermediary task:
- set_fact:
users: "{{ users | default([]) + ['user%s' | format(item)] }}"
loop: "{{ range(1, 21) | list }}"
Which gives
{
"users": [
"user1",
"user2",
"user3",
"user4",
"user5",
"user6",
"user7",
"user8",
"user9",
"user10",
"user11",
"user12",
"user13",
"user14",
"user15",
"user16",
"user17",
"user18",
"user19",
"user20"
]
}
This is actually really close to the example found in the documentation: https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#with-sequence
This said you won't be able to fit a list into your lineinfile module, because you do need a string.
So you can either create a string right away:
- set_fact:
users: "{{ users | default('') + ' user%s' | format(item) }}"
loop: "{{ range(1, 21) | list }}"
- lineinfile:
path: /etc/ssh/shd_config
regexp: '^AllowUsers '
line: 'AllowUsers ansible {{ users }}'
notify: "restart ssh"
So, instead of creating a list we are creating a string:
{
"users": " user1 user2 user3 user4 user5 user6 user7 user8 user9 user10 user11 user12 user13 user14 user15 user16 user17 user18 user19 user20"
}
Or use the join filter when using the users list:
- set_fact:
users: "{{ users | default([]) + ['user%s' | format(item)] }}"
loop: "{{ range(1, 21) | list }}"
- lineinfile:
path: /etc/ssh/shd_config
regexp: '^AllowUsers '
line: 'AllowUsers ansible {{ users | join(' ') }}'
notify: "restart ssh"
For example
- set_fact:
users: "{{ ['user']|product(range(start,end))|map('join')|list }}"
vars:
start: 1
end: 5
gives
users:
- user1
- user2
- user3
- user4
The next option gives the same result
- set_fact:
users: "{{ query('sequence', user_range) }}"
vars:
start: 1
end: 4
user_range: "start={{ start }} end={{ end }} format=user%d"

How to get interpolated value of variable in Ansible / Jinja2

I'm trying to define Ansible variables this way:
user:
name: First Last
nick: '{{ vars["user"]["name"] | regex_replace("\W", "_") }}'
email: '{{ vars["user"]["nick"] }}#example.com'
And the result email is: "{{ vars[\"user\"][\"name\"] | regex_replace(\"\\W\", \"_\") }}#example.com.
I also tried to set email like this: {{ lookup("vars", "user.nick") }}#example.com or {{ lookup("vars", "user")["nick"] }}#example.com, and it says An unhandled exception occurred while running the lookup plugin 'vars'.
Is there a way to get resulting variable values as:
user:
name: First Last
nick: First_Last
email: First_Last#example.com
?
ansible 2.9.10,
python version = 3.8.5
It's not possible cross-reference keys in a dictionary. It's necessary to declare the variables outside the dictionary. For example, the playbook
- hosts: localhost
vars:
my_name: First Last
my_nick: "{{ my_name | regex_replace('\\W', '_') }}"
user:
name: "{{ my_name }}"
nick: "{{ my_nick }}"
email: "{{ my_nick }}#example.com"
tasks:
- debug:
var: user
gives (abridged)
user:
email: First_Last#example.com
name: First Last
nick: First_Last
A more flexible option is to create the variables in the loop. For example, the playbook
- hosts: localhost
vars:
users:
"First Last":
domain: example.com
tasks:
- debug:
msg:
- "name: {{ name }}"
- "nick: {{ nick }}"
- "email: {{ email }}"
loop: "{{ users|dict2items }}"
vars:
name: "{{ item.key }}"
nick: "{{ item.key|regex_replace('\\W', '_') }}"
email: "{{ nick ~ '#' ~ item.value.domain }}"
gives (abridged)
msg:
- 'name: First Last'
- 'nick: First_Last'
- 'email: First_Last#example.com'

Conditional set_fact only with selectattr?

I am trying to use set_fact, but only when an item is defined. I'm having trouble figuring out how to do this.
Playbook:
---
- hosts: localhost
vars:
apps:
- name: app1
role: "app1-role"
- name: app2
role: "app2-role"
- name: app4
role: "app4-role"
tasks:
- name: "Find a matching app, and print the values"
set_fact:
app: "{{ apps | selectattr('name', 'match', app_name) | first }}"
The task fails when there is no match, for example: app_name=app3, with this message:
FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: No first item, sequence was empty.
I have tried a few different conditionals, but I'm not quite sure how to structure this.
when: (apps | selectattr('name', 'match', app_name)) is undefined
This condition always evaluates as False - skipping: [localhost] => {"changed": false, "skip_reason": "Conditional result was False"}.
I tried this :
when: "{{ apps | selectattr('name', 'match', app_name) | list }}"
It seems that without the list filter, selectattr returning a generator object does not seem to allow converting to a boolean for the test evaluation. I'm not 100% sure about this interpretation though.
Note that having is defined or not would not change anything, it would be totally equivalent.
Try
- set_fact:
app: "{{ mylist|first }}"
vars:
mylist: "{{ apps|selectattr('name', 'match', app_name)|list }}"
when: mylist|length > 0
Next option is json_query instead of selectattr
- set_fact:
app: "{{ mylist|first }}"
vars:
query: "[?name=='{{ app_name }}']"
mylist: "{{ apps|json_query(query) }}"
when: mylist|length > 0

Ansible get first element from list

Suppose I have the following vars_file:
mappings:
- primary: 1.1.1.1
secondary: 2.2.2.2
- primary: 12.12.12.12
secondary: 11.11.11.11
and hosts file
1.1.1.1
12.12.12.12
5.5.5.5
and the following playbook task
- name: Extract secondary from list
debug:
msg: "{{ (mappings | selectattr('primary', 'search', inventory_hostname) | list | first | default({'secondary':None})).secondary }}"
The current task works and will give empty string when no match are found, but I would like to know if there is a better way/cleaner way of doing it without passing a dictionary to the default constructor.
An option would be to use json_query
- debug:
msg: "{{ mappings | json_query(\"[?primary=='\" + inventory_hostname + \"'].secondary\") }}"
, but selectattr works too
- debug:
msg: "{{ mappings | selectattr('primary', 'equalto', inventory_hostname) | map(attribute='secondary') | list }}"

Resources