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?

1
  • link produces 404 error, is it possible to re-update this?
    – getleft
    Commented Sep 14, 2023 at 8:28

2 Answers 2

82

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 }}"
5
  • 31
    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
    Commented Dec 18, 2016 at 22:25
  • Note that the validate command will probably need the full path to visudo to work. Commented May 24, 2018 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.) Commented Nov 22, 2019 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.
    – AdamKalisz
    Commented Apr 2, 2020 at 9: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. Commented Jun 20, 2021 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.

4
  • 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 ... Commented Aug 16, 2020 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
    Commented Aug 16, 2020 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. Commented Aug 17, 2020 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. Commented Apr 13, 2022 at 15:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.