First thing first - I'm not an Ansible wiz and I guess there is a better way to do this, but this is how I managed to do this.
I needed to create users on Linux servers, I used this to read the users parameters from a file and create all by using 'with_items'.
This worked perfectly.
I wanted to add an option to create only one user from the list.
This is my yml script:
---
- hosts: all
user: deploy
become: yes
tasks:
- include_vars: ../variables/users.yml
# - command: echo {{ Guser }}
- debug: msg={{ users }}
- name: Create DevOps team users
user: name={{ item.username }} shell=/bin/bash createhome=yes comment='Created by Ansi' groups={{ item.use_sudo }} uid={{ item.use_uid }} group={{ item.use_group }} state=present
ignore_errors: yes
with_items:
- '{{ Guser }}'
when: Guser is defined
And this is how I run it:
ansible-playbook --extra-vars "Guser=gilil2" -l myserver /home/devops/roles/create-users/tasks/create_user_by_name.yml -u deploy
Snip from my variables/users.yml
---
users:
- username: gilil
use_sudo:
use_uid: 2005
use_group: users
- username: gilil2
use_sudo:
use_uid: 1010
use_group: users
- username: gilil3
use_sudo:
use_uid: 1011
use_group: users
Use selectattr Jinja2 filter:
- name: Create DevOps team users
user: name={{ item.username }} shell=/bin/bash createhome=yes comment='Created by Ansi' groups={{ item.use_sudo }} uid={{ item.use_uid }} group={{ item.use_group }} state=present
ignore_errors: yes
with_items: "{{ users | selectattr('username','equalto',Guser|default('uknown')) | list }}"
This will reduce original users list to a list of elements where username attribute equals to Guser vars' value or unknown if Guser is undefined. I assume that there are no user with uknown username, so selectattr can generate empty list in this case.
Related
I have a setup consisting of prod and dev environments
there are 2 projects ( project1 and project2 )
I have dev's and ops users. Devs to only be created on dev servers in projects that the user is assigned and ops's to be created in all projects and envs.
I'd like for all users to be defined in the same user definition file.
my user definitions :
- username:
profile: # dev / ops
projects: # project1 / project2 / all
key: #"ssh-rsa key
OSgroups: "" # which OS groups is user member of
OSpass: "" # hashed OS password
my user create playbook:
- name: Create users
become: yes
user:
name={{ item.username }}
shell={{ item.shell }}
groups={{ item.groups }}
createhome=yes
password={{ item.OSpass }}
## now the problem part
with_items:
- "{{ users }}"
when: "{{ defaults_for_env.environment }} == {{ item.profile }}"
##
------------------------------------------------------------
## environment defaults
---
defaults_for_env:
- environment: "dev"
when just running usercreate playbook users are created, so the commands work.
What I'd like is for the playbook to:
for host is in inventory group [development] to create dev's assigned to inventory group [project1] and all users of type ops.
And for hosts in inventory group [prod] to only create users of type ops.
I cant get my head around the loops and inventory'n'stuff
Hope my question makes sense ?
One possible solution to your current requirement.
Inventory
---
all:
children:
dev:
hosts:
devhost1:
devhost2:
prod:
hosts:
prodhost1:
prodhost2
group_vars/all.yaml
---
#....
default_users:
- name: opsuser1
shell: /bin/bash
groups:
- group1
- group2
createhome: true
password: S0S3cr3t
- name: opsuser2
shell: /bin/sh
groups:
- wheel
- docker
- users
createhome: false
password: n0ts0S3cr3t
users: "{{ default_users + (specific_users | default([])) }}"
group_vars/dev.yml
---
#....
specific_users:
- name: devuser1
shell: /bin/bash
groups:
- groupa
- groupb
createhome: true
password: v3rYS3cr3t
- name: devuser2
shell: /bin/sh
groups:
- titi
- toto
- tata
createhome: false
password: U1trAS3cr3t
Your playbook
- hosts: all
become: true
tasks:
- name: Create users
user:
name: "{{ item.username }}"
shell: "{{ item.shell }}"
groups: "{{ item.groups }}"
createhome: "{{ item.createhome | bool }}"
password: ""{{ item.password | password_hash('sha512', 'S3cretS4lt') }}"
loop: "{{ users | flatten(levels=1) }}"
The playbook will go over all your hosts. By default it will read the values in the all group where you have the definition of default_users (i.e. ops) + the calculation for the users list being default_users + specific_users.
For machines in the prod group, specific_users is null and will default to an empty list.
For machines in the dev group, specific_users will be added to the default ones.
The loop is then made on users which will have the correct values for each machine depending on its situation.
I am writing a playbook to create a user if it does not exist. If it exist then it ask again for a new username. It must ask again for new user input. But var_promt runs only one time. How can I do that?
- name: An example prompting playbook
hosts: all
vars_prompt:
- name: username
prompt: "Enter a username"
tasks:
- name: Print out your input
debug:
msg: "You provided the {{ username }} for the prompt"
- name: User Exist
command: grep {{ username }} /etc/passwd
ignore_errors: yes
register: user_exist
- name: User Existance output
debug: var=user_exist.stdout
- name: User creation
user: name={{ username }}
when: user_exist.stdout is not match(".*:.*:.*:.*::.*:")
- name: Display Message
debug:
msg: "User {{ username }} already exists"
when: user_exist.stdout is match(".*:.*:.*:.*::.*:")
- name: User check
command: id {{ username }}
ignore_errors: yes
register: ID
- name: UserID of Username
debug: var=ID.stdout
name: An example prompting playbook
hosts: localhost
vars_files:
/root/users.yml
tasks:
name: Create deploy user
user:
name: "{{ item.name }}"
shell: "{{ item.shell }}"
password: "{{ lookup('password', '/tmp/{{ item.name }}.txt chars=ascii_letters') | password_hash('sha512') }}"
createhome: yes
groups: "{{ item.groups }}"
update_password: on_create
with_items: "{{ users }}"
I am using an Ansible playbook to manage users (taken from https://keksi.io/tutorials/2016/12/05/how-to-manage-remote-server-users-with-ansible/):
vars/users.yml file (users, user to group assignments, passwords, SSH keys):
---
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"
- username: user3
[...]
And this is the playbook:
- hosts: all
become: true
tasks:
- include_vars: ../vars/users.yml
- name: Install sudo
apt: name=sudo state=present
- name: Add the group admin
group:
name: admin
state: present
- 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') }}"
when: item.username is defined
with_items: '{{ users }}'
- name: Setup administrator users with complete sudo access
user: name={{ item.username }} groups=sudo append=yes
with_items: '{{ users }}'
when: item.admin is defined and item.admin == True
- name: Add SSH-keys to users
authorized_key:
user: "{{ item.0.username }}"
key: "{{ item.1 }}"
with_subelements:
- "{{ users }}"
- keys.active
- flags:
skip_missing: True
when: item.0.state is not defined or item.0.state != "absent"
- name: Remove old SSH-keys from users
authorized_key:
user: "{{ item.0.username }}"
key: "{{ item.1 }}"
state: absent
with_subelements:
- "{{ users }}"
- keys.disabled
- flags:
skip_missing: True
when: item.0.state is not defined or item.0.state != "absent"
I can correctly execute this playbook on all servers and have admin and normal users created and managed on them.
Now I want to add a new step. I have several servers, and not every users should be configured on every server, and some users should be in an additional group only on some servers (for example developers on developing servers).
So I wish to create some assignments for users on hosts or host groups, something like:
- host: host1
admins:
- user1
- user2
deployers:
- user2
- user3
users:
- user4
- user5
- host: hostgroup1
admins:
- user2
users:
- user3
- user5
So I would like to be able to execute the playbook on all servers to have users created or updated based on this declarations, without writing a duplication of the playbook for every host.
I don't have any idea about how to achieve this, could you help me please?
Edit: I tried to add a new "hosts" key in my users.yml variable file, this way:
---
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
And I modified the task this way:
- 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)
My explanation: I need to create the user if it's admin (so it must be created on every host or if the current host's inventory_hostname is listed into one of the hosts subkeys arrays (in a second step I wish to extend this check also if the current host is in an hostgroup listed in one of item.hosts subkeys.
The problem is that this way user1 is created (because item.admin is True) but user2 not because the other condition is always False.
You could use host_vars or group_vars to store the users data instead of vars/users.yml
When you run the playbook against multiple hosts the appropriate values for each host or group member will be read from the related group_vars or host_cars file
See here for more information: https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#splitting-out-host-and-group-specific-data
I am currently working on a host where i have installed ansible. I have created 2 application accounts with groups with nologin and within that groups i want to add users, so that every department has their own ansible directory.
My vars look like below:
---
- hosts: localhost
become: yes
vars:
ansible_groupuser:
- name: "ansible-dictators"
ansible_groupuser_uid: "3000"
ansible_users:
- idia
- josefs
- donaldt
- kimjongu
- name: "ansible-druglords"
ansible_groupuser_uid: "3001"
ansible_users:
- pabloe
- javierg
- frankl
- rossu
Now i have 2 plays. 1 to create the Groupuser:
# This creates the groupuser
- name: Play 1 Create central ansible user and group per department
user:
name: "{{ item.name }}"
shell: "/sbin/nologin"
home: "/home/{{ item.name }}"
comment: "{{ item.name }} Group Account"
uid: "{{ item.ansible_groupuser_uid }}"
append: "yes"
with_items:
- "{{ansible_groupuser}}"
And 1 to create the "normal" users:
- name: Play 2 Create users
user:
name: "{{ item.1 }}"
shell: "/bin/bash"
home: "/home/{{ item.1 }}"
comment: "{{ item.1 }}"
groups: "{{ item.0.name }}"
append: "yes"
with_subelements:
- "{{ ansible_groupuser }}"
- ansible_users
If i run this play it creates the groupuser ansible-dictators on 3000 and ansible-druglords on 3001. idia gets 3002, josefs gets 3003 etc. It gets kinda messy, when i want to add a 3th groupuser like ansible-rockstars, it starts counting at the first available uid, 3010. What i want is to place the groupusers and the common users in 2 different ranges (2000 and 3000 for example)
When i do a with_together on the first play, like below, it works:
- name: Play1 Create central ansible user and group per department
user:
name: "{{ item.0.name }}"
shell: "/sbin/nologin"
home: "/home/{{ item.0.name }}"
comment: "{{ item.0.name }} Group Account"
uid: "{{ item.1 }}"
append: "yes"
with_together:
- "{{ansible_groupuser}}"
- "{{ range(3000,3020)|list }}"
when: item.0 != None
But when i do a with_together on the second play, it doesnt work:
- name: Create users
user:
name: "{{ item.1 }}"
shell: "/bin/bash"
home: "/home/{{ item.1 }}"
comment: "{{ item.1 }}"
groups: "{{ item.0.name }}"
append: "yes"
uid: "{{ item.2 }}"
with_together:
- "{{ ansible_groupuser }}"
- ansible_users
- "{{ range(2000,2020)|list }}"
Anyone got a suggestion how to make the second play work with a uid in a certain range? Or another suggestion how to get the uid's in different groups? To give the groupusers an uid in the vars is no problem. But i am expecting a lot of "common" users to add (+50) and i dont want to specify a uid for all of those users.
Hope it makes sense. Thanks in advance.
I think range(...) approach has a flaw: if you delete some user from your list in the future, IDs for subsequent entries will change and you can end up with messed file permissions on your system.
You can patch user module to support --firstuid/--lastuid arguments of the underlying adduser command, so you can set different range for uid generation.
But I'd suggest you to define "static" uids for top-level users in your vars file (from some predefined range, say: 3000..30xx) – this way you can safely add/remove top-level user/groups in the future.
And leave "common" users to get their ids automatically, so adding/deleting them will not mess your ids. If you like them to be from some specific range, you can modify system-wide /etc/adduser.conf with FIRST_UID=5000/LAST_UID=6000.
What is wrong when iterating over a collection in ansible?
When executing ansible-playbook -i "localhost," -c local main.yml the error is
- name: echo kerberos
shell: echo "addprinc -pw {{ item.password }} {{ item.username }}"
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
But for me it looks like I am already correctly following these rules.
Here my minimal example:
main.yml
---
- hosts: all
tasks:
- name: echo kerberos
shell: echo "addprinc -pw {{ item.password }} {{ item.username }}"
with_items: "{{ users }}"
users.yml
---
users:
- username: test_user
password: test_user
sn: User
uid: 50001
gid: 100
- username: test_user1
password: test_user
cn: Test User1
sn: User1
uid: 50002
gid: 100
user_groups:
- cn: access1
gid: 100001
users:
- test_user1
You should be careful with padding in YAML:
---
- hosts: all
tasks:
- name: echo kerberos
shell: echo "addprinc -pw {{ item.password }} {{ item.username }}"
with_items: "{{ users }}"
shell and with_items are aligned with name.