When automating privileged operations with Ansible, we often need to handle interactive password prompts from both Ansible itself (via --ask-become-pass
) and external commands (via expect
). The fundamental problem arises when we want to reuse the same sudo password across both contexts without manual re-entry.
Many developers first try to access the password through environment variables:
- name: Debug environment
debug:
var: ansible_env
However, Ansible deliberately doesn't expose the become password in environment variables or facts for security reasons. The password only exists temporarily in memory during privilege escalation.
Here are three tested approaches to solve this:
1. Using ansible.builtin.pause with Register
- name: Capture become password
pause:
prompt: "Enter become password"
echo: no
register: become_pass
when: ansible_become_password is not defined
- name: Use password in expect
expect:
command: /bin/bash -c "/usr/bin/privileged_command"
responses:
Password: "{{ ansible_become_password | default(become_pass.user_input) }}"
2. Custom Plugin Approach
Create a custom lookup plugin (lookup_plugins/become_pass.py
):
from ansible.plugins.lookup import LookupBase
class LookupModule(LookupBase):
def run(self, terms, variables=None, **kwargs):
return [variables.get('ansible_become_password')]
Then in your playbook:
- name: Expect with become password
expect:
command: /usr/bin/some_command
responses:
Password: "{{ lookup('become_pass') }}"
3. Using ansible-vault with Prompt
For more security-conscious environments:
- name: Read vaulted password
ansible.builtin.set_fact:
sudo_pass: "{{ lookup('ansible.builtin.prompt', 'BECOME password', private=True) }}"
- name: Use in expect
expect:
command: /usr/bin/secure_command
responses:
Password: "{{ sudo_pass }}"
Always remember:
- Never store passwords in plaintext
- Use ansible-vault for sensitive variables
- Set appropriate permissions on any temporary files
- Consider using SSH certificates instead of passwords where possible
For long-term solutions, consider:
- name: Configure passwordless sudo
lineinfile:
path: /etc/sudoers
line: "%wheel ALL=(ALL) NOPASSWD: ALL"
validate: 'visudo -cf %s'
Or implement proper SSH certificate authentication to eliminate password prompts entirely.
When automating privileged operations with Ansible, we often need the sudo password in two contexts: for Ansible's own privilege escalation (--ask-become-pass
) and within expect
scripts that interact with privilege prompts from target systems. The challenge lies in accessing the initially entered password programmatically.
Traditional methods like become_user
or vars_prompt
don't solve this because:
- Ansible intentionally doesn't expose the prompted password as a variable
- The password is only stored in memory during execution
- Security best practices prevent password exposure
Here are three viable approaches with example implementations:
Method 1: Environment Variable Passing
# In your shell before running ansible:
export BECOME_PASS="secretpassword"
ansible-playbook --become --ask-become-pass -e "ansible_become_pass=$BECOME_PASS" playbook.yml
# In your playbook:
- name: Use password in expect
expect:
command: /bin/bash -c "/usr/bin/my_command"
responses:
Password: "{{ ansible_become_pass }}"
Method 2: Temporary Password File
# Create a secure temp file (ensure proper permissions)
echo "secretpassword" > /tmp/ansible_become_pass
chmod 600 /tmp/ansible_become_pass
# In playbook:
- name: Read password file
ansible.builtin.slurp:
src: /tmp/ansible_become_pass
register: password_file
no_log: true
- name: Use password in expect
expect:
command: /bin/bash -c "/usr/bin/my_command"
responses:
Password: "{{ password_file.content | b64decode | trim }}"
Method 3: Custom Password Plugin
Create a custom password plugin (plugins/lookup/become_pass.py
):
from ansible.plugins.lookup import LookupBase
from ansible.utils.display import Display
display = Display()
class LookupModule(LookupBase):
def run(self, terms, variables, **kwargs):
return [variables.get('ansible_become_pass', '')]
Then use it in your playbook:
vars:
become_pass: "{{ lookup('become_pass') }}"
tasks:
- name: Use looked up password
expect:
command: /bin/bash -c "/usr/bin/my_command"
responses:
Password: "{{ become_pass }}"
- Always use
no_log: true
for tasks handling passwords - Set restrictive permissions on any password files
- Prefer Method 1 (environment variables) for most use cases
- Never commit passwords to version control
If possible, consider these more secure alternatives:
- Configure passwordless sudo for specific commands
- Use SSH certificate authentication
- Implement privilege escalation through Ansible's become system exclusively