Running specific commands with sudo using Ansible

It seems a trivial use case, but it’s not. Consider a situation when you’re not the user allowed a full sudo access on the remote machine. However, you are only allowed the sudo access on certain commands, which, of course, requires a password. For example, starting or stopping a service.

Ansible allows privilege escalation, but that will not solve your problem because:

  1. Elevated access is changed to the user root by default
  2. Elevated access requires a password, i.e. password for the root user

You obviously don’t have access to the root user.

There are a couple of other directives you can use, which the ansible doc makes it hard to understand even though it tries to be simple in language. You can try to use the ansible_become_user to change to another user while escalating the privilege besides root. However, if you are already the user who has the sudo command access, this will not help.

If you have access to change (or get changed) the /etc/sudoers file, you can change the sudo command to be run through NOPASSWD.

If you are here, it is likely that you do not have the permissions to change the sudoers file either.


Resolution

I saw a couple of workarounds mentioned across Github1, StackOverflow2, and Reddit34, but they were outdated, and didn’t work for me.

One of the solution5 suggests to use sudo with -S and echo the password from the terminal. This will, however, probably leak your password on the shell, and we don’t want this, do we?

However, is there a way Ansible can input the password to sudo? Yes! Perhaps we can use something like the -y flag that is often used to skip prompts of yes/no on the terminal. Thankfully, Ansible is built over Python, and most Python modules are available within Ansible. “How does that help?”, you ask. Python has a very helpful module called pexpect. Similarly, Ansible has expect.

- name: Stop service
  ansible.builtin.expect:
    command: "sudo systemctl stop service"
    responses:
      (?i)password: "{{ pass }}"
  no_log: true

This solves our problem! We can load this password through Ansible vault, and pass it in without being logged anywhere! We also do not have to rely on having access or permissions through the /etc/sudoers file, saving a lot of headache of trying to get approvals.