handle all the questions that is asked during Ubuntu upgrade through ansible? - ansible

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

Related

Ansible expect adds other character to response and does not enter it

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

Ansible logical AND with conditions not working [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 4 months ago.
Improve this question
I try to run task depending on two conditions using logical "and". For the first condition I created the following task:
- name: Check if configuration already exists, then skip next task
stat:
path: "{{openldap_config}}/cn=config"
register: is_configured
Then I crated the following task, which is working fine:
- name: add new basic-configuration
shell: /opt/symas/sbin/slapadd -n 0 -F {{openldap_config}} -l /home/{{ansible_user}}/config.ldif
args:
executable: /bin/bash
when: not is_configured.stat.exists
If a configuration file exists the task will be skipped. Then I have a few task where I use "group_names" like this on:
- name: generating deltasyncrepl LDIF for main DB
template:
src: main_db_repl.j2
dest: /home/{{ansible_user}}/main-db-repl.ldif
owner: "{{ansible_user}}"
group: "{{ansible_group}}"
mode: '660'
when: "'ldap_provider' in group_names"
That's also working. BUT now I would like to have a logical "and" for both conditions:
- name: add replication of cn=config to all provider
shell: /opt/symas/bin/ldapmodify -Y EXTERNAL -H ldapi:/// -f /home/{{ansible_user}}/repl_config.ldif
args:
executable: /bin/bash
when: not is_configured.stat.exists
and "'ldap_provider' in group_names"
This is not working. I tried different quoting but I could not find a a working solution. I try it with brackets too, also not working. Some how it must be possible to have a logical AND with a int- and a string- variable.
Thanks for any help
While you can manually stat the file and that works just fine, this is actually exactly the case that the creates parameter to the shell and command modules is designed to handle. You should also migrate to the cmd parameter (supported since Ansible 2.0) instead of using args, so that people reading your code don't have to understand that rarely-used syntax:
- name: Basic configuration for OpenLDAP
shell:
cmd: /opt/symas/sbin/slapadd -n 0 -F {{ openldap_config }} -l /home/{{ ansible_user }}/config.ldif
executable: /bin/bash
creates: "{{ openldap_config }}/cn=config"
register: openldap_config_result
While you didn't include your other attempts at solving this problem, generally the best way to apply multiple conditions to a task is to use a list, which will implicitly apply and between each condition in the list:
- name: Add replication of cn=config to all providers
shell:
cmd: /opt/symas/bin/ldapmodify -Y EXTERNAL -H ldapi:/// -f /home/{{ ansible_user }}/repl_config.ldif
executable: /bin/bash
when:
- openldap_config_result is changed
- "'ldap_provider' in group_names"
You can also do it as a single string, though you have to make sure your quoting is correct (which it isn't in your question):
when: openldap_config_result is changed
and 'ldap_provider' in group_names

Add Conditionals to Ansible Role for Idempotent Tasks Runs

I've been searching for quite some time and tried many variants and similar answers without success. Hopefully this is something simple I am missing.
Ansible 2.9.6
I am creating many playbooks that all share a large set of custom Roles for my clients. I want to keep all logic out of the playbooks, and place that logic in the roles themselves to allow maximum re-usability. Basically, I just want to add boilerplate playbooks for simple "Role" runs via tags, with some vars overrides here and there.
My problem is that I can't seem to make some roles idempotent by using conditions - the conditionals don't work. The error I get is:
fatal: [localhost]: FAILED! => {"msg": "The conditional check 'homebrew_base.rc != 0' failed.
The error was: error while evaluating conditional (homebrew_bash.rc != 0): 'homebrew_bash' is undefined
The error appears to be in '/Users/eric/code/client1/provisioning/roles/bash/tasks/main.yaml': line 12, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Change shell (chsh) for macOS to homebrew managed version
^ here
"}
Below is the boilerplate code for my playbooks:
# ./playbook.yaml
---
- name: Provisioning
hosts: localhost
connection: local
pre_tasks:
- include_vars: "{{ item }}"
with_fileglob:
- "{{ playbook_dir }}/vars/global.yaml"
tags: always
tasks:
- name: Use homebrew bash binary
include_role:
name: bash
tags: bash
The above is truncated quite a bit, but only thing missing are additional var files and a whole bunch of include_roles.
Below is my role file in the entirely though. They are largely untested because of the error I keep getting.
# ./roles/bash/tasks/main.yaml
---
- name: Check /etc/shells contains "/usr/local/bin/bash"
command: grep -Fxq "/usr/local/bin/bash" /etc/shells
register: homebrew_bash
ignore_errors: True
changed_when:
- homebrew_bash.rc != 0
- name: Check that homebrew installed /usr/local/bin/bash
stat:
path: /usr/local/bin/bash
register: homebrew_bash_binary
- name: Change shell (chsh) for macOS to homebrew managed versoin
tags: bash, chsh
shell: chsh -s /usr/local/bin/bash
become: yes
when:
- homebrew_bash.rc != 0
- homebrew_bash_binary.stat.exists = True
Ps: I do plan on abstracting those hardcoded paths into roles/bash/defaults/. But I need it working first before that.
Ps2: If there is a better way to use a contains filter (instead of the grep hack), I'm all ears.
I've tried:
making separate tasks/chsh.yaml, and using the include: command to call that task within the role. When I do this, I get an odd error telling me the variable is undefined - in the tasks/chsh.yaml - even though I am checking for the variable in tasks/main.yaml! That doesn't seem right.
using quotes in various places in the conditions
commenting out each condition: both give the same error, just differenet names.
Again, I am trying to keep this logic in the roles only - not in the playbook.
Thanks!
Figured it out. I was missing the "tags" on the conditionals!
# ./roles/bash/tasks/main.yaml
---
- name: Check /etc/shells contains "/usr/local/bin/bash"
tags: bash, chsh
command: grep -Fxq "/usr/local/bin/bash" /etc/shells
register: homebrew_bash
ignore_errors: True
changed_when:
- homebrew_bash.rc != 0
- name: Check that homebrew installed /usr/local/bin/bash
tags: bash, chsh
stat:
path: /usr/local/bin/bash
register: homebrew_bash_binary
- name: Change shell (chsh) for macOS to homebrew managed versoin
tags: bash, chsh
shell: chsh -s /usr/local/bin/bash
become: yes
when:
- homebrew_bash.rc != 0
- homebrew_bash_binary.stat.exists = True

Ansible interactively respond to non preset remote shell output

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.

ANSIBLE - Answer yes no to questions

I am working on a playbook that will upgrade an app I have. Part of that upgrade means that I get asked a question.
Ideally I would like to be able to answer yes / no to these questions and not have to do any sort of pause or prompt for user input.
From what I have read online it is definitely possible but I cannot seem to get it to work for me.
Here is my code:
- name: Upgrade
expect:
command: /tmp/bin/update_script.sh
environment:
JAVA_HOME: /opt/java/
responses:
Question:
- Do you want to use the standard cipher suites [N]: n
You are mixing your response types. If you provide a list, it doesn't want/need the question. If you want to specifically match questions to responses, use a regex to match the question as the key of a dictionary.
So, version 1:
- name: Upgrade
expect:
command: /tmp/bin/update_script.sh
environment:
JAVA_HOME: /opt/java/
responses:
Question:
- n
or version 2 (check my regex*):
- name: Upgrade
expect:
command: /tmp/bin/update_script.sh
environment:
JAVA_HOME: /opt/java/
responses:
Question:
cipher: n
I have used expect, but not the ansible module, and it was years ago, and I never used it much. Hopefully this will work as-is either way, but be prepared to twiddle with it. ;)
Good luck!
This done what i needed .. Unfortunately it does not allow you answer yes / no but it gets the job done for now.
It would have been nice to have more control but its works as a temp workaround for now .. When I have a permanent solution I will post again.
- name: Upgrade Starting
shell: |
yes | /tmp/bin/update_script.sh
You should just pass the answer to the question as a variable using extra_variables. This will avoid the prompt and allow you to set the variable to some default value as well.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#passing-variables-on-the-command-line
ansible-playbook playbooks/release.yml --extra-vars 'updateciphers=True updatedcipherslist=["cipher1","cipher2","cipher3"]'
-> You may need to escape the double quotes.
vars:
updateciphers: False
tasks:
- name: Upgrade
command: /tmp/bin/update_script.sh
environment:
JAVA_HOME: /opt/java/
when: updateciphers
with_items: "{{ updatedcipherslist }}"

Resources