Ansible task to create user with looping and checks - ansible

I'm trying to create a task where I can set up users based in a group of environments and types of servers when I'm running a task for that specific env and server.
For example, let's say I'm running the playbook in the oracle servers for production, the task should only add the user1 and not user2.
So I've set this way the vars:
"internal_users": [
{
"username": user1,
"comment": "Name of the User",
"password": "$1$D3d32t4t53y5ytyttgtuudhkjfgt!fdfsdf.",
"env": ["prod","uat"],
"hosts": ["apache","oracle"]
},
{
"username": user2,
"comment": "Name of the User2",
"password": "$1$D3d32t4t53y5fdfvrgrt45234fdfsdf.",
"env": ["uat"],
"hosts": ["apache","oracle"]
}
]
I believe I can get the actual env and server that I'm running the playbook using the var vars['env'] for environment and inventory_hostname.
Then the task:
- name: Create Internal users
vars:
env: "{{ vars['env'] }}"
host: "{{ inventory_hostname }}"
user:
name: "{{ item.username }}"
comment: "{{ item.comment }}"
groups: wheel,internal_users
append: yes
shell: /bin/bash
password: "{{ item.password }}"
update_password: on_create
when: env in item.hosts
with_dict: "{{ internal_users }}"
It isn't working, could someone help with the logic to make it work?
Thank you.

Variable "internal_users" is a list not a dictionary. "loop" must be used instead of "with_dict". For example, the task running on "oracle"
- name: Create Internal users
debug:
msg:
- "{{ item.username }}"
- "{{ item.comment }}"
- "{{ item.password }}"
loop: "{{ internal_users }}"
when:
- env in item.env
- host in item.hosts
vars:
env: uat
host: "{{ inventory_hostname }}"
gives
"msg": [
"user1",
"Name of the User",
"$1$D3d32t4t53y5ytyttgtuudhkjfgt!fdfsdf."
]
"msg": [
"user2",
"Name of the User2",
"$1$D3d32t4t53y5fdfvrgrt45234fdfsdf."
]

Here is the final task working:
- name: Create Internal users
vars:
env: "{{ vars['env'] }}"
host: "{{ group_names[0] }}"
user:
name: "{{ item.username }}"
comment: "{{ item.comment }}"
groups: wheel,internal_users
append: yes
shell: /bin/bash
password: "{{ item.password }}"
update_password: on_create
when:
- env in item.env
- host in item.hosts
loop: "{{ internal_users }}"
Thanks to Vladimir Botka for the help.

Related

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 ansible create a dict using the common values from a list and a dict?

In ansible, the vmware_guest_info module will give us a list of the tags on a vm, but does not include any information about those tags:
"tags": [
"10.16.3",
"dicky",
"develop"
],
The vmware_tag_info module gives us a dict withinfo on those tags, including description and Id, but NOT the tags name:
"10.16.3": {
"tag_category_id": "urn:vmomi:InventoryServiceCategory:6eb9d643-8fa3-42a1-8b50-78a1c6e99867:GLOBAL",
"tag_description": "10.16.3",
"tag_id": "urn:vmomi:InventoryServiceTag:ca46ab80-be91-4c3a-8f9f-019d163dd954:GLOBAL",
"tag_used_by": []
},
The vmware_category_info module gives us a list that includes ID and name of a tag.
"tag_category_info": [
{
"category_associable_types": [],
"category_cardinality": "SINGLE",
"category_description": "nodeVersion",
"category_id": "urn:vmomi:InventoryServiceCategory:6eb9d643-8fa3-42a1-8b50-78a1c6e99867:GLOBAL",
"category_name": "nodeVersion",
"category_used_by": []
},
]
So it seems I need to combine the output of three different lists to get the tag value, tag name and tag ID.
I really hope that someone has already done this. If not, can anyone shed some light on how to iterate over the output of vmware_tag_info and vmware_category_info, and find when tag_category_id matches category_id?
How about this?
---
- name: Example playbook
hosts: localhost
gather_facts: no
vars:
vcenter_hostname: change me
vcenter_username: administrator#vsphere.local
vcenter_password: change me
dc1: change me
vm_name: change me
tasks:
- name: Gather tags information
vmware_tag_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
register: tags_info_result
- name: Gather tags category information
vmware_category_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
register: tags_category_info_result
- name: Set tags_information variable
set_fact:
tags_information: >-
{{ tags_information | default({})
| combine({
item.key: tags_category_info_result.tag_category_info
| selectattr('category_id', '==', item.value.tag_category_id)
| list
| first
| combine(item.value)
| combine({'tag_name': item.key})
})
}}
with_dict: "{{ tags_info_result.tag_facts }}"
- name: Gather VM information
vmware_guest_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
datacenter: "{{ dc1 }}"
name: "{{ vm_name }}"
tags: true
register: vm_info_result
- name: Display VM tags information
debug:
msg:
- "tags information about {{ vm_name }}"
- "{{ tags_information[item] }}"
loop: "{{ vm_info_result.instance.tags }}"
when:
- "'tags' in vm_info_result.instance"

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 check if inventory_hostname is in list

I am trying to check if inventory_hostname is in a list into an imported variable.
vars/users.yml file:
---
users:
- username: user1
comment: "User 1"
group: admin
password: "sha password"
keys:
active:
- "ssh-rsa etc"
admin: yes
- username: user2
comment: "User 2"
group: users
groups: deployer
keys:
active:
- "ssh-rsa etc"
hosts:
user:
- host1
- host2
deployer:
- host3
I want to execute a task only if inventory_hostname is into any of hosts lists (user, deployer, others ...).
I tried this:
- name: Create users
user:
name: "{{ item.username }}"
comment: "{{ item.comment | default('User {{item.username}}') }}"
password: "{{ item.password | default('!') }}"
state: "{{ item.state | default('present') }}"
shell: "{{ item.shell | default('/bin/bash') }}"
group: "{{ item.group | default('users') }}"
with_items: '{{ users }}'
when: item.username is defined and ((item.admin is defined and item.admin == True) or (item.hosts is defined and item.hosts.user is defined and inventory_hostname in item.hosts.user)
It works for user1 (which have admin enabled) but not for user2 (if this play is executed on host1 which is included into user2's hosts.user list).
Well .. I tried your code snippet and it works well for both users. Only thing which can make it fail is that hostnames in item.host.user are not matching the inventory_hostname. You can try to debug the inventory_hostname before this task to see what are the inventory hostnames read by ansible and whether you have specified them correctly in item.host.user list.
- debug: var=inventory_hostname

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