51

In the documentation, there is an example of using the lineinfile module to edit /etc/sudoers.

- lineinfile: "dest=/etc/sudoers state=present regexp='^%wheel' line='%wheel ALL=(ALL) NOPASSWD: ALL'"

Feels a bit hackish.

I assumed there would be something in the user module to handle this but there doesn't appear to be any options.

What are the best practices for adding and removing users to /etc/sudoers?

chishaku
  • 4,577
  • 3
  • 25
  • 33

2 Answers2

81

That line isn't actually adding an users to sudoers, merely making sure that the wheel group can have passwordless sudo for all command.

As for adding users to /etc/sudoers this is best done by adding users to necessary groups and then giving these groups the relevant access to sudo. This holds true when you aren't using Ansible too.

The user module allows you to specify an exclusive list of group or to simply append the specified groups to the current ones that the user already has. This is naturally idempotent as a user cannot be defined to be in a group multiple times.

An example play might look something like this:

- hosts: all
  vars:
    sudoers:
      - user1
      - user2
      - user3
  tasks:
    - name: Make sure we have a 'wheel' group
      group:
        name: wheel
        state: present

    - name: Allow 'wheel' group to have passwordless sudo
      lineinfile:
        dest: /etc/sudoers
        state: present
        regexp: '^%wheel'
        line: '%wheel ALL=(ALL) NOPASSWD: ALL'
        validate: visudo -cf %s

    - name: Add sudoers users to wheel group
      user:
        name: "{{ item }}"
        groups: wheel
        append: yes
      with_items: "{{ sudoers }}"
ydaetskcoR
  • 53,225
  • 8
  • 158
  • 177
  • 30
    `validate: visudo -cf %s` as an attribute of the `lineinfile` task will ensure that you don't mess something up, by running it through the validation command first. – xenithorb Dec 18 '16 at 22:25
  • Note that the `validate` command will probably need the full path to `visudo` to work. – Matt Hughes May 24 '18 at 17:49
  • The problem with this approach is that you have to a) be sure you have the wheel group already there and already enabled in sudoers, and b) be sure that you want to put the user into the wheel group and not some other one. What if you want a user to be NOPASSWD, but limit their commands? The lineinfile module gets trickier when you need to insert a line but *not* at the end. (Because inserting it at the end would break the #includedir statement, which must be on the last line.) – Todd Walton Nov 22 '19 at 13:43
  • 3
    Btw. if you are wondering, what the `%s` in `validate` means, it is the destination (dest) or path of the file that is supposed to be modified. Here is the [documentation](https://docs.ansible.com/ansible/latest/modules/lineinfile_module.html). – AdamKalisz Apr 02 '20 at 09:50
  • Great answer. A slightly different approach would be to use the `copy` module instead of the `lineinfile` module to copy the sudoers files from the control node to each managed node. There are pros and cons to using the copy vs. lineinfile module. I tend to like the copy module to have a single, standardized file on the control node to ensure each managed node is using the same exact file. But, of course, there are possible drawbacks to using copy. Just food for thought here. – JeremyCanfield Jun 20 '21 at 20:19
8

I prefer to use /etc/sudoers.d/ for this if possible (this is less risky, more modular and self-decriptive), so this approach looks like:

$ cat files/*
%admins ALL=(ALL) NOPASSWD: ALL

$ cat tasks/*
- name: sudoers | Create sudoers.d files
  copy:
    src: ./
    dest: /etc/sudoers.d
    owner: root
    group: root
    mode: ug+rwX,o=
    force: yes

File are pre-checked with visudo -cf file_name.

dess
  • 223
  • 4
  • 8
  • You are missing the filename in `src: `. Also, `force: yes` is superfluous as this is the default. Perhaps it makes sense to be more explicit about the destination as this would create `/etc/sudoers.d` as a file if it doesn't exist ... – maxschlepzig Aug 16 '20 at 11:05
  • Thank you for the note! Actually, I'm considering following: file name isn't missed, syntax means all contents of directory; `force` is used for module action specifics emphasis, could be omitted; if `dest:` is a non-existent path and `src:` is a directory (which is) `dest:` path (directory, not file) is created. – dess Aug 16 '20 at 21:43
  • 2
    Ok, `./` looked like an oversight. Having a subdirectory in your files directory - such that you could use - say - `src: sudoers.d` - arguably would make this pattern more obvious. – maxschlepzig Aug 17 '20 at 17:42
  • Personally I prefer `ansible.builtin.template` over `ansiblt.builtin.copy` or `ansible.builtin.file`. While we maybe don't need variables inside the file, this allows us to report accurately at the end of the playbook if something has changed or not. – Darren Felton Apr 13 '22 at 15:49