Using rabbitmq_user module with multiple permissions - ansible

I'm working on a RabbitMQ Ansible role and I have to write the task dedicated to users creation and configuration.
I defined the following variable in my playbook:
vars:
- rabbitmq_resources:
- type_resource: "vhost"
name: "/test"
node: "rabbit#host1ansible"
- type_resource: "vhost"
name: "/other"
node: "rabbit#host1ansible"
- type_resource: "user"
name: "rabbitmqadmin"
password: "rabbitmqadmin"
permissions:
- vhost: "/test"
configure_priv: ".*"
read_priv: ".*"
write_priv: ".*"
- vhost: "/other"
configure_priv: ".*"
read_priv: ".*"
write_priv: ".*"
tags: "administrator"
state: present
Then I want to write the task code dedicated to user processing:
- name: Creating RabbitMQ users
rabbitmq_user:
name: "{{ item.name|mandatory }}"
permissions:
vhost: "{{ item.value.vhost }}"
configure_priv: "{{ item.value.configure_priv }}"
read_priv: "{{ item.value.read_priv }}"
write_priv: "{{ item.value.write_priv }}"
tags: "{{ item.tags }}"
state: present
with_subelements:
- "{{ rabbitmq_resources }}"
- type_resource
- permissions
when: item.type_resource == "user"
The syntax above generates a message "subelements lookup expects a list of two or three items, the optional third item must be a dict with flags skip_missing".
I tested various loop syntaxes (with_items, with_subelements, with_dict) but I'm still unable to find the right one.
Help will be appreciated.

You approach isn't correct. You need to select only users from your dict and loop over it:
- name: Creating RabbitMQ users
rabbitmq_user:
name: "{{ item.name | mandatory }}"
permissions: "{{ item.permissions | default(omit) }}"
tags: "{{ item.tags | default(omit) }}"
state: present
with_items:
- "{{ rabbitmq_resources | selectattr('type_resource','equalto','user') | list }}"

Finally, I decided to replace the permissions list by a JSON array:
permissions: [{"vhost": "/test","configure_priv": ".*","read_priv": ".*","write_priv": ".*"},
{"vhost": "/other","configure_priv": ".*","read_priv": ".*","write_priv": ".*"}]
The task code is now:
- name: Creating RabbitMQ users
rabbitmq_user:
name: "{{ item.name|mandatory }}"
permissions: "{{ item.permissions }}"
tags: "{{ item.tags }}"
state: present
with_items: "{{ rabbitmq_resources }}"
when: item.type_resource == "user"
and it works fine (default values code not included).
Dan

Related

Ansible can't loop through subelements in variables files

I have the following user lists in separated files.
The idea behind this is to create multiple users and assign them to different user groups.
To make it easier, I shortened the list. I reality they include passwords and etc.
First variables file
userlist-os:
group: os
users:
- comment: Test User
username: ostest1
user_id: 9404
user_state: present
- comment: Test User
username: ostest2
user_id: 9405
user_state: present
Second variables file
userlist-zos:
group: zos
users:
- comment: Test User1
username: zostest1
user_id: 9204
user_state: present
- comment: Test User2
username: zostest2
user_id: 9205
user_state: present
This is how my playbook looks like:
- name: test
hosts: all
user: root
vars_files:
- [userlist-zos.yml]
- [userlist-os.yml]
tasks:
- name: Create user accounts
user:
name: "{{ item.users.username }}"
update_password: on_create
uid: "{{ item.users.user_id }}"
shell: /bin/bash
create_home: yes
group: "{{ item.group }}"
state: present
comment: "{{ item.users.comment }}"
when: item.users.user_state == 'present'
with_items:
- "{{ userlist-os }}"
- "{{ userlist-zos }}"
The problem is that I'm not getting into the sub elements of users(variable username is undefined), but when I set an index like this name: "{{ item.users.0.username }}" I do get the first username from each file.
Any help is appreciated.
In your scenario, item.users are lists of users, they are not dictionaries. Therefore they don't have username field, they have list elements which have that field instead. You were able to access to first element of the list with "item.users.0.username". What I suggest you to do is to access these nested variables with an include_task variable as follows:
main.yaml
- name: Trial
hosts: localhost
vars:
# YOUR VARS
tasks:
- name: Create user accounts
include_tasks: helper.yml
with_items:
- "{{ userlistos }}"
- "{{ userlistzos }}"
loop_control:
loop_var: list
helper.yml
- name: Create user accounts
user:
name: "{{ item.username }}"
update_password: on_create
uid: "{{ item.user_id }}"
shell: /bin/bash
create_home: yes
group: "{{ list.group }}"
state: present
comment: "{{ item.comment }}"
when: item.user_state == 'present'
with_items:
- "{{list.users}}"

Create only those users from a list that do not exist

I have a list of users and I only want to create those, which do not exist on the system.
This is what I have tried:
- name: Connection to Unix server
hosts: localhost
vars:
USER_ID_details:
- user_id: my_user1
groups: wheel
real_full_name: my_user_name1
affected_host: localhost
email_id: my_user1#ibm.com
- user_id: my_user2
groups: wheel
real_full_name: my_user_name2
affected_host: localhost
email_id: my_user2#ibm.com
tasks:
- name: check for the ID is present
#shell: "id {{ item.user_id }}"
shell: grep "{{ item.user_id }}" /etc/passwd | awk -F":" '{print $1}'
loop: "{{ USER_ID_details }}"
ignore_errors: true
register: id_check
- name: setting var
set_fact:
user_id_names1: "{{ user_id_names1|default([]) + [item.stdout] }}"
with_items: "{{ id_check.results }}"
when: item.stdout != ""
- debug: var=user_id_names1
- block:
- name: create Linux user as per specification
user:
name: "{{ item.user_id }}"
password: "{{ pass_reg.stdout_lines[0] | password_hash('sha512') }}"
group: "{{ group_name }}"
groups: "{{ item.groups }}"
comment: "{{ comment }}"
shell: "{{ user_shell }}"
#uid: "{{ uid_num.item }}"
home: "/home/{{ item.user_id}}"
loop: "{{ USER_ID_details }}"
when:
- os_type == "RedHat"
- "{{ item.user_id }} not {{id_check.results}}"
What could be the best way to check if user exists, and only add those, that don't exist on server. I'm trying to check the user_id_names1 list of ids generated against list dictionary USER_ID_details and filter the existing ones.
As Vladimir Botka stated on the comment, ansible does that already. Most modules (including the user module) will ensure that the state you specify will be present on the machine, after ansible ran.
For example, if you specify that a certain user exists on the system, it will after you ran the playbook. It will be created if it didn't exist before, but it will not be added, if it already existed.
The catch is, that ansible will try to create the state you specified, possibly changing your existing users.
For example, let's assume your user already exists, but has changed the default shell to /bin/zsh while in your playbook you specify, that it should have /bin/bash. In that case, ansible will change the default shell to /bin/bash whenever you run your playbook.
If you don't care about existing users being modified (or you are sure they never will be) you can just run the user module for all users every time, as users will not be added twice.
Otherwise you can do this to check if a user exists and only add it if it does not:
tasks:
- name: get list of existing users
getent:
database: passwd
- name: get list of existing usernames
set_fact:
existing_users: "{{ ansible_facts.getent_passwd.keys() | list }}"
- name: create Linux user as per specification
user:
name: "{{ item.user_id }}"
password: "{{ pass_reg.stdout_lines[0] | password_hash('sha512') }}"
group: "{{ group_name }}"
groups: "{{ item.groups }}"
comment: "{{ comment }}"
shell: "{{ user_shell }}"
home: "/home/{{ item.user_id}}"
loop: "{{ USER_ID_details }}"
when: item.user_id not in existing_users
Make sure to read the documentation of the user module and that you understand what all the options do.
For example, the password option will set the password of that user to the specified value. If the user changed his password, you will change it back every time you run the playbook. Set update_password: on_create to prevent that.
You are also setting the primary group of all users to the same value (in group_name). Make sure that is what you actually want to do.
#toydarian Used below method too when i didnot know about the getent option.
- name: check for the ID is present
#shell: "id {{ item.user_id }}"
shell: grep "{{ item.user_id }}" /etc/passwd | awk -F":" '{print $1}'
loop: "{{ USER_ID_details }}"
ignore_errors: true
register: id_check
- name: setting var
set_fact:
user_id_names1: "{{ user_id_names1|default([]) + [item.stdout] }}"
with_items: "{{ id_check.results }}"
when: item.stdout != ""
- debug: var=user_id_names1
- name: create Linux user as per specification
user:
name: "{{ item.user_id }}"
password: "{{ pass_reg.stdout_lines[0] | password_hash('sha512') }}"
group: "{{ group_name }}"
groups: "{{ item.groups }}"
comment: "{{ item.real_full_name }}"
shell: "{{ user_shell }}"
#uid: "{{ uid_num.item }}"
home: "/home/{{ item.user_id}}"
loop: "{{ USER_ID_details }}"
when:
- os_type == "RedHat"
- item.user_id not in user_id_names1

Can we have 2 with_items in ansible in a single task

Below is the condition
- name: Find the image
slurp:
src: "{{ IMAGE }}"
register: slurp_results
- name: Upload image
shell: |
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/TESTIMAGE
with_items: "{{ (slurp_results.content|b64decode).splitlines() }}"
The above code works.
But I would need "TESTIMAGE" also to be replaced as {{ item }} like below.
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/{{ item }}
How to define 2 with_items in a single shell task with 2 different slurp results
I believe you can by using the subelements module. Here is a link. Try going by this example:
- name: Setup MySQL users, given the mysql hosts and privs subkey lists
mysql_user:
name: "{{ item.0.name }}"
password: "{{ item.0.mysql.password }}"
host: "{{ item.1 }}"
priv: "{{ item.0.mysql.privs | join('/') }}"
with_subelements:
- "{{ users }}"
- mysql.hosts
Users is referred to as item.0 and hosts as item.1 and so on.

Looping in hostvars

I'm wondering if it is possible to perform a loop in the hostvars folder when using Ansible?
Here is what I've tried but haven't had success in making it work - or is it just not possible to do?
---
list_pool: 'list ltm pool {{ items }}'
with_items:
- 'abc123'
- 'def456'
I would use the "list_pool" variable in a playbook afterward:
- name: List pool
bigip_command:
server: "{{ some_server }}"
user: "{{ some_user }}"
password: "{{ some_password }}"
commands:
- "{{ list_pool }}"
validate_certs: no
delegate_to: localhost
Not sure what you mean when you say you want to loop over hostvars folder.
From what I can interpret from your tasks is: "You need to execute big-ip command list ltm <pool-name> for multiple pools in the list list_pool"
If that's what you're after, this should work:
- name: Set list_pool fact
set_fact:
list_pool: "{{ list_pool | default([]) + [item] }}"
with_items:
- 'abc123'
- 'def456'
- name: List pool
bigip_command:
server: "{{ some_server }}"
user: "{{ some_user }}"
password: "{{ some_password }}"
commands:
- "list ltm {{ item }}"
validate_certs: no
delegate_to: localhost
with_items: "{{ list_pool }}"
I got this working with the following solution:
hostvars file would look like this:
---
pre_checks:
checks:
pool:
- name: "pool_123"
- name: "pool_456"
...
And the play would look like this:
--output truncated---
- name: Fetch device host_vars
set_fact:
device_config: "{{ ((lookup('file','{{playbook_dir}}/host_vars/{{inventory_hostname}}.yml')) | from_yaml) }}"
- name: Check pool
bigip_command:
server: "{{ inventory_hostname }}"
user: "{{ remote_username }}"
password: "{{ remote_passwd }}"
commands:
- "list ltm pool {{ item }}"
validate_certs: no
with_items:
- "{{ device_config.pre_checks | json_query('checks.pool[*].name') }}"
delegate_to: localhost
when: "'active' in Active_LTM['stdout'][0]"
register: Pre_Checks
- name: Verify pool
debug: var=item
with_items: "{{ Pre_Checks.results | map(attribute='stdout_lines') | list }}"

Ansible - failed to lookup user

How can I solve problem with run ansible role below? If a user doesn't exist on the remote server, ansible gets me the error "Failed to lookup user test1: 'getpwnam(): name not found: test1". I need manage multiple users on multiple servers. Thanks
vars:
user_list:
- user: test1
state: present
path: /usr/local/test1/.ssh/authoried_keys
keys:
- "ssh-rsa test1"
- user: test2
state: absent
path: /home/test2/.ssh/authoried_keys
keys:
- "ssh-rsa test2"
tasks:
- name: Manage SSH-keys
authorized_key:
user: "{{ item.0.user }}"
key: "{{ item.1 }}"
path: "{{ item.0.path }}"
state: "{{ item.0.state }}"
with_subelements:
- '{{ user_list }}'
- keys
CentOS Linux 7, Ansible 2.4.2.0
Perhaps you could check the existing users through ansible's wrapper for getent?
It feels a bit simpler and you don't need to use the shell module:
tasks:
- name: Get existing users
getent:
database: passwd
- name: Disable expired users
user:
name: "{{ item.name }}"
shell: /sbin/nologin
with_items:
- "{{ users_removed }}"
when: item.name in getent_passwd.keys()
Note though that as #techraf points out, at production environments you should always aim at declaring and knowing beforehand which users should and shouldn't be present :)
I think, that I solved my problem.
tasks:
- name: Check for users
shell: cat /etc/passwd | cut -f1 -d":"
register: sshkeys_users
changed_when: False
- name: Manage SSH-keys
authorized_key:
user: "{{ item.0.user }}"
key: "{{ item.1 }}"
path: "{{ item.0.path }}"
state: "{{ item.0.state }}"
with_subelements:
- '{{ user_list }}'
- keys
when: sshkeys_users is defined and item.0.user in sshkeys_users.stdout_lines

Resources