I have written a role to create users. Now I wanted to modify the role to also give the option to remove users. What looked very simple in the beginning is now giving me constant headaches.
Here is my role:
---
- name: Dictionary playbook example
hosts: localhost
vars:
my_default_values:
users: &def
state: 'present'
force: 'yes'
remove: 'no'
my_values:
users:
- username: bla
<<: *def
state: absent
- username: bla2
<<: *def
tasks:
- debug: var=my_default_values
- debug: var=my_values
Here is the output:
PLAY [Dictionary playbook example] ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"my_default_values": {
"users": {
"force": "yes",
"remove": "no",
"state": "present"
}
}
}
TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"my_values": {
"users": [
{
"force": "yes",
"remove": "no",
"state": "absent",
"username": "bla"
},
{
"force": "yes",
"remove": "no",
"state": "present",
"username": "bla2"
}
]
}
}
PLAY RECAP ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0
rescued=0 ignored=0
This is working fine as long as I always define <<: *def
below every - username
in my role. But this is problematic if let's say I want to have the users
variables defined in an external file users.yml. I guess I would need some kind of loop?
In the end I want to use the role with Katello and so the users are defined inside of Katello. This is why I need to get this working in a more dynamic fashion.
CodePudding user response:
I can see two approaches:
If you plan on looping on those users and use the values of the dictionaries, then, just define the adequate default with the purposed default
filter:
roles/demo/tasks/main.yml
- debug:
msg:
user:
username: "{{ item.username }}"
state: "{{ item.state | default('present') }}"
force: "{{ item.force | default('yes') }}"
remove: "{{ item.remove | default('no') }}"
loop: "{{ users }}"
loop_control:
label: "{{ item.username }}"
Playbook:
- hosts: localhost
gather_facts: no
roles:
- name: demo
users:
- username: bla
state: absent
- username: bla2
Which will yield:
ok: [localhost] => (item=bla) =>
msg:
user:
force: 'yes'
remove: 'no'
state: absent
username: bla
ok: [localhost] => (item=bla2) =>
msg:
user:
force: 'yes'
remove: 'no'
state: present
username: bla2
If you really do need a list inside your role, recreate a new list, combining the the default value to the input given to the role, taking advantage of the fact that, when combining two dictionaries containing the same key, the values of the second dictionary will override the values of the first one:
roles/demo/tasks/main.yml
- set_fact:
users_with_default: >-
{{
users_with_default | default([])
[user_defaults | combine(item)]
}}
loop: "{{ users }}"
vars:
user_defaults:
state: 'present'
force: 'yes'
remove: 'no'
- debug:
var: users_with_default
Playbook:
- hosts: localhost
gather_facts: no
roles:
- name: demo
users:
- username: bla
state: absent
- username: bla2
Which will yield:
ok: [localhost] =>
users_with_default:
- force: 'yes'
remove: 'no'
state: absent
username: bla
- force: 'yes'
remove: 'no'
state: present
username: bla2
CodePudding user response:
Use module_defaults. It's not necessary to include state: present and remove: no because these are defaults. In the list my_users include only required parameter name and parameters with non-default values
- hosts: localhost
module_defaults:
ansible.builtin.user:
force: yes
vars:
my_users:
- name: foo
state: absent
- name: bar
In the task, by default omit optional parameters that you want to use
tasks:
- ansible.builtin.user:
name: "{{ item.name }}"
state: "{{ item.state|default(omit) }}"
force: "{{ item.force|default(omit) }}"
remove: "{{ item.remove|default(omit) }}"
loop: "{{ my_users }}"