Reputation: 21
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.
Upvotes: 2
Views: 1065
Reputation: 21
My role finally works. Maybe it is not the best solution, but it does work just fine for what I wanted to achieve:
---
# tasks file for securehost
- set_fact:
users_with_default: >-
{{
users_with_default | default([])
+ [user_defaults | combine(item)]
}}
loop: "{{ users }}"
vars:
user_defaults:
state: 'present'
force: 'yes'
remove: 'no'
- name: Allow 'wheel' group to have passwordless sudo
lineinfile:
dest: /etc/sudoers
state: present
regexp: '^%wheel'
line: '%wheel ALL=(ALL) NOPASSWD: ALL'
validate: 'visudo -cf %s'
- name: create base homedir /lhome with mode 775
file:
path: /lhome
state: directory
mode: "u=rwx,g=rx,o=rx"
- name: Add / Remove users
environment: umask=077
user: name={{ item.username }} uid={{ item.uid }} home="/lhome/{{ item.username }}" groups={{ item.groups }} state={{ item.state }} remove={{ item.remove }} force={{ item.force }}
with_items: "{{ users_with_default }}"
- name: change mode of all homedirs under /lhome to mode 0700
file:
path: "/lhome/{{ item.username }}"
state: directory
mode: 0700
with_items: "{{ users_with_default }}"
when:
- item.state != 'absent'
- name: Add authorized keys for users
authorized_key: user={{ item.username }} key="{{ item.key }}" exclusive={{ pubkey_exclusive }}
with_items: "{{ users_with_default }}"
when:
- item.state != 'absent'
- name: Change root password
user:
name: root
password: "{{ root_password_hash }}"
when: change_root_pw | bool
Again thanks a lot for your quick help on this.
Upvotes: 0
Reputation: 68254
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 }}"
Upvotes: 1
Reputation: 39304
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
Upvotes: 1