Manage Unix Users Over Multiple Evironments with Ansible

896 views
Skip to first unread message

Ralph Bolton

unread,
Nov 13, 2015, 1:02:25 PM11/13/15
to Ansible Project
Hi,

I'm trying to manage a small number of Unix users on a smallish estate of servers (~100 servers). My users are either devs, sysadmins or support, and need different access to different boxes. I've got a nice way to give them differing levels of sudo access, but now want to figure out how to grant and revoke access to different boxes. Obviously, I've got a variety of groups in my ansible hosts file, and I have a Yaml definition for my users and groups.

For example, the devs really only need access to the host groups dev_servers and test_servers. However, let's say user Fred needs temporary access to production, I'd like to add him to a group, run Ansible and then let him do his work. When he's done, remove him from that group and then run Ansible to revoke his access.

So far, I have a vars/main.yml that looks something like:

---
unix_groups
:
 
- group: general
    state
: present
    gid
: 1500

unix_users
:
   
- user: fred
     state
: present
     uid
: 5000
     
group: general
     root_access
: restricted
 
- user: barney
    state
: present
    uid
: 5001
   
group: general
    root_access
: none
 
- user: wilma
    state
: present
    uid
: 5002
   
group: general
    root_access
: full



...and a tasks/main.yml that contains:

- name: Pull in user/group variables from role_vars
  include_vars
: main.yml

- name: Create Unix groups from the groups.yml file
  action
: group name={{ item.group }} state={{ item.state }} gid={{ item.gid }}
  with_items
: unix_groups

- name: Create Unix users from the users.yml file
  action
: user name={{ item.user }} state={{ item.state }} group={{ item.group | default(None) }} uid={{ item.uid | default(None) }} shell=/bin/bash expires=0
  with_items
: unix_users

- name: Create sudoers file if the user is allowed root access
 
template: src=../templates/sudoers-{{ item.root_access|default(None) }}.j2 dest=/etc/sudoers.d/{{ item.user }} owner=root group=root mode=0440
 
when: item.state == "present" and (item.root_access|default(None) == "full" or item.root_access|default(None) == "restricted")
  with_items
: unix_users

- name: Revoke root access if user is not allowed it
  file
: dest=/etc/sudoers.d/{{ item.user }} state=absent
 
when: item.state != "present" or (item.root_access|default(None) != "full" and item.root_access|default(None) != 'restricted')
  with_items
: unix_users

All of this works nicely - if I were to set Wilma's root_access to 'restricted' or 'none', then her sudo config would either change or be removed entirely. Likewise, if I set her 'state' to 'absent' her account if removed from the systems. This works nicely for all the hosts I apply this role to (which at the moment is all of them). So far so good...

Now I'd like to be able to add users to certain hosts (I'm guessing host groups makes most sense). I tried adding something like

    access_to: test_hosts, dev_hosts

...and

    access_to:
    - test_hosts
    - dev_hosts

...to vars/main.yml and then tried various permutations of "when" clause in my user creation. For example:

   when: "inventory_hostname in item.access_to"

...but nothing I've tried seems to work. I realise the normal pattern is to apply a role to certain host groups, and so perhaps I need to apply different roles to different groups and yet somehow supply them with my single Yaml user definition. I'm not sure how I'd revoke access if I'm not running against a group of hosts, but I'm sure I could figure something out there.

All this feels like I've made this a lot harder for myself than I should have done. What's the "right" way to do this sort of thing? Any ideas if I can make what I have work in some sensible way, or should I be reworking it somehow else?

Cheers,

...Ralph

Joanna Delaporte

unread,
Nov 13, 2015, 1:24:18 PM11/13/15
to Ansible Project
Does the following work?

   when: "inventory_hostname in item.value.access_to"

I have used dicts a little for users, and that is how I reference details for users.

Joanna

Ralph Bolton

unread,
Nov 16, 2015, 4:38:05 AM11/16/15
to Ansible Project
Thanks for the suggestion - Unfortunately it doesn't work for me :-( I get:

TASK: [users | Create Unix users from the users.yml file] *********************
fatal
: [ralph] => error while evaluating conditional: inventory_hostname in item.value.access_to

FATAL
: all hosts have already failed -- aborting


I tried this in the play:

  - name: debug output
    debug
: msg="access to is {{item.access_to }}"
    with_items
: unix_users

...and got this as output:

    "msg": "access to is ['dev_hosts', 'test_hosts', 'uat_hosts']"


...so it's getting it, and even knows its a list of names. If I put the hostname in the list it matches and we're all good - but I'd really rather use Ansible host groups. I guess I need a way to 'eval()' the list so that each of items in the list is looked up in groups[]. I tried to do this as a template, and successfully made up the right sort of 'code' as text, but then couldn't find a way to have it re-evaluated into actual data.

I'm thinking I need to find a whole different way to do this, but can't find any good advice on how I should approach the problem.

Cheers,

...Ralph

Arbab Nazar

unread,
Nov 16, 2015, 5:30:06 AM11/16/15
to Ansible Project
Can you please check this as well:

when: item.get('state', 'present') == 'present'

I am just sending this reply quickly without reading the whole question, but I think that will solve your problem.

For Reference, please check this role:

Marcus Franke

unread,
Nov 16, 2015, 6:44:57 AM11/16/15
to Ansible Project

Hi,

have you thought about managing those users with LDAP and sssd? The permissions could be managed with groups.

Maybe this approach is easier to maintain. In case of on or off boarding, just create or disable the user. No need to run Ansible just because someone left your organization.

regards,
/mf


--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To post to this group, send email to ansible...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/0082cfa6-c60d-409b-a2f3-2d694c9540e4%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ralph Bolton

unread,
Nov 16, 2015, 7:06:06 AM11/16/15
to Ansible Project
Oh my word... I've just cracked it:

- name: Create Unix users from the users.
yml file
  action
: user name={{ item.0.user }} state={{ item.0.state }} group={{ item.0.group | default(None) }} uid={{ item.0.uid | default(None) }} shell=/bin/bash expires=0
 
when: "item.1 == 'all' or inventory_hostname in groups[item.1]"
  with_subelements
:
 
- unix_users
 
- access_to


I spent a lot of Friday looking for some patterns for this and found very little. It seems it was in the doco all along: http://docs.ansible.com/ansible/playbooks_loops.html#looping-over-subelements. This approach effectively checks the user against each group of hosts separately, which has lots more screen output but not a great deal more execution time.

I'd love to use LDAP or some such for this - it would be way more convenient and would mean I could do things like enforce password policies and whatnot too. As it stands, I don't have scope to set up any sort of 'auth server', so unfortunately, Ansible is the best I've got. For the scale of what I've got to solve for, it's actually not as bad as that sounds - I'm sure that once we've got lots of people in multiple different roles and needing different levels of access then an LDAP solution would be forthcoming.

Thanks all for your help and suggestions - it gave me the 'shove' I needed to get to the solution.

Cheers,

...Ralph

Jonathan Davila

unread,
Nov 16, 2015, 11:54:14 AM11/16/15
to Ansible Project
Here is a repo with my current favorite role for managing users across different departments and environments:

Ralph Bolton

unread,
Nov 17, 2015, 4:08:07 AM11/17/15
to Ansible Project
Thanks Jonathan - that looks really good - it could be just what I need!

...Ralph
Reply all
Reply to author
Forward
0 new messages