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.
Related
Context: provisioning fresh new servers.
I would like to provision them just once especially the update part.
After launching the first playbook bootstrap.yml I made it leave a file in a folder for me to know that the playbook ran well.
So then in the same playbook I would have to add a condition above every task (which I did) to check for the existance of this file.
If file exists skip running the playbook against all those machines who have that file.
Problem: How do I add the condition to run the playbook only if the file isn't found? I don't want to add a "when:" statement for each of my own tasks I find it silly.
Question: Does anyone have a better solution that maybe solves this with a single line or a parameter in the inventory file that I haven't thought of?
edit:
This is how i check for the file
- name: Bootstrap check
find:
path: /home/bot/bootstrapped-ok
register: bootstrap
and then when condition would be:
when: bootstrap.matched == 0
so if file is not found run the entire playbook.
I think you may be over-complicating this slightly. Would it be accurate to say "I want to bail early on a playbook without error under a certain condition?"
If so, you want "end_host"
How do I exit Ansible play without error on a condition
Just begin with a check for the file, and end_host if it's found.
- name: Bootstrap check
stat:
path: /home/bot/bootstrapped-ok
register: bootstrap
- name: End the play if previous provisioning was successful
meta: end_host
when: bootstrap.stat.exists == True
- name: Confinue if bootstrap missing
debug:
msg: "Hello"
Relatively new to Ansible but I'm just wondering what the syntax looks like if I want to run a command on an ASA like show run | i opmanager and then print the output. I have put a pause in because after the output is printed I want it to wait before continuing.
I have an ASA I want to configure with the playbook to see if i can deploy new SNMPv3 credentials to whilst also removing an old set.
This task removes any existing ManageEngine config for SNMP
tasks:
- name: Show remainging opmanager config
asa_command:
commands: show run | i opmanager
register: ManageEngine
pause:
prompt: "Do you want to proceed? (yes/no)"
register: confirm
Regarding your question
I'm just wondering what the syntax looks like
you may have a look into the Ansible Collections documentation Run arbitrary commands on Cisco ASA devices, the documentation of debug_module to Print statements during execution and the pause_module to Pause playbook execution.
# This task removes any existing ManageEngine config for SNMP
tasks:
- name: Show remaining opmanager config
asa_command:
commands: show run | i opmanager
register: ManageEngine
- name: Show result
debug:
msg: "{{ ManageEngine }}"
- name: Pause until confirmation
pause:
prompt: "Do you want to proceed? (yes/no)"
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
This is the offending line where the error seems to be:
- name: Generate random password
vars:
jupyter_pwd: "{{ lookup('password', '/root/jupyter_password length=10') }}"
I'm trying to generate a random password and store it in the jupyter_pwd variable.
This is not how Ansible works. It's not like i.e. bash's variable expansion. In Ansible you need to use specified action (command) to do the task - in this case you need to use the task that would support executing shell commands or scripts. This middle layer is needed because Ansible can run your playbook on any target host, be it remote or local machine, with the same unaltered playbook. Therefore it needs to provide middle man for any task to be able to do that and this is what Ansible's actions are for.
In your case you most likely need command action:
- name: "Generate random password"
command: command-to-execute
register: jupyter_pwd
Where command-to-execute is binary or script on remote host you want to run. Whatever that command-to-execute returns is captured and made available as jupyter_pwd variable to the rest of your playbook.
See [docs] for more information.
If command action is too strict (which may be if you want to chain several shell tools), then shell action may be the way to go instead.
With the command module, if the command creates a file, you can check to see if that file exists. If it does it prevents the command executing again.
- command: touch ~/myfile
args:
creates: ~/myfile
However, if the command does not create a file then on a re-run it executes again.
To avoid a second execution, I create some random file on a change (notify) as follows:
- command: dothisonceonly # this does not create a file
args:
creates: ~/somefile
notify: done
then the handler:
- name: done
command: touch ~/somefile
This approach works but is a bit ugly. Can anyone shed let on best practice? Maybe setting some fact? Maybe a whole new approach?
It is a fact (in common language) that a command was run successfully on a specific target host, so the most appropriate would be to use local facts (in Ansible vernacular).
In your handler save the state as a JSON file under /etc/ansible/facts.d with copy module and content parameter.
It will be retrieved and accessible whenever you run a play against the host with a regular fact-gathering process.
Then you can control the tasks with when condition (you need to include the default filter for the situation when no fact exists yet).
Ideally with Ansible, check for the state that was changed by the first command rather than using a file as a proxy. The reason being that checking the actual state of something provides better immutability since it is tested on every pass.
If there is a reason not to use that approach. Then register the result of the command and use that instead of a notifier to trigger creation of the file.
- command: dothisonceonly # this does not create a file
creates: ~/somefile
register: result
- file:
path: ~/somefile
state: touch
when: result|succeeded
If you are curious to see what is happening here, add:
- debug: var=result
Be aware with notifiers that they are run at the end of the play. This means that if a notifier is triggered by a task but then then play fails to complete, the notifier will not be run. Conversely there are Ansible options which cause notifiers to run even when not triggered by tasks.
This is my actual implementation based on the answer by #techraf
- command: echo 'phew'
register: itworks
notify: Done itworks
when: ansible_local.itworks | d(0) == 0
and handler:
- name: Done itworks
copy:
content: '{{ itworks }}'
dest: /etc/ansible/facts.d/itworks.fact
Docs:
http://docs.ansible.com/ansible/playbooks_variables.html#local-facts-facts-d
Thanks #techraf this works great and persists facts.
EDIT
Applied default value logic in #techraf's comment.