Iterate with Ansible with_dict over list of a dictionaries - ansible

I am stuck in iterating over the list of a dictionary. Sample vars.yml and the minimal playbook is bellow.
---
- hosts: localhost
connection: local
gather_facts: false
become: false
vars:
csvfile: "{{ lookup('file', 'vars/users.csv') }}"
tasks:
- name: Convert CSV to YAML
template:
src: "./users_csv.j2"
dest: "vars/users.yml"
run_once: true
- name: Include users from users.yml to users variable
include_vars:
file: vars/users.yml
name: users
- debug:
msg: "{{ users.value }}"
with_dict:
- "{{ users }}"
My Jinja2 template produces a list of dictionaries in YAML format as below:
--
users:
- username: Auser1
group: Admin
- username: Auser2
group: Admin
- username: Auser3
group: User
Anyhow, when I am iterating the dictionary, I am not able to get for example a username or group.
Most far I got is getting a fatal error message saying:
fatal: [localhost]: FAILED! => {"msg": "with_dict expects a dict"}
I know how to iterate over the list, but I don't have an idea why it fails here.

The users is not a dictionary, its a list variable of dictionaries.
if you want to parse this variable in a loop, you can use:
- debug:
msg: "username: {{ item.username }}, group: {{ item.group }}"
with_items:
- "{{ users.users }}"
hope it helps
UPDATE
i noticed now that when including the var file, you pass the name: users instruction as well. this cause all the variables of the file to be placed under the users variable. So to refer to the users list which is defined in the variable file, you need to use users.users.
updated the with_items to:
with_items:
- "{{ users.users }}"

Related

Ansible nested variable in playbook

I'm trying to use getent to lookup a user's home directory and then apply that information to a copy job. I know we can't do nested variables, but I'm just stuck. I have been playing with the lookup('var's...) syntax and various ways to pull a value out of a dict. In this particular case I do know the user's home directory, but now it's more an exercise in figuring this out. ANSIBLE_USER is defined as ansible
My playbook is:
#lookup ansible user's home directory
#drops the value into a getent_passwd variable
- hosts: all
become_user: root
become: true
tasks:
- name: get info
getent:
key: "{{ ANSIBLE_USER }}"
database: passwd
- debug:
var: getent_passwd.{{ ANSIBLE_USER }}.4
- set_fact:
ANSIBLE_HOME: "{{ getent_passwd['ansible'][4] }}"
- hosts: all
become_user: root
become: true
tasks:
- name: copy iptables files
copy:
src: "iptables/{{ IPTABLESCONFSRC }}/iptables.sh"
dest: "{{ ANSIBLE_HOME }}/temp/iptables.sh"
This works because I'm manually defining the 'ansible' string in the ANSIBLE_HOME line. However, what I'm functionally trying to accomplish is:
ANSIBLE_HOME: "{{ getent_passwd['{{ ANSIBLE_HOME }}'][4] }}"
The best I can get is a undefined variable error because I end up looking for: getent_passwd[ansible][4] or getent_passwd.ansible.4 and that doesn't exist via:
ANSIBLE_HOME: "{{ lookup('vars', 'getent_passwd.' + ANSIBLE_USER + '.4') }}"
or
ANSIBLE_HOME: "{{ lookup('vars', 'getent_passwd[' + ANSIBLE_USER + '][4]') }}"
The debug output shows:
ok: [HOSTNAME] => {
"getent_passwd.ansible.4": "/home/ansible"
}
This seems to work because the debug var is already considered Jinja, so it's effectively double nesting for you.
Oh geeze. Guess this was easier then what I was thinking. This:
ANSIBLE_HOME: "{{ getent_passwd[ANSIBLE_USER][4] }}"
works fine.

set path when file exists in Ansible yml code

I'm trying to set a var only when a file exists, here is one of my attempts
---
- hosts: all
tasks:
- stat:
path: '{{ srch_path_new }}/bin/run'
register: result
- vars: srch_path="{{ srch_path_new }}"
when: result.stat.exists
This also didn't work
- vars: srch_path:"{{ srch_path_new }}"
The task you are looking for is called set_fact: and is the mechanism ansible uses to declare arbitrary "host variables", sometimes called "hostvars", or (also confusingly) "facts"
The syntax would be:
- set_fact:
srch_path: "{{ srch_path_new }}"
when: result.stat.exists
Also, while vars: is a legal keyword on a Task, its syntax is the same as set_fact: (or the vars: on the playbook): a yaml dictionary, not a key:value pair as you had. For example:
- debug:
msg: hello, {{ friend }}
vars:
friend: Jane Doe
and be aware that vars: on a task exist only for that task

Using Ansible loop to create multiple users: Undefined error

I am using the following ansible code to create multiple unix user accounts
---
- hosts: test
become: true
tasks:
- name: more complex items to add several users
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
groups: "{{ item.groups }}"
state: present
with_items: "{{ user_details }}"
I am storing the user information by using a separate a variable file as below
`cat /etc/ansible/vars.yml
---
user_details:
- { name: testuser1, uid: 1002, groups: "admin, logs" }
- { name: testuser2, uid: 1003, groups: logs: }`
To execute above playbook , I tried with both the commands below
sudo ansible-playbook /etc/ansible/userloop.yml -e /etc/ansible/vars.yml
sudo ansible-playbook /etc/ansible/userloop.yml
but both commands are failing with below error
fatal: [host-003]: FAILED! => {"msg": "'user_details' is undefined"}
fatal: [host-004]: FAILED! => {"msg": "'user_details' is undefined"}
How to resolve the issue ? I want to maintain a separate variable file to store the user information rather then putting them in the same playbook file .
You can also refer the multiple variable files in playbooks like below
- hosts: all
become: true
vars_files:
- /etc/ansible/vars.yml
tasks:
- name: more complex items to add several users
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
groups: "{{ item.groups }}"
state: present
with_items: "{{ user_details }}"
The type of variables is in the column "Parameter" of the module user. Try the structure of the data below
user_details:
- {name: 'testuser1', uid: 1002, groups: ['admin', 'logs']}
- {name: 'testuser2', uid: 1003, groups: ['logs']}
You are missing # while passing the vars.yml. Hence, the ansible is not reading the file. Try the below command. It works for me.
sudo ansible-playbook /etc/ansible/userloop.yml -e #/etc/ansible/vars.yml

how can i loop over a variable that might have single value?

I'm writing a playbook and want to loop a role over a variable that gets its value from the user. however that value might not always be a list of items, it might be a single value and whenever that happens it throws an error.
My Task:
- name: task name
include role:
name: role name
vars:
cluster_name: '{{ item }}'
loop: "{{ list_or_not }}"
loop_control:
loop_var: item
error:
...Invalid data passed to 'loop', it requires a list...
Have you tried the: "| list" filter?
Sorry cannot test at the moment.
You could test if the variable is a string, and if so, transform it into a single-item list. Something like this:
---
- hosts: localhost
gather_facts: false
tasks:
- set_fact:
list_or_not: ["{{ list_or_not }}"]
when: list_or_not is string
- debug:
msg: "{{ item }}"
loop: "{{ list_or_not }}"

Ansible: variable in variable / loop or iterating through item

Here is a simple vars file we have to debug
./roles/test/vars/{{ ansible_distribution|lower }}/apt-packages.yml
packages:
required:
- htop
# - aptitude
package:
htop:
allow_unauthenticated: no
autoclean: no
autoremove: no
cache_valid_time: 0
# default_release:
force: no
force_apt_get: no
install_recommends: yes
only_upgrade: no
purge: no
state: latest
update_cache: yes
upgrade: no
Here is a simple task to debug
./roles/test/tasks/main.yml
- name: "Register variable"
include_vars:
#dir: vars/ubuntu
file: "vars/{{ ansible_distribution|lower }}/apt-packages.yml"
name: apt_install
- name: "This a test"
apt:
name: "{{item}}"
cache_valid_time: "{{ apt_install.package[item].cache_valid_time }}"
state: "{{ apt_install.package[item].state }}"
update_cache: "{{ apt_install.package[item].update_cache }}"
with_items: "{{ apt_install.packages.required }}"
./roles/test-playbook.yml
- name: "playbook test"
hosts: localhost
roles:
- role: test
become: true
become_user: root
become_method: sudo
using following answer stackoverflow.com/questions/29276198 we are trying to loop through items and get item related values.
Tasks is looping well through items but it is impossible to retrieve related variable with [item] syntax or any other one we have tested.
We have always the same error
fatal: [localhost]: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: dict object has no element [u'htop']
But calling directly the variable works
- name: "echo variable test"
debug:
msg: "{{ apt_install.package.htop.allow_unauthenticated }}"
What is the right syntax to get current loop value of a variable and use it inside another variable to retrieve related value ... (inside the same task) ?
So far it's we who are going around in circles without end !
Kind Regards
This is fully working using a loop / loop_control instead of with_items
- name: "This a test"
apt:
name: "{{item}}"
cache_valid_time: "{{ apt_install.package[item].cache_valid_time }}"
state: "{{ apt_install.package[item].state }}"
update_cache: "{{ apt_install.package[item].update_cache }}"
loop: "{{ apt_install.packages.required|flatten(levels=1) }}"
loop_control:
index_var: index
So as it we can define different settings for each package and for each distrib.
Now I can export too settings for each package in different var files.
Learning is hard :)

Resources