Creating a loop in ansible Play book - Error - ansible

The Play book
---
- name: task 11
hosts: prod
vars_files:
- users_pass.yml
tasks:
- name: create group profs
group:
name: profs
state: present
- name: create users who have depart set as profs
user:
name: "{{ item.uname }}"
groups: "{{ item.department }}"
shell: /bin/bash
password: "{{ item.password | password_hash ('sha256') }}"
when: "'{{ item.department }}'=='profs'"
loop: " {{ users }} "
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#the users_pass.yml
---
users:
- uname: linda
password: password
department: profs
- uname: lisa
password: secret
department: profs
- uname: anna
password: geheim
department: students
when running the above mentioned playbook and adding this var file i get the following error:
fatal: [ansible5]: FAILED! => {
"msg": "Invalid data passed to 'loop', it requires a list, got this instead: [{'uname': 'linda', 'password': 'password', 'department': 'profs'}, {'uname': 'lisa', 'password': 'secret', 'department': 'profs'}, {'uname': 'anna', 'password': 'geheim', 'department': 'students'}] . Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."
}
I really don't understand, I am sure that my declaration of variable is correct. Any help, please?

I think the problem is due to the space within the quotes in variable to loop, i.e. " {{ users }} ". There might be another "hidden" issue in the when condition due to the use of Jinja delimiters {{.
Overall, it should work with changes as below:
- name: create users who have depart set as profs
user:
name: "{{ item.uname }}"
groups: "{{ item.department }}"
shell: /bin/bash
password: "{{ item.password|password_hash('sha256') }}"
when: item.department == "profs"
loop: "{{ users }}"

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}}"

How to get interpolated value of variable in Ansible / Jinja2

I'm trying to define Ansible variables this way:
user:
name: First Last
nick: '{{ vars["user"]["name"] | regex_replace("\W", "_") }}'
email: '{{ vars["user"]["nick"] }}#example.com'
And the result email is: "{{ vars[\"user\"][\"name\"] | regex_replace(\"\\W\", \"_\") }}#example.com.
I also tried to set email like this: {{ lookup("vars", "user.nick") }}#example.com or {{ lookup("vars", "user")["nick"] }}#example.com, and it says An unhandled exception occurred while running the lookup plugin 'vars'.
Is there a way to get resulting variable values as:
user:
name: First Last
nick: First_Last
email: First_Last#example.com
?
ansible 2.9.10,
python version = 3.8.5
It's not possible cross-reference keys in a dictionary. It's necessary to declare the variables outside the dictionary. For example, the playbook
- hosts: localhost
vars:
my_name: First Last
my_nick: "{{ my_name | regex_replace('\\W', '_') }}"
user:
name: "{{ my_name }}"
nick: "{{ my_nick }}"
email: "{{ my_nick }}#example.com"
tasks:
- debug:
var: user
gives (abridged)
user:
email: First_Last#example.com
name: First Last
nick: First_Last
A more flexible option is to create the variables in the loop. For example, the playbook
- hosts: localhost
vars:
users:
"First Last":
domain: example.com
tasks:
- debug:
msg:
- "name: {{ name }}"
- "nick: {{ nick }}"
- "email: {{ email }}"
loop: "{{ users|dict2items }}"
vars:
name: "{{ item.key }}"
nick: "{{ item.key|regex_replace('\\W', '_') }}"
email: "{{ nick ~ '#' ~ item.value.domain }}"
gives (abridged)
msg:
- 'name: First Last'
- 'nick: First_Last'
- 'email: First_Last#example.com'

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

Ansible manage ssh users with templates

I've got an ansible playbook for adding over 50 ssh-users for a packer ami build.
Here is how my /playbooks/roles/users/tasks/create_user.yml: looks like.
---
- name: "User {{ item.name }}"
user:
comment: "Add {{ item.name }} account"
name: "{{ item.name }}"
home: "/data/companies/project/{{ item.name }}"
state: present
uid: "{{ item.uid }}"
group: company
groups: company
shell: /sbin/nologin
state: present
generate_ssh_key: no
password: "{{ item.password }}"
- name: "Create home directory for {{ item.name }}"
file:
path: "/data/companies/project/{{ item.name }}"
state: directory
owner: "{{ item.name }}"
group: company
mode: 0700
Here's how /playbooks/roles/users/vars/main.yml file looks like
---
location: UK
users:
- name: user1
password: $6$8T8lH2vS$JKIdqkQmHUHR/s75RYMguPyHTisnNrXIPOjJ9IWxMHB4LY9PJX.3rgkmfLCWAHDi5VYZno2ntlYm7Kkdy0iAZ.
uid: 601
location: UK
- name: user2
password: $6$8T8lH2vS$JKIdqkQmHUHR/s75RYMguPyHTisnNrXIPOjJ9IWxMHB4LY9PJX.3rgkmfLCWAHDi5VYZno2ntlYm7Kkdy0iAZ
uid: 602
location: USA
Here's how my "/playbooks/roles/users/tasks/main.yml" looks like
---
- name: Create users based on location
include: create_users.yml
loop: "{{ users | selectattr('location', 'equalto', location) | list }}"
When the corresponding packer build has been run there are no errors but user1,user2 and their attributes are not getting created.
amazon-ebs: TASK [: Create users based on location] ***********************************
amazon-ebs:
amazon-ebs: PLAY RECAP *********************************************************************
amazon-ebs: default : ok=10 changed=7 unreachable=0 failed=0
amazon-ebs:
Please can someone help me understand as to why users are not getting created? Thanks
This is commonly approached through the use of variables and loops. Extract all the variable stuff out into a list variable, containing dictionaries of params, then use a loop to read through and apply. So based on your role approach:
Create a task file containing the two tasks to run - /playbooks/roles/users/tasks/create_user.yml:
---
- name: User "{{ item.name }}"
user:
comment "{{ item.name }} company account"
name: "{{ item.name }}"
home: "/data/company/{{ item.name }}"
state: present
uid: 666
group: company
groups: company
shell: /bin/bash
state: present
generate_ssh_key: no
password: "{{ item.password }}"
- name: "Create home directory for {{ item.name }}"
file:
path: "/data/company/{{ item.name }}"
state: directory
owner: "{{ item.name }}"
group: company
mode: 0700
Create a vars file containing your users - /playbooks/roles/users/vars/main.yml:
---
location: UK # This will be your default. It can be overridden in a variety of places
users:
- name: user1
password: <some password>
location: US
- name: user2
password: <some password>
location: UK
- name: user3
password: <some password>
location: UK
then in /playbooks/roles/users/tasks/main.yml:
---
- name: Create users based on location
include: create_user.yml
loop: "{{ users | selectattr('location', 'equalto', location) | list }}"
Hopefully most of it is self explanatory. Because you are using a role, by placing the users variable in /playbooks/roles/users/vars/main.yml, the variable is automatically made available. The users | selectattr('location', 'equalto', location) expression, takes the user variable and filters the list to only include objects where the 'location' element is equal to the value specified in the 'location' variable.
Variables
Loops
Roles
Some additional hints, you can use defaults too.
Also, in case you want to protect the encrypted passwords, you can store them in Ansible Vault:
users.yml example:
users:
- name: user1
uid: 666
group: group1
groups: [allusers]
comment: User One
home: /home/user1
shell: /bin/bash
passwords: "{{ vault_passwords }}"
The vault file you crypt with 'ansible-vault' command looks like (decrypted):
passwords:
- name: user1
password: $6$509875346RH.RAaVNW2iWtf/QJ4zUGaJiEyh.
playbook included tasks file example:
- name: Set up user accounts
user:
name: "{{ item.name }}"
create_home: '{{ item.createhome | default("no") }}'
uid: "{{ item.uid }}"
group: "{{ item.group }}"
state: '{{ item.state | default("present") }}'
comment: '{{ item.comment | default("Ansible created comment") }}'
home: "{{ item.home }}"
shell: '{{ item.shell | default("/bin/bash") }}'
with_items: "{{ users }}"
- name: Set up user account passwords
user:
name: "{{ item.name }}"
password: '{{ item.password | default("*") }}'
with_items: "{{ passwords }}"

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

Resources