I'm trying to install no-ip's Dynamic Update Client. I'm using the expect module to answer every prompt, but each time the script ask for my password, it adds another character and does not enter the password (the command exceeded timeout).
Here is my task (noip_email and noip_password are variables from a vault. I tried printing them and they are correct).
- name: DNS - install no-ip DUC
ansible.builtin.expect:
command: "make install"
chdir: /usr/local/src/noip
echo: true
responses:
"By typing the number associated with it": "0"
"login/email": "{{ noip_email }}"
"password": "{{ noip_password }}"
"update interval": "20"
"run something": "n"
I tried to simplify the matching word; using the full sentence; not using variables (from a vault); I tried not to match output (see the second example in ansible's doc), tried a bigger timeout, and also tried adding manual "enter" with \n at the end of the prompt.
None of the solution I tried or found on the internet worked, and I ran out of ideas. I really don't know why it adds another character to the password input, and why it doesn't enter (\n) my password, especially when every other prompt is correct and entered.
System:
Ansible 2.13.5
Python 3.10
Ubuntu 22.04, Raspberry Pi 4 8Gb
I've solved my issue by using the expect binary with the shell module.
- name: DNS - install no-ip DUC
ansible.builtin.shell: |
set timeout 30
spawn make install
expect "number associated"
send "0\n"
expect "email"
send "{{ noip_email }}\n"
expect "password"
send "{{ noip_password }}\n"
expect "update"
send "20\n"
expect "run"
send "n\n"
args:
executable: /usr/bin/expect
chdir: /usr/local/src/noip
Related
For a specific case I need to echo a random string returned by ansible's function password_hash that contains multiple random $ signs (encryption method bcrypt). The echoed string is then piped into the next command.
Ideally I would just escape the entire string. If that is not possible, I would like to escape all $ signs. How is that possible? If there is an ansible native command that automatically escapes a variable, that would be fine, too.
Full task if needed
- name: Update Admin password
shell: "echo \"update users set passwd={{ zabbix_conf.admin_password|password_hash('bcrypt') }} where username='Admin'\" | mysql --user={{ zabbix_conf.db_user}} --password={{ zabbix_conf.db_password }} zabbix"
Currently doesn't work as expected, because the $ signs affect the echo to be empty.
Ansible already has an inbuilt command for executing mysql queries called mysql_query. Therefore one can simply do:
- name: Update Admin password
mysql_query:
login_db: "{{ zabbix_conf.db }}"
login_user: "{{ zabbix_conf.db_user}}"
login_password: "{{ zabbix_conf.db_password }}"
query: "update users set passwd='{{ zabbix_conf.admin_password|password_hash('bcrypt') }}' where username='Admin'"
That way you don't need to escape anything and it looks much cleaner, too.
In case someone wants to reproduce this: Ansible requires the installation of passlib to password_hash bycrypt.
Code:
- expect:
command: virsh console myguest
responses:
'Escape character is': ''
'root>': 'show interfaces em0 | grep Current'
timeout: 5
register: result
This task will hang forever. I believe it was because of after finishing the show interface command, the system still get back to the 'root>' prompt for next command, and because of my 'root>' response provided, the prompt-response got into a dead loop.
I know ideally I could provided 2 responses as list to 'root>', one would be the show command, the other would be Ctrl-]. But there is no text expression of Ctrl-], I cannot really provide it.
So I set the timeout, hoped it would just loop up to 5 seconds. But that's not the case.
I set the single response as list item, but the task error out for next response.
So what I can do to run the show command in virsh console, and then get out of it, so my playbook can continue?
You can use \u001d to represent ^] (CTRL-]) and allow the expect call to either complete successfully, or timeout and fail.
Also, you can add ignore_errors: true to allow you to process the error rather than have Ansible fail immediately. For example:
- expect:
command: "virsh console {{ guest }}"
responses:
'Escape character is': ''
'root>':
- 'show interfaces em0 | grep Current'
- "\u001d"
register: expect_response
ignore_errors: true
- debug:
msg: "Expect failed with rc={{ expect_response.rc }}"
when: expect_response.failed
I am working on upgrading my machines to Ubuntu 16 from Ubuntu 14. I have around 200+ machines. I wanted to do this through ansible so that I can automate it instead of doing it manually. When I run below command for the upgrade, it asked me some questions on the screen and I always chose default answers for those and it worked for me.
Most of the time there were some questions on the pop up screen and I answered default for those and sometimes there were questions on the command line which I answered to default 'N' when running the upgrade.
do-release-upgrade
So now if I want to do this through ansible how can we deal with those questions that pops up during the upgrade? I always want to chose default answer for those questions but is there any way to deal with this through ansible? Below is what I got but not sure how to deal with those questions.
---
- hosts: upgrade_test
serial: "{{ num_serial }}"
tasks:
# Use a block to perform tasks conditionally—only if running Ubuntu 14.04.
- block:
- debug:
msg: 'This server is running Ubuntu 14.04 LTS and will be upgraded to 16.04 LTS.'
- name: Run do-release-upgrade.
command: do-release-upgrade
# Note: In Ansible 2.7+, use the reboot module instead.
- name: Reboot the server.
command: reboot
async: 0
poll: 0
- name: Wait for server to reboot.
wait_for:
host: "{{ ansible_ssh_host }}"
port: 22
state: started
connection: local
become: no
when: ansible_distribution == 'Ubuntu' and ansible_distribution_version == '14.04'
You have two options here:
Either you script all the questions with the expect module
- name: Run do-release-upgrade.
expect:
command: do-release-upgrade
responses:
Question:
- N
- N
- N
- ...
It would maybe be the safest solution because you really control what happen and you can even regex script that the question are what you expect with the second form of the module
- name: Run do-release-upgrade.
expect:
command: do-release-upgrade
responses:
Configuration file '/etc/pam.d/login'.*: N
Still, that forces you to either script all the question or be one hundred percent sure you always have the same amount of questions.
Or you can use the utility command yes
Which answers y per default but could answer whatever you want, passed as argument
Name
yes - output a string repeatedly until killed
Synopsis
yes [STRING]...
yes OPTION
Description
Repeatedly output a line with all specified STRING(s), or 'y'.
Source: https://linux.die.net/man/1/yes
Default usage of yes
$ yes
y
y
y
y
^C
Usage of yes with argument
$ yes N
N
N
N
N
^C
So, with that, you can change you task to
- name: Run do-release-upgrade.
shell: yes N | do-release-upgrade
Note: you need to change from the command module to the shell module, because the command module does not accept the usage of pipes (|)
The command(s) will not be processed through the shell, so variables like $HOME and operations like "<", ">", "|", ";" and "&" will not work. Use the shell module if you need these features.
Source: https://docs.ansible.com/ansible/latest/modules/command_module.html
Thanks in advance for any advice / help on this.
I have tried the "expect" and many iterations of the shell / command modules but neither offers what I (and I imagine others) want to do in this case. Reaching out to the wider group in hopes there is a solution I haven't found yet.
Our software has a shell command that prints out a list of files that it will modify and prompts the user to continue with a standard [y/n] prompt. Similar to what YUM would do if you were upgrading software, it spits out a bunch of output and waits for user input.
The following changes need to be made:
Create /home/XYZ-file
Enable and start the ABC service
Enable and start the DEF service
Allow? [y/N] n
SOMETIMES, depending on the files that it lists, we DON'T want to continue... Sometimes, we DO. so I want to be able to prompt my ansible user and give them the choice based on the list. I know it's annoying human intervention and not in the spirit of automation, but for this one step we're willing to forego things and have a human actually look at these files and make the decision.
Currently "expect" only matches preset output with preset user responses. I don't want to do this since I don't know what files will be presented to the user, so I can't use anything preset.
What I would like is to display all the output of the shell command and prompt the ansible user to decide, based on the output.
simple task to issue the command and register the output:
- name: Issue XYZ command
shell: xyz
register: xyz_output
- debug: var=xyz_output.stdout
The problem is that the shell command hangs in this case because ansible isn't able to:
display the output
&
prompt the ansible user to continue or not
any help greatly appreciated!
a sample yaml that you can take for reference, and improve as per your requirement.
---
- hosts: all
gather_facts: False
tasks:
- name: print
shell: cat inventory
register: fileout
- debug: var=fileout.stdout
- name: pause
pause: prompt='Confirm action by giving - yes/no:'
register: pause
- name: Ansible create file.
file:
path: "/home/ansible/vops.txt"
state: touch
mode: 0777
when: pause.user_input == 'yes'
- name: Ansible start service
service:
name: httpd
state: started
when: pause.user_input == 'yes'
i have used 'pause' module to pause the play and prompt for input, and used 'when' condition to compare the input result and procceed for action.
is there any mechanism that checks if the SSH/SUDO password is correct? When deploying a playbook across the whole environment, after putting in the wrong password, ansible runs on all hosts with the wrong password, it fails and my LDAP/AD account is locked out.
Since, as it turns out, Ansible does not seem to have this functionality, I decided to create a workaround myself:
in site.yml, I added a role that only runs on one server and has 1 or optionally 2 tasks in it. The first one checks if login itself works, the second one checks if sudo works.
- name: Check ssh password first
command: echo "ssh password correct"
changed_when: false
- name: Check sudo password first
command: echo "sudo password correct"
become: yes
changed_when: false
As a good workaround, I usually put this in site.yml:
- hosts: all
gather_facts: false
tasks:
- name: site.yml | Check if Password is correct
become: true
command: echo "PW is correct"
run_once: true
tags:
- always
That task will run always, no matter what tags you start the playbook with and will check if the ssh/sudo password works on one host before hammering all your servers with login requests.