Installing ohmyzsh for multiple users using ansible? - linux

I want to install ohmyzsh for a list of users using gantsign.oh-my-zsh role which is available in ansible galaxy gantsign.oh-my-zsh so i have to run this role for users here is my playbook
- name: setup ohmyzsh for users
hosts: all
vars:
userlist:
- username: "viktor"
password: "viktor123"
become: yes
roles:
- role: gantsign.oh-my-zsh
users:
- username: "{{ item.username }}"
oh_my_zsh:
theme: gnzh
plugins:
- git
- zsh-syntax-highlighting
- zsh-autosuggestions
- docker
loop: "{{ userlist }}"
tasks:
but I got this error after running playbook
fatal: [worker1]: FAILED! => {"msg": "[{'username': '{{ item.username }}', 'oh_my_zsh': {'theme': 'gnzh', 'plugins': ['git', 'zsh-syntax-highlighting', 'zsh-autosuggestions', 'docker']}, 'loop': '{{ userlist }}'}]: 'item' is undefined"}
fatal: [worker2]: FAILED! => {"msg": "[{'username': '{{ item.username }}', 'oh_my_zsh': {'theme': 'gnzh', 'plugins': ['git', 'zsh-syntax-highlighting', 'zsh-autosuggestions', 'docker']}, 'loop': '{{ userlist }}'}]: 'item' is undefined"}
fatal: [registry1]: FAILED! => {"msg": "[{'username': '{{ item.username }}', 'oh_my_zsh': {'theme': 'gnzh', 'plugins': ['git', 'zsh-syntax-highlighting', 'zsh-autosuggestions', 'docker']}, 'loop': '{{ userlist }}'}]: 'item' is undefined"}

You can't use loops inside the task level roles: keywords. Use include_role inside your tasks instead (untested example):
- name: setup ohmyzsh for users
hosts: all
vars:
userlist:
- username: "viktor"
password: "viktor123"
become: yes
tasks:
- name: run role for each user
include_role:
name: gantsign.oh-my-zsh
vars:
users:
- username: "{{ item.username }}"
oh_my_zsh:
theme: gnzh
plugins:
- git
- zsh-syntax-highlighting
- zsh-autosuggestions
- docker
loop: "{{ userlist }}"

Answer for the generic looping over ansible roles using include_role.
As for the specific problem, the role expects its list of users as a role variable, so you do not need to loop over the role at all, just pass all users to the role itself, as the oh-my-zsh role docs suggest.
- hosts: all
vars:
oh_my_zsh_defaults:
theme: gnzh
plugins:
- git
- zsh-syntax-highlighting
- zsh-autosuggestions
- docker
roles:
- role: gantsign.oh-my-zsh
users:
- username: "viktor"
oh_my_zsh: oh_my_zsh_defaults
- username: "otheruser"
oh_my_zsh: oh_my_zsh_defaults

Related

How to pass data from text file to ansible playbook?

I want to have text file which contains name and password
name: "Peter", "Joe", "Mark"
password: "smith", "biden", "garyy"
And I have playbook like this
---
- hosts: myhosts
become: yes
remote_user: root1
become_user: root
vars_files:
- vars.yml
vars:
ansible_ssh_private_key_file: "{{key}}"
tasks:
- name: Create users
user: name="{{item.name}}" shell=/bin/bash home="/srv/{{item.name}}" groups=root generate_ssh_key=yes ssh_key_bits=2048
loop: "{{ lookup('file', 'userspasswd.txt', wantList=True)| list }}"
- name: Set password to users
shell: echo "{{item.name}}:{{item.password}}" | sudo chpasswd
no_log: True
loop: "{{ lookup('file', 'userspasswd.txt', wantList=True)| list }}"
I am getting error like this
fatal: [xxx.xxx.xxx.xxx]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'name'\n\nThe error appears to be in '/home/root1/Documents/ansiblekernel/main.yml': line 12, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Create users\n ^ here\n"}
Is there any correct way of doing this? Cause I am new to this?
Given the file
shell> cat userspasswd.txt
name: "Peter", "Joe", "Mark"
password: "smith", "biden", "garyy"
Neither wantList=True nor the filter list will help you to parse the file because it's not a valid YAML. If you can't change the structure of the file you'll have to parse it on your own.
Declare the variables
userspasswd_lines: "{{ lookup('file', 'userspasswd.txt').splitlines() }}"
userspasswd_values: "{{ userspasswd_lines|
map('split', ':')|
map('last')|
map('regex_replace', '\"', '')|
map('split', ',')|
map('map', 'trim')|
list }}"
userspasswd_dict: "{{ dict(userspasswd_values.0|
zip(userspasswd_values.1)) }}"
give
userspasswd_lines:
- 'name: "Peter", "Joe", "Mark"'
- 'password: "smith", "biden", "garyy"'
userspasswd_values:
- - Peter
- Joe
- Mark
- - smith
- biden
- garyy
userspasswd_dict:
Joe: biden
Mark: garyy
Peter: smith
Iterate the dictionary. Test it
- name: Create users
debug:
msg: |
name: {{ item }}
shell: /bin/bash
home: /srv/{{ item }}
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
loop: "{{ userspasswd_dict.keys()|list }}"
- name: Set password to users
debug:
msg: 'echo "{{ item.key }}:{{ item.value}}" | sudo chpasswd'
loop: "{{ userspasswd_dict|dict2items }}"
gives
TASK [Create users] **************************************************************************
ok: [test_11] => (item=Peter) =>
msg: |-
name: Peter
shell: /bin/bash
home: /srv/Peter
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
ok: [test_11] => (item=Joe) =>
msg: |-
name: Joe
shell: /bin/bash
home: /srv/Joe
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
ok: [test_11] => (item=Mark) =>
msg: |-
name: Mark
shell: /bin/bash
home: /srv/Mark
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
TASK [Set password to users] *****************************************************************
ok: [test_11] => (item={'key': 'Peter', 'value': 'smith'}) =>
msg: echo "Peter:smith" | sudo chpasswd
ok: [test_11] => (item={'key': 'Joe', 'value': 'biden'}) =>
msg: echo "Joe:biden" | sudo chpasswd
ok: [test_11] => (item={'key': 'Mark', 'value': 'garyy'}) =>
msg: echo "Mark:garyy" | sudo chpasswd
Example of a complete playbook for testing
- hosts: myhosts
vars:
userspasswd_lines: "{{ lookup('file', 'userspasswd.txt').splitlines() }}"
userspasswd_values: "{{ userspasswd_lines|
map('split', ':')|
map('last')|
map('regex_replace', '\"', '')|
map('split', ',')|
map('map', 'trim')|
list }}"
userspasswd_dict: "{{ dict(userspasswd_values.0|
zip(userspasswd_values.1)) }}"
tasks:
- block:
- debug:
var: userspasswd_lines
- debug:
var: userspasswd_values
- debug:
var: userspasswd_dict
run_once: true
- name: Create users
debug:
msg: |
name: {{ item }}
shell: /bin/bash
home: /srv/{{ item }}
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
loop: "{{ userspasswd_dict.keys()|list }}"
- name: Set password to users
debug:
msg: 'echo "{{ item.key }}:{{ item.value}}" | sudo chpasswd'
loop: "{{ userspasswd_dict|dict2items }}"- hosts: myhosts
vars:
userspasswd_lines: "{{ lookup('file', 'userspasswd.txt').splitlines() }}"
userspasswd_values: "{{ userspasswd_lines|
map('split', ':')|
map('last')|
map('regex_replace', '\"', '')|
map('split', ',')|
map('map', 'trim')|
list }}"
userspasswd_dict: "{{ dict(userspasswd_values.0|
zip(userspasswd_values.1)) }}"
tasks:
- block:
- debug:
var: userspasswd_lines
- debug:
var: userspasswd_values
- debug:
var: userspasswd_dict
run_once: true
- name: Create users
debug:
msg: |
name: {{ item }}
shell: /bin/bash
home: /srv/{{ item }}
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
loop: "{{ userspasswd_dict.keys()|list }}"
- name: Set password to users
debug:
msg: 'echo "{{ item.key }}:{{ item.value}}" | sudo chpasswd'
loop: "{{ userspasswd_dict|dict2items }}"

Ansible playbook loop with with_items

I have to update sudoers.d multiple user files with few lines/commands using ansible playbook
users.yml
user1:
- Line1111
- Line2222
- Line3333
user2:
- Line4444
- Line5555
- Line6666
main.yml
- hosts: "{{ host_group }}"
vars_files:
- ../users.yml
tasks:
- name: Add user "user1" to sudoers.d
lineinfile:
path: /etc/sudoers.d/user1
line: '{{ item }}'
state: present
mode: 0440
create: yes
validate: 'visudo -cf %s'
with_items:
- "{{ user1 }}"
The above one is working only for user1..
If I want to also include user2 --> How to change the file name : path: /etc/sudoers.d/user1
I tried below and its not working :
Passing below users as variable to main.yml while running
users:
- "user1"
- "user2"
- name: Add user "{{users}}" to sudoers.d
lineinfile:
path: /etc/sudoers.d/{{users}}
line: '{{ item }}'
state: present
mode: 0440
create: yes
validate: 'visudo -cf %s'
with_items:
- "{{ users }}"
So, basically I want to pass in users to a variable {{users}} as user1 and user2 and wanted to use the lines for each user from users.yml and add it to respective user files (/etc/sudoers.d/user1 and /etc/sudoers.d/user2).
So /etc/sudoers.d/user1 should look like
Line1111
Line2222
Line3333
and /etc/sudoers.d/user2 should look like
Line4444
Line5555
Line6666
Try to add quotes:
users:
- "user1"
- "user2"
- name: "Add user {{users}} to sudoers.d"
lineinfile:
path: "/etc/sudoers.d/{{users}}"
line: "{{ item }}"
state: present
mode: 0440
create: yes
validate: 'visudo -cf %s'
with_items:
- "{{ users }}"
As per Ansible Documentation on Using Variables:
YAML syntax requires that if you start a value with {{ foo }} you quote the whole line, since it wants to be sure you aren’t trying to start a YAML dictionary. This is covered on the YAML Syntax documentation.
This won’t work:
- hosts: app_servers
vars:
app_path: {{ base_path }}/22
Do it like this and you’ll be fine:
- hosts: app_servers
vars:
app_path: "{{ base_path }}/22"
cat users.yml
---
users:
- user1:
filename: user1sudoers
args:
- Line1111
- Line2222
- Line3333
- user2:
filename: user2sudoers
args:
- Line4444
- Line5555
- Line6666
I use template here, instead of lineinfile
---
cat sudoers.j2
{% if item.args is defined and item.args %}
{% for arg in item.args %}
{{ arg }}
{% endfor %}
{% endif %}
the task content
---
- hosts: localhost
vars_files: ./users.yml
tasks:
- name: sync sudoers.j2 to localhost
template:
src: sudoers.j2
dest: "/tmp/{{ item.filename }}"
loop: "{{ users_list }}"
when: "users_list is defined and users_list"
after run the task.yml, generate two files under /tmp directory.
cat /tmp/user1sudoers
Line1111
Line2222
Line3333
cat /tmp/user2sudoers
Line4444
Line5555
Line6666

Ansible to create users and assign them to a particular group based on vars

Gurus, I'm learning ansible and trying to understand how the variables can be used, i have come across writing below playbook but i'm not understanding How to assign a particular group to particular users based on the Variables i have defined in the play for both Users and Groups under vars
I have below play where i want to create Users anika and rigved and want to assign them to a docker group while ayush and eshant should have the test group assigned.
I'm not getting the idea to achieve this so far. However as of now while running it creates users and assigns both group to all users.
$ cat nested_playbook-1.yml
---
- name: testing nested play
hosts: localhost
vars:
users:
- anika
- rigved
- ayush
- eshant
grps:
- docker
- test
tasks:
- name: make users members of groups
user:
name: "{{ item[0] }}"
state: present
groups: "{{ item[0] }}"
with_nested:
- "{{ users }}"
- "{{ grps }}"
The way you've structured your data doesn't show any relationship between users and groups. One option would be to structure it like this:
grps:
- name: docker
users:
- anika
- rigved
- name: test
users:
- ayush
- eshant
With this structure, you can loop over grps using the subelements filter, like this:
---
- name: testing nested play
gather_facts: false
hosts: localhost
vars:
grps:
- name: docker
users:
- anika
- rigved
- name: test
users:
- ayush
- eshant
tasks:
- debug:
msg:
user:
name: "{{ item.1 }}"
state: present
groups: "{{ item.0.name }}"
loop: "{{ grps|subelements('users') }}"
loop_control:
label: "{{ item.1 }}"
The subelements filter is a way of creating a "nested loop": it iterates over each member of the 'users' key for each group in grps. During each loop iteration, item is a 2-tuple in which the first item is the corresponding element from grps and the second item iterates over the users key for that element. In other words, you end up iterating over this list:
[{'name': 'docker', 'users': ['anika', 'rigved']}, anika}
[{'name': 'docker', 'users': ['anika', 'rigved']}, rigved}
[{'name': 'test', 'users': ['ayush', 'eshant']}, ayush}
[{'name': 'test', 'users': ['ayush', 'eshant']}, eshant}
So within the loop, item.0 refers to the group entry itself (and thus item.0.name is the group name), and item.1 refers to the user.
PLAY [testing nested play] *******************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] => (item=anika) => {
"msg": {
"user": {
"groups": "docker",
"name": "anika",
"state": "present"
}
}
}
ok: [localhost] => (item=rigved) => {
"msg": {
"user": {
"groups": "docker",
"name": "rigved",
"state": "present"
}
}
}
ok: [localhost] => (item=ayush) => {
"msg": {
"user": {
"groups": "test",
"name": "ayush",
"state": "present"
}
}
}
ok: [localhost] => (item=eshant) => {
"msg": {
"user": {
"groups": "test",
"name": "eshant",
"state": "present"
}
}
}
PLAY RECAP ***********************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Alternately, you could structure your data like this:
users:
- name: anika
group: docker
- name: rigved
group: docker
- name: ayush
group: docker
- name: eshant
group: docker
You can just use a simple loop to process this structure:
---
- name: testing nested play
gather_facts: false
hosts: localhost
vars:
users:
- name: anika
group: docker
- name: rigved
group: docker
- name: ayush
group: docker
- name: eshant
group: docker
tasks:
- debug:
msg:
user:
name: "{{ item.name }}"
state: present
groups: "{{ item.group }}"
loop: "{{ users }}"
loop_control:
label: "{{ item.name }}"
Note that in both the above examples I'm using loop_control on the tasks just to set the loop label and make the output look nicer. You could remove loop_control from the above tasks with no impact on how they operate.

How to convert a iterate loop list into a string to use in Linux

I have Ansible role that I want to iterate over.
The goal is to create new user accounts from a list. The playbook calls the role and sends the list to iterate.
The OS (Linux Debian 8.8) sees the all of var unicode "[u'user']"
Some other tests performed show new users:
['test']
[u'test']
All I really want is to have the var to be a string so I make a new user and add the needed keys and other files. I can also join the var into paths for key and other files.
I have searched for an easy way to "| to_string", (not in Ansible)
The Filter "to_yaml" gets rid of the unicode but not "[]" and adds "\n" at the end.
The item for the ssh key copy if for the various id_(type).pub files.
I have read:
Convert Ansible variable from Unicode to ASCII
Code
Playbook:
vars_files:
- /home/admin/common/vars/UserList
gather_facts: False
roles:
- { role: common, "{{ UserList }}" }
UserList file
---
UserList:
- 'test'
...
role/common/main.yml
---
- name: Add user to server
user:
name: "{{ UserList }}"
shell: /bin/bash
- name: make direcotry
file:
path: "/home/{{ UserList }}/.ssh"
state: directory
- name: Copy ssh public key to user/.ssh/_key_.pub
copy:
src: "/home/{{ UserList }}/.ssh/{{ item }}"
dest: "/home/{{ UserList }}/.ssh/{{ item }}"
mode: 600
force: no
with_items:
- id_rsa.pub
- id_dsa.pub
- id_ecdsa.pub
...
A different form, but still errored as below.
roles:
- role: common
with_items:
- "{{ UserList }}"
Error
(item=id_rsa.pub) => {"failed": true, "invocation": {"module_args": {"dest": "/home/[u'test']/.ssh/id_rsa.pub", "force": false, "mode": 600, "src": "/home/[u'test']/.ssh/id_rsa.pub"}, "module_name": "copy"}, "item": "id_rsa.pub", "msg": "could not find src=/home/[u'test']/.ssh/id_rsa.pub"}
Workaround
I found a workarount for my issue. I would be cautious to call it a solution. For this case it will suffice. I need to "loop" with my var with the builtin {{ item }}. Then {{ item }} is used as a string and I can create the PATHs I need. I can also iterate through the a series of items with "with_nested".
- name: create empty file
file:
path: "{{ '/home/' + item + '/.ssh/authorized_keys' }}"
state: touch
with_items:
- "{{ UserList }}"
- name: Copy ssh public key to user/.ssh/_key_.pub
copy:
src: "{{ '/home/' + item[1] + '/.ssh/' + item[0] }}"
dest: "{{ '/home/' + item[1] + '/.ssh/' + item[0] }}"
mode: 600
force: no
with_nested:
- [ 'id_rsa.pub' , 'id_dsa.pub' , 'id_ecdsa.pub' ]
- "{{ UserList }}"

How can I change root password via ansible to ~30 hosts?

I need to update more than 30 hosts the root password.
I read that I can user user module for that... but I'm doing something wrong.
Can you drop some help here ?
Typo in above yaml file .
- name: Password rollover
user:
name: "{{ lookup('env', 'USER') }}" ## Should not have "-"
update_password: always
password: "{{ lookup('env', 'PASSWORD') }}"
I came across a similar situation and what I did to solve it was a playbook like this:
Filename: password_rollover.yml
---
- name: Password rollover
user:
- name: "{{ lookup('env', 'USER') }}"
update_password: always
password: "{{ lookup('env', 'PASSWORD') }}"
You use it passing those env variables:
USER=root PASSWORD=newpass ansible-playbook --limit group password_rollover.yml -b

Resources