I want to execute some script on remote via Ansible and get result file from remote to host.
I wrote a playbook like below:
---
- name : script deploy
hosts: all
vars:
timestamp: "{{ lookup('pipe', 'date +%Y%m%d%H%M%S') }}"
become: true
tasks:
- name: script deployment
script: ./exe.sh {{ansible_nodename}}_{{ timestamp }}
args:
chdir: /tmp
exe.sh successfully executed on remote and redirect result to output file like remote_20170806065817.data
Script execution takes a few seconds, and I tried to fetch result file after execution done.
But {{timestamp}} is re-evaluated and changed when I fetch it.
So fetch cannot find script-execution result file name.
What I want is assign immutable (constant) value in my playbook.
Is there any workaround?
Ansible uses lazy evaluation, so variables are evaluated at the moment of their use.
You should set the fact, which will be evaluated once:
---
- name : script deploy
hosts: all
become: true
tasks:
- set_fact:
timestamp: "{{ lookup('pipe', 'date +%Y%m%d%H%M%S') }}"
- name: script deployment
script: ./exe.sh {{ansible_nodename}}_{{ timestamp }}
args:
chdir: /tmp
Related
I'm running a few tasks in a playbook which runs a bash script and registers its output:
playbook.yml:
- name: Compare FOO to BAZ
shell: . script.sh
register: output
- name: Print the generated output
debug:
msg: "The output is {{ output }}"
- include: Run if BAZ is true
when: output.stdout == "true"
script.sh:
#!/bin/bash
FOO=$(curl example.com/file.txt)
BAR=$(cat file2.txt)
if [ $FOO == $BAR ]; then
export BAZ=true
else
export BAZ=false
fi
What happens is that Ansible registers the output of FOO=$(curl example.com/file.txt) instead of export BAZ.
Is there a way to register BAZ instead of FOO?
I tried running another task that would get the exported value:
- name: Register value of BAZ
shell: echo $BAZ
register: output
But then I realized that every task opens a separate shell on the remote host and doesn't have access to the variables that were exported in previous steps.
Is there any other way to register the right output as a variable?
I've come up with a workaround, but there must be an other way to do this...
I added a line in script.sh and cat the file in a seperate task
script.sh:
...
echo $BAZ > ~/baz.txt
then in the playbook.yml:
- name: Check value of BAZ
shell: cat ~/baz.txt
register: output
This looks a bit like using a hammer to drive a screw... or a screwdriver to plant a nail. Decide if you want to use nails or screws then use the appropriate tool.
Your question misses quite a few details so I hope my answer wont be too far from your requirements. Meanwhile here is an (untested and quite generic) example using ansible to compare your files and run a task based on the result:
- name: compare files and run task (or not...)
hosts: my_group
vars:
reference_url: https://example.com/file.txt
compared_file_path: /path/on/target/to/file2.txt
# Next var will only be defined when the two tasks below have run
file_matches: "{{ reference.content == (compared.content | b64decode) }}"
tasks:
- name: Get reference once for all hosts in play
uri:
url: "{{ reference_url }}"
return_content: true
register: reference
delegate_to: localhost
run_once: true
- name: slurp file to compare from each host in play
slurp:
path: "{{ compared_file_path }}"
register: compared
- name: run a task on each target host if compared is different
debug:
msg: "compared file is different"
when: not file_matches | bool
Just in case you would be doing all this just to check if a file needs to be updated, there's no need to bother: just download the file on the target. It will only be replaced if needed. You can even launch an action at the end of the playbook if (and only if) the file was actually updated on the target server.
- name: Update file from reference if needed
hosts: my_group
vars:
reference_url: https://example.com/file.txt
target_file_path: /path/on/target/to/file2.txt
tasks:
- name: Update file on target if needed and notify handler if changed
get_url:
url: "{{ reference_url }}"
dest: "{{ target_file_path }}"
notify: do_something_if_changed
handlers:
- name: do whatever task is needed if file was updated
debug:
msg: "file was updated: doing some work"
listen: do_something_if_changed
Some references to go further on above concepts:
uri module
get_url module
slurp module
delegating tasks in ansible
registering output of tasks
run_once
handlers
I need to get a shell output to a variable in set_fact in ansible. And this variable is referred in the somewhere in the same playbook but targeting a different set of hosts
But I am not sure how to do that.
This is my playbook:
---
- hosts: kube-master
tasks:
- name: Print the value
become: true
shell: |
echo "hi"
set_fact:
banuka: <value-taken-from-above-shell-command>
- hosts: kube-minions
tasks:
- name: Print the variable
become: true
shell: |
echo {{banuka}}
But I don't know these:
1. How to get the shell command output to the variable in set_fact and refer it somewhere
2. Is it possible to have multiple hosts (as shown in the above playbook)
I would do it the following way:
---
- hosts: master01
tasks:
- name: Print the value
shell: |
echo "hi"
register: somevariablename
- name: set fact
set_fact:
myvar: "{{ somevariablename.stdout }}"
- hosts: kube-minions
tasks:
- name: Print the variable
shell: |
echo "{{ hostvars['master01'].myvar }}"
It's important to understand, that set_fact only set a variable on the host which it's executed. Since you have a group kube-master it could be executed on each. I had the same issue in a playbook with Kubernetes-Masters and i used one of them as a "primary" e.q. master01. Then the echo hi will only be executed on that one.
In the second hostgroup, you must refer to the hostvars of your kube-master host like the master01.
Do you have the need to run over all the hosts in the group kube-master?
I have the below Ansible script which runs on localhost
- name: set env
shell: ". /tmp/testenv"
- name: get env
debug:
msg: "{{ lookup('env','TEST') }}"
In above script I'm trying to source the file and access the environment variables using the lookup. But it seems like environment variable is not set. Is there anyway I can get this to work?
The shell command is one process and the lookup looks in the environment of the Ansible process, which is a different one. You have to echo the environment variable in your shell command and register the result.
- hosts: localhost
connection: local
gather_facts: no
tasks:
- name: set env
shell: "echo 'export TEST=test' > /tmp/testenv"
- name: check env
shell: ". /tmp/testenv; echo $TEST"
register: result
- name: get env
debug:
msg: "{{ result.stdout }}"
I use Ansible to create and delete AWS launch configurations. I add the timestamp in the name.
My problem is that, I can create the LC, but when it comes to deletion, the timestamp changes and then the playbook of deletion can't find the LC to delete it.
this is how I use the timestamp variable:
I put this in a file called timestamp_lc.yml:
- set_fact: now="{{ lookup('pipe', 'date +%Y%m%d%H%M%S') }}"
and in the playbooks I call it:
- include: timestamp_lc.yml
How to make the variable now persistent so that ansible does not execute the date command every time I call the variable now ?
this is the creation task:
- name: Create launch configuration
sudo: yes
command: >
aws autoscaling create-launch-configuration
--region {{ asg.region }}
--launch-configuration-name "{{ asg.launch_configuration.name }}_{{ now }}"
The deletion task:
- name: Delete launch configuration
sudo: yes
command: >
aws autoscaling delete-launch-configuration
--region {{ asg.region }}
--launch-configuration-name {{ asg.launch_configuration.name }}_{{ now }}
That will happen with every execution of the ansible as you are getting the value from the date command and setting a fact of that and that keeps on updating with every iteration.
One way I can think is to save the value in a file in an extension on the target server or the local server -- I feel this would be more reliable
---
- name: test play
hosts: localhost
tasks:
- name: checking the file stats
stat:
path: stack_delete_info
register: delete_file_stat
- name: tt
debug:
var: delete_file_stat
- name: test
shell: echo "{{ lookup('pipe', 'date +%Y%m%d%H%M%S') }}" > stack_delete_info
when: delete_file_stat.stat.exists == false
In Ansible, I have written an Yaml playbook that takes list of host name and the executes command for each host. I have registered a variable for these task and at the end of executing a task I append output of each command to a single file.
But every time I try to append to my output file, only the last record is getting persisted.
---
- hosts: list_of_hosts
become_user: some user
vars:
output: []
tasks:
- name: some name
command: some command
register: output
failed_when: "'FAILED' in output"
- debug: msg="{{output | to_nice_json}}"
- local_action: copy content='{{output | to_nice_json}}' dest="/path/to/my/local/file"
I even tried to append using lineinfile using insertafter parameter yet was not successful.
Anything that I am missing?
You can try something like this:
- name: dummy
hosts: myhosts
serial: 1
tasks:
- name: create file
file:
dest: /tmp/foo
state: touch
delegate_to: localhost
- name: run cmd
shell: echo "{{ inventory_hostname }}"
register: op
- name: append
lineinfile:
dest: /tmp/foo
line: "{{ op }}"
insertafter: EOF
delegate_to: localhost
I have used serial: 1 as I am not sure if lineinfile tasks running in parallel will garble the output file.
Ansible doc recommend use copy:
- name: get jstack
shell: "/usr/lib/jvm/java/bin/jstack -l {{PID_JAVA_APP}}"
args:
executable: /bin/bash
register: jstackOut
- name: write jstack
copy:
content: "{{jstackOut.stdout}}"
dest: "tmp/jstack.txt"
If you want write local file, add this:
delegate_to: localhost
Why to complicate things ?
I did this like that and it worked:
ansible-playbook your_playbook.yml >> /file/you/want/to/redirect/output.txt
you can also try some parsing with grep or some other stuff with tee -a.