Can I modify Ansible 'remote_tmp' on a per task basis? - ansible

I am running some tasks with delegate_to. When those tasks run, they use the same remote_tmp directory setup from the ansible.cfg.
However, due to permissions and directory size issues, I need to change the remote_tmp directory for tasks that I am using delegate_to for.
Example:
- name: Create Temp Dir
delegate_to: localhost
file:
path: "{{ my_temp_dir }}"
state: directory
I am using Ansible 2.3.2.

As of 2.5, yes, changing remote_tmp, which is an ansible_* variable is something possible, by adding it to the vars of the said task.
For example:
- tempfile:
state: file
suffix: temp
vars:
ansible_remote_tmp: /tmp/temp_file_1_task
Given a playbook with the two tasks:
- tempfile:
state: file
suffix: temp
vars:
ansible_remote_tmp: /tmp/temp_file_1_task
- tempfile:
state: file
suffix: temp
vars:
ansible_remote_tmp: /tmp/temp_file_2_task
A reduced version of the extra verbose log would yield:
<127.0.0.1> EXEC /bin/sh -c '/usr/local/bin/python /tmp/temp_file_1_task/ansible-tmp-1642453716.1902952-890-119314712082250/AnsiballZ_tempfile.py && sleep 0'
and
<127.0.0.1> EXEC /bin/sh -c '/usr/local/bin/python /tmp/temp_file_2_task/ansible-tmp-1642453716.6039782-905-172576654053914/AnsiballZ_tempfile.py && sleep 0'

Related

Reusing environment variables between tasks in Ansible

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

Skip task if user can't sudo

I am trying to run a playbook with these tasks on a few thousand servers
- name: Check root login config
shell: "egrep -i '^PermitRootLogin' /etc/ssh/sshd_config|awk '{print $2}'"
register: config_value
async : 3
become: yes
poll: 1
- name: "config value"
debug: msg="{{ inventory_hostname }} - {{ config_value.stdout }}"
They have slightly varied configs but this should work on most of them. While running it ,ansible gets stuck somewhere in the middle on some hosts where my user doesn't have passwordless sudo or sudo privileges.
I want to skip those servers where this doesn't work.Is there a way to do that ?
ansible-playbook -i hosts playbook.yml --ask-become-pass
I tried giving a wrong password too ,but it still hangs.
Ansible continues with the rest of the hosts if one task fails on one or more hosts. You could use that behaviour by provoking it before the actual tasks. Don't set become in the playbook, do this instead:
- name: Ping or fail host
become: true
ping:
- name: Check root login config
become: true
shell: "egrep -i '^PermitRootLogin' /etc/ssh/sshd_config|awk '{print $2}'"
register: config_value
async : 3
become: yes
poll: 1
- name: "config value"
debug: msg="{{ inventory_hostname }} - {{ config_value.stdout }}"

Ansible copy file using command syntax

The assignment is as follows:
Lets create a file touch afile.txt, prior to creating tasks
Create a playbook test.yml to
copy afile.txt from your control machine to host machine at /home/ubuntu/ location as afile_copy.txt and
debug the above task to display the returned value
Execute your playbook (test.yml) and observe the output
I did following
Created the afile_copy.txt using touch
created the playbook as follows:
- name: copy files
hosts: all
tasks:
- name: copy file
command: cp afile.txt /home/ubuntu/afile_copy.txt
register:output
- debug: var=output
When I run the playbook using the command
ansible-playbook -i myhosts test.yml
it fails with the error message
stderr: cp: cannot stat 'afile.txt' : no such file or directory
The afile.txt is present in directory /home/scrapbook/tutorial
You should use copy module instead of command module. command module executes on the remote node.
1)first execute the ad-hoc command for copy:
ansible all -i myhosts -m copy -a "src=afile.txt dest=/home/ubuntu/"
2) After execute the above command,execute this playbpook:
hosts: all
tasks:
stat: path=/home/ubuntu/afile_copy.txt
register: st
name: rename
command: mv afile.txt /home/ubuntu/afile_copy.txt
when: not st.stat.exists
register: output
debug: var=output
Copy module should be used instead of command module
- name: copy files
hosts: all
tasks:
- name: copy file
copy: src=afile.txt dest=/home/ubuntu/afile_copy.txt
register:output
- debug: var=output
---
- name: copy files
hosts: all
tasks:
- name: copy file
copy:
src: afile.txt
dest: /home/ubuntu/afile_copy.txt
register: output
- debug: var=output

How to permanently set environment variable?

Host is Ubuntu 16.04
I'm trying to set environment variable for user, with:
- hosts: all
remote_user: user1
tasks:
- name: Adding the path in the bashrc files
lineinfile: dest=/home/user1/.bashrc line='export MY_VAR=TEST' insertafter='EOF' state=present
- name: Source the bashrc file
shell: . /home/user1/.bashrc
- debug: msg={{lookup('env','MY_VAR')}}
Unfortunately it outputs:
TASK [debug] *******************************************************************
ok: [xxxxx.xxx] => {
"msg": ""
}
How can I export variable so next time I run some tasks on this machine I can use {{ lookup('env', 'MY_VAR') }} to get value of this variable?
Because lookups happen locally, and because each task runs in it's own process, you need to do something a bit different.
- hosts: all
remote_user: user1
tasks:
- name: Adding the path in the bashrc files
lineinfile: dest=/home/user1/.bashrc line='export MY_VAR=TEST' insertafter='EOF' state=present
- shell: . /home/user1/.bashrc && echo $MY_VAR
args:
executable: /bin/bash
register: myvar
- debug: var=myvar.stdout
In this example I am sourcing the .bashrc and checking the var in the same command, and storing the value with register
All lookups in Ansible are local. See documentation for details:
Note
Lookups occur on the local computer, not on the remote computer.

Ansible writing output from multiple task to a single file

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.

Resources