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

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

Related

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'

VARIABLE IS NOT DEFINED when trying to register output in playbook

I'm trying to register a variable with the output to a query of a F5 pool and I'm getting this error:
"<type 'list'>": "VARIABLE IS NOT DEFINED!",
What is that I'm doing wrong?
Any help appreciated.
Thanks!
---
- name: GRAB F5 FACTS
hosts: f5
connection: local
gather_facts: no
tasks:
- name: Collect BIG-IP facts
bigip_device_facts:
gather_subset: ltm-pools
provider: "{{ prov }}"
register: bigip_device_facts
- name: FACTS OUTPUT
debug:
var: "{{ item.members | rejectattr('state', 'match', '^present$') | map(attribute='name') | list }}"
register: jkout
with_items: "{{ bigip_device_facts.ltm_pools }}"
when: item.full_path == "/Common/mypool"
- name: Set a variable
debug:
msg: "jkvar={{ jkout }}"
You are using the debug: module with the option var: and this expects a variable, not a jinja2 template.
So either change it to:
debug:
var: item.members
or
debug:
msg: "{{ item.members }}"
Like said by #dgw, the problem is with the var option of debug module.
https://docs.ansible.com/ansible/latest/modules/debug_module.html#parameters
This playbooks works:
- name: test rejectattr
hosts: localhost
gather_facts: no
vars:
members:
- { name: "one", state: "present" }
- { name: "two", state: "absent" }
- { name: "three", state: "present" }
tasks:
- name: FACTS OUTPUT
debug:
msg: "{{ members | rejectattr('state', 'match', '^present$') | map(attribute='name') | list }}"
Thanks for your responses. I'll investigate it further.
Apart from that, I think I've been able to solve it another way.
- name: FACTS OUTPUT
set_fact:
listado: "{{ item.members | rejectattr('state', 'match', '^present$') | map(attribute='name') | list }}"
with_items: "{{ bigip_device_facts.ltm_pools }}"
when: item.full_path == "/Common/mypool"
- debug: msg={{ listado }}
register: jkout
- name: Set a variable
debug:
msg: "jkvar={{ jkout }}"
Is that a right way to do it?
Thanks!!

How to create a 'null' default in Ansible

I want 'lucy' to follow the user module creators' default behaviour which is to create and use a group matching the user name 'lucy'. However for 'frank' I want the primary group to be an existing one; gid 1003. So my hash looks like this:
lucy:
comment: dog
frank:
comment: cat
group: 1003
And my task looks like this:
- name: Set up local unix user accounts
user:
name: "{{ item.key }}"
comment: "{{ item.value.comment }}"
group: "{{ item.value.group | default(undef) }}"
loop: "{{ users|dict2items }}"
This doesn't work, as undef is not recognised. Nor is anything else I can think of. 'null', 'None' etc. all fail. '' creates an empty string which is not right either. I can't find out how to do it.
Any ideas?
default(omit) is what you are looking for. For example,
- name: Set up local Unix user accounts
user:
name: "{{ item.key }}"
comment: "{{ item.value.comment }}"
group: "{{ item.value.group | default(omit) }}"
loop: "{{ users|dict2items }}"
Comments
Comment by Lucas Basquerotto: "... omit only works correctly when used directly in a module, it won't work in a set_fact ..."
A: You're wrong. For example, default(omit) works both in set_fact and in the module. The first item in the list defaults to false with the result "VARIABLE IS NOT DEFINED!". The second item defaults to omit. Omitted parameter get_checksum defaults to true with the checksum in the results
shell> cat pb.yml
- hosts: localhost
tasks:
- set_fact:
test:
- "{{ gchk|default(false) }}"
- "{{ gchk|default(omit) }}"
- stat:
path: /etc/passwd
get_checksum: "{{ item }}"
loop: "{{ test }}"
register: result
- debug:
var: item.stat.checksum
loop: "{{ result.results }}"
gives
shell> ansible-playbook pb.yml | grep item.stat.checksum
item.stat.checksum: VARIABLE IS NOT DEFINED!
item.stat.checksum: 7c73e9f589ca1f0a1372aa4cd6944feec459c4a8
In addition to this, default(omit) works as expected also in some expressions. For example
- debug:
msg: "{{ {'a': item}|combine({'b': true}) }}"
loop: "{{ test }}"
gives
msg:
a: false
b: true
msg:
b: true
See the results without default values
shell> ansible-playbook pb.yml -e "gchk={{ true|bool }}"

Adding field to dict items

Consider the following play. What I am trying to do is add a field, tmp_path which is basically the key and revision appended together to each element in the scripts dict.
---
- hosts: localhost
connection: local
gather_facts: no
vars:
scripts:
a.pl:
revision: 123
b.pl:
revision: 456
tasks:
- with_dict: "{{ scripts }}"
debug:
msg: "{{ item.key }}_{{ item.value.revision }}"
# - with_items: "{{ scripts }}"
# set_fact: {{item.value.tmp_path}}="{{item.key}}_{{item.value.revision}}"
# - with_items: "{{ scripts }}"
# debug:
# msg: "{{ item.value.tmp_path }}"
...
Obviously the commented code doesn't work, any idea how I can get this working? Is it possible to alter the scripts dict directly, or should I somehow be creating a new dict to reference instead?
By the way welcome to correct the terminology for what I am trying to do.
OK, I think I got a solution (below), at least to let me move forwards with this. Disadvantages are it has removed the structure of my dict and also seems a bit redundant having to redefine all the fields and use a new variable, If anyone can provide a better solution I will accept that instead.
---
- hosts: localhost
connection: local
gather_facts: no
vars:
scripts:
a.pl:
revision: 123
b.pl:
revision: 456
tasks:
- with_dict: "{{ scripts }}"
debug:
msg: "{{ item.key }}_{{ item.value.revision }}"
- with_dict: "{{ scripts }}"
set_fact:
new_scripts: "{{ (new_scripts | default([])) + [ {'name': item.key, 'revision': item.value.revision, 'tmp_path': item.key ~ '_' ~ item.value.revision}] }}"
# - debug:
# var: x
# - with_dict: "{{ scripts }}"
- with_items: "{{ new_scripts }}"
debug:
msg: "{{ item.tmp_path }}"
...
BTW credit to the following question which pointed me in the right direction:
Using Ansible set_fact to create a dictionary from register results

Conditionally joining two lists or taking the first and saving it in a variable

I have a role for a vpn server and some of them should be accessible to everyone, others only to admins.
Users and admins do not intersect, I want to use the variable only_admins that is True or False to toggle between these two.
I came up with this piece of configuration:
- name: Set vpn_users variable
set_fact: "vpn_users={{ users + admins}}"
when: ! only_admins
- name: Set vpn_users variable
set_fact: "vpn_users={{ admins }}"
when: only_admins
# works
- debug: "var={{ item }}"
with_items: users
# works
- debug: "var={{ item }}"
with_items: admins
# does not work
- debug: "var={{ item }}"
with_items: vpn_users
Any tips to get this working? Or other approaches?
Your syntax for set_fact is not good as per the documentation (http://docs.ansible.com/set_fact_module.html). You also use a lot of unnecessary quoting.
Here is a self-contained example that works fine :
---
- hosts: localhost
vars:
users:
- ua
- ub
admins:
- aa
- ab
tasks:
- debug: var=only_admins
- name: Set vpn_users variable to admins and users
set_fact:
vpn_users: "{{ users + admins }}"
when: not only_admins
- name: Set vpn_users variable to admins only
set_fact:
vpn_users: "{{ admins }}"
when: only_admins
- debug: var=item
with_items: users
- debug: var=item
with_items: admins
- debug: var=item
with_items: vpn_users
Try it with :
ansible-playbook test.yml --extra-vars '{ "only_admins": false }'
or :
ansible-playbook test.yml --extra-vars '{ "only_admins": true }'
Note that for debugging, you can print whatever var you'd like with the debug module. So instead of doing :
- debug: var=item
with_items: vpn_users
you just can do :
- debug: var=vpn_users
(ditto for the two preceding tasks).
This seems to work, didn't find out why the set_fact didn't work though:
- debug: "var={{ item }}"
with_items: "{{ admins if only_admins else (admins + users) }}"

Resources