Create different sets of users on different sets of hosts

151 views
Skip to first unread message

Paul Slootman

unread,
Oct 27, 2014, 10:55:03 AM10/27/14
to ansible...@googlegroups.com
Hi,
A new ansible user here... I've been perusing the mailing list archives and have gathered a lot of useful tidbits.

I've been fairly successful in figuring out how to create a set of users on a list of hosts. However, what I'm not so clear about is how to create different sets of users on different categories of hosts. We're a software house that administrates the server farms at our customers. Let's say we have 20 customers, and each customer can have 3-20 servers; these servers fall into different categories as well: application servers, database servers, test app. servers, test DB servers for example.

One set of users should be created on all systems always, namely our sysadmins.
Another set of users should only be created on the database servers; likewise for the application servers.

The approach I'd find logical would be to write a playbook such as:

- hosts: all_hosts
  tasks:
  - name: Add sysadmin users
    user: name={{ item.key }} password={{ item.value.password }} uid={{ item.value.uid }} group={{ item.value.group }} groups="" comment="{{ item.value.comment }}" state={{ item.value.state }} update_password=always
    with_dict: sysadmin_user
 
- hosts: db_hosts
  tasks:
  - name: Add DBA users
    user: name={{ item.key }} password={{ item.value.password }} uid={{ item.value.uid }} group={{ item.value.group }} groups="" comment="{{ item.value.comment }}" state={{ item.value.state }} update_password=always
    with_dict: dba_user
 
- hosts: application_hosts
  tasks:
  - name: Add application users
    user: name={{ item.key }} password={{ item.value.password }} uid={{ item.value.uid }} group={{ item.value.group }} groups="" comment="{{ item.value.comment }}" state={{ item.value.state }} update_password=always
    with_dict:appl_user

This would work, but would entail listing all hosts at least twice: once for the "all_hosts" list, and once for the specific type of host. I'd like to be able to compose the "all_hosts" list automatically out of the other lists. I've tried a couple of ways after reading things in the mailing list archive that might be applicable, but haven't had any success.

So, in short, my question really boils down to: Is it possible to merge existing host lists so that I don't have to repeat hosts in different lists?  The same question also applies to user lists, although I suspect that if it's possible with host lists, the same method will work for user lists as well.

(Being able to merge host lists would mean it would be possible to define hosts in lists according to customer + type, and then build different host lists such as "all DB hosts" or "all hosts at customer XYZ".)


Thanks!

mephage

unread,
Oct 28, 2014, 6:07:00 PM10/28/14
to ansible...@googlegroups.com
Paul,

I recently encountered what I believe the be the same scenario. I googled around and couldn't seem to find anything to fit the scenario so I used the following approach (probably re-inventing the wheel). The key is to make use of roles, playbooks and vars separately. I've adapted your example and truncated it for simplicity:

1. Create a role 'groups_users' consisting of least 'roles/groups_users/tasks/main.yml':
---
- name: Add groups
  groups: name={{ item.name }}
  with_items: groups
- name: Add users
  user: name={{ item.name }}
        uid={{ item.uid }}
  with_items: users

2. Define these groups in your inventory file 'myhosts':
[myhosts]
host1
host2
host3

[groups_users_sysadmins]
host1
host2
host3

[groups_users_db]
host1

3. Create external vars files for each of your groups:

'external_vars/groups_users_sysadmins':
---
groups:
- name: sysadmins
users:
- name=username1
  uid=1234
- name=username2
  uid=2345

'external_vars/groups_users_db':
---
groups:
- name: db
users:
- name=username3
  uid=3456
- name=username4
  uid=4567

4. Create group_vars files for each group:

'group_vars/myhosts':
---
groups_users_sysadmins: no
groups_users_db: no

'group_vars/myhosts':
---
groups_users_sysadmins: yes

'group_vars/db':
---
groups_users_db: yes

5. Create a playbook that adds each group conditionally:
'groups_users.yml':
---
- name: Add sysadmins group and users
  hosts: all
  vars_files:
  - external_vars/groups_users_sysadmins.yml
  roles:
  - { role: groups_users, when: groups_users_sysadmins == True }

- name: Add db group and users
  hosts: all
  vars_files:
  - external_vars/groups_users_db.yml
  roles:
  - { role: groups_users, when: groups_users_db == True }

It's quite a different approach and requires a few more files, but it allows for more scalability.

mephage

unread,
Oct 29, 2014, 10:29:17 PM10/29/14
to ansible...@googlegroups.com
Made an error (why aren't google group posts editable? who knows...). Should read:


4. Create group_vars files for each group:

'group_vars/myhosts':
---
groups_users_sysadmins: no
groups_users_db: no

'group_vars/sysadmins':

---
groups_users_sysadmins: yes

'group_vars/db':
---
groups_users_db: yes

Paul Slootman

unread,
Oct 30, 2014, 4:41:48 AM10/30/14
to ansible...@googlegroups.com
Thanks for your example, I'm just about understanding how it works :-)  I did need to add a .yml extension to the external_vars/groups_users_* files but that was obvious from how the playbook referenced those file. Also the role needs "group:" and not "groups:" (the error message could have been a bit clearer...).

However, when I try this, the gathering facts works fine, then:

TASK: [groups_users | Add groups] ********************************************* 
fatal: [host1] => with_items expects a list or a set
fatal: [host2] => with_items expects a list or a set
fatal: [host3] => with_items expects a list or a set

FATAL: all hosts have already failed -- aborting

This is the sort of thing that's probably trivial to fix with some experience but I have no clue how to proceed now :-(

FWIW I'm running:
ansible 1.8 (devel 44afa7facc) last updated 2014/10/27 14:31:42 (GMT +200)

Paul Slootman

unread,
Oct 30, 2014, 6:14:53 AM10/30/14
to ansible...@googlegroups.com
Following up on myself:

If I change the roles/groups_users/tasks/main.yml to this:

---
- name: Add groups
  group: name={{ item.name }}
  with_dict: groups

- name: Add users
  user: name={{ item.name }}
        uid={{ item.uid }}
  with_items: users

i.e. with_dict instead of with_items in the Add groups task, then I don't get an error. However the output of --check does not look like it is doing what I'd expect. Also why does the "Add users" task not need with_dict instead of "with_items"?!

GATHERING FACTS ***************************************************************
ok: [host1]
ok: [host3]
ok: [host2]

TASK: [groups_users | Add groups] ********************************************* 
skipping: [host1] => (item={'key': 'ungrouped', 'value': []})
skipping: [host1] => (item={'key': 'myhosts', 'value': ['host1', 'host2', 'host3']})
skipping: [host2] => (item={'key': 'ungrouped', 'value': []})
skipping: [host1] => (item={'key': 'all', 'value': ['host1', 'host2', 'host3']})
skipping: [host2] => (item={'key': 'myhosts', 'value': ['host1', 'host2', 'host3']})
skipping: [host1] => (item={'key': 'groups_users_db', 'value': ['host3']})
skipping: [host2] => (item={'key': 'all', 'value': ['host1', 'host2', 'host3']})
skipping: [host3] => (item={'key': 'ungrouped', 'value': []})
skipping: [host1] => (item={'key': 'groups_users_sysadmins', 'value': ['host1', 'host2', 'host3']})
skipping: [host2] => (item={'key': 'groups_users_db', 'value': ['host3']})
skipping: [host3] => (item={'key': 'myhosts', 'value': ['host1', 'host2', 'host3']})
skipping: [host2] => (item={'key': 'groups_users_sysadmins', 'value': ['host1', 'host2', 'host3']})
skipping: [host3] => (item={'key': 'all', 'value': ['host1', 'host2', 'host3']})
skipping: [host3] => (item={'key': 'groups_users_db', 'value': ['host3']})
skipping: [host3] => (item={'key': 'groups_users_sysadmins', 'value': ['host1', 'host2', 'host3']})

TASK: [groups_users | Add users] ********************************************** 
skipping: [host1] => (item={'name': 'username3', 'uid': 3456})
skipping: [host1] => (item={'name': 'username4', 'uid': 4567})
skipping: [host3] => (item={'name': 'username3', 'uid': 3456})
skipping: [host2] => (item={'name': 'username3', 'uid': 3456})
skipping: [host3] => (item={'name': 'username4', 'uid': 4567})
skipping: [host2] => (item={'name': 'username4', 'uid': 4567})

PLAY RECAP ******************************************************************** 
host1                     : ok=6    changed=0    unreachable=0    failed=0    
host2                     : ok=6    changed=0    unreachable=0    failed=0    
host3                     : ok=6    changed=0    unreachable=0    failed=0    

This is shown twice (identical output), once for each play.


Paul

mephage

unread,
Nov 3, 2014, 3:48:06 PM11/3/14
to ansible...@googlegroups.com
Hey Paul,

My apologies for the delayed reply.

I can't see why you're getting the result you are. But I did notice a few errors in the snippets I posted. Here's a testing and working example:

https://github.com/mephage/ansible-groups_users_example

Paul Slootman

unread,
Nov 11, 2014, 11:00:04 AM11/11/14
to ansible...@googlegroups.com
Also apologies, I was offline for a bit and then had other priorities at work.

I've downloaded your github example as a zip file, unpacked it, modified host and usernames to reflect the local situation.

I now don't get an error, but now the tasks just show "skipping" for everything:

PLAY [Add sysadmins group and users] ****************************************** 

GATHERING FACTS *************************************************************** 
ok: [host2]
ok: [host1]

TASK: [groups_users | Add groups] ********************************************* 
skipping: [host1] => (item={'state': 'present', 'gid': 7575, 'name': 'impulsys'})
skipping: [host2] => (item={'state': 'present', 'gid': 7575, 'name': 'impulsys'})

TASK: [groups_users | Add users] ********************************************** 
skipping: [host1] => (item={'comment': 'Paul Slootman 2', 'state': 'present', 'group': 'groupname', 'name': 'pslootman', 'password': '$1$.xxxxxxxxxxxxxxxxxxxxxx', 'pubkey': 'ssh-rsa AAAA....', 'uid': 54343})
skipping: [host2] => (item={'comment': 'Paul Slootman 2', 'state': 'present', 'group': 'groupname', 'name': 'pslootman', 'password': '$1$.xxxxxxxxxxxxxxxxxxxxxx', 'pubkey': 'ssh-rsa AAAA....', 'uid': 54343})

PLAY [Add db group and users] ************************************************* 

GATHERING FACTS *************************************************************** 
ok: [host1]
ok: [host2]

TASK: [groups_users | Add groups] ********************************************* 
skipping: [host1] => (item={'state': 'present', 'gid': 7576, 'name': 'dbagroup'})
skipping: [host2] => (item={'state': 'present', 'gid': 7576, 'name': 'dbagroup'})

TASK: [groups_users | Add users] ********************************************** 
skipping: [host1] => (item={'comment': 'Nelson DBA', 'state': 'present', 'group': 'dbagroup', 'name': 'ndba', 'password': '$1$.xxxxxxxxxxxxxxxxxxxxxx', 'pubkey': None, 'uid': 54331})
skipping: [host2] => (item={'comment': 'Nelson DBA', 'state': 'present', 'group': 'dbagroup', 'name': 'ndba', 'password': '$1$.xxxxxxxxxxxxxxxxxxxxxx', 'pubkey': None, 'uid': 54331})

PLAY RECAP ******************************************************************** 
host1                      : ok=6    changed=0    unreachable=0    failed=0   
host2                      : ok=6    changed=0    unreachable=0    failed=0   

I've modified roles/groups_users/tasks/main.yml to this:

---
- name: Add groups
  group: name={{ item.name }} gid={{ item.gid }} state={{ item.state }}
  with_items: unix_groups

- name: Add users
  user: name={{ item.name }}
        uid={{ item.uid }}
        password={{ item.password }}
        group={{ item.group }}
        groups=""
        comment="{{ item.comment }}"
        state={{ item.state }}
        update_password=always
        shell=/bin/bash
  with_items: unix_users

i.e. added extra fields and renamed "groups" and "users" to "unix_groups" and "unix_users". This is reflected by corresponding changes to external_vars/groups_users_db.yml and external_vars/groups_users_sysadmins.yml .

My problem is that I don't quite get how the group_vars/* files interact with roles stuff. I haven't had a lot of time to experiment with this yet, so perhaps it's something obvious but I didn't want to delay this response in case you thought I was ignoring you :-)

If it's something obvious to someone, please let me know. I'll continue trying it when I get time to spend on this.


thanks,
Paul
Reply all
Reply to author
Forward
0 new messages