How to use environment variable for Ansible shell's creates? - ansible

I am using the built-in shell module, and want to use an environment variable inside the creates arg.
Something like the following:
- name: Run pyenv install of Python 3.10.7
shell:
cmd: |
exec $SHELL
pyenv install 3.10.7
# Is something like this possible?
creates: $PYENV_ROOT/versions/3.10.7
My questions are:
Is something like this possible?
Would it expand the environment variable from the Ansible control node or Ansible target?

Possible and the ansible way of doing this would be
- name: Run pyenv install of Python 3.10.7
shell:
cmd: |
exec "{{ ansible_env.SHELL }}"
pyenv install 3.10.7
creates: "{{ ansible_env.PYENV_ROOT }}/versions/3.10.7"
In this case, the variable will be from the managed node (target). Provided gather_facts is set to true.
To use the values from the control node, use lookup
creates: "{{ lookup('env','PYENV_ROOT') }}/versions/3.10.7"

Related

Whats the difference between ansible 'raw', 'shell' and 'command'?

What is the difference between raw, shell and command in the ansible playbook? And when to use which?
command: executes a remote command on the target host, in the same shell of other playbook's tasks.
It can be used for launch scripts (.sh) or for execute simple commands. For example:
- name: Cat a file
command: cat somefile.txt
- name: Execute a script
command: somescript.sh param1 param2
shell: executes a remote command on the target host, opening a new shell (/bin/sh).
It can be used if you want to execute more complex commands, for example, commands concatenated with pipes. For example:
- name: Look for something in a file
shell: cat somefile.txt | grep something
raw: executes low-level commands where the interpreter is missing on the target host, a common use case is for installing python. This module should not be used in all other cases (where command and shell are suggested)
Since I were I stumbling about the same question, I wanted to share my findings here too.
The command and shell module, as well gather_facts (annot.: setup.py) depend on a properly installed Python interpreter on the Remote Node(s). If that requirement isn't fulfilled one may experience errors were it isn't possible to execute
python <ansiblePython.py>
In a Debian 10 (Buster) minimal installation i.e., python3 was installed but the symlink to python missing.
To initialize the system correctly before applying all other roles, I've used an approach with the raw module
ansible/initSrv/main.yml
- hosts: "{{ target_hosts }}"
gather_facts: no # is necessary because setup.py depends on Python too
pre_tasks:
- name: "Make sure remote system is initialized correctly"
raw: 'ln -s /usr/bin/python3 /usr/bin/python'
register: set_symlink
failed_when: set_symlink.rc != 0 and set_symlink.rc != 1
which is doing something like
/bin/sh -c 'ln -s /usr/bin/python3 /usr/bin/python'
on the remote system.
Further Documentation
raw module – Executes a low-down and dirty command
A common case is installing python on a system without python installed by default.
... but not only restricted to that
Playbook Keyword - pre_tasks
A list of tasks to execute before roles.
Set the order of task execution in Ansible

Setting an environment variable in Ansible from a command output of bash command

I would like to set output of a shell command as an environment variable in Ansible.
I did the following to achieve it:
- name: Copy content of config.json into variable
shell: /bin/bash -l -c "cat /storage/config.json"
register: copy_config
tags: something
- name: set config
shell: "echo $TEMP_CONFIG"
environment:
TEMP_CONFIG: "{{copy_config}}"
tags: something
But somehow after the ansible run, when I do run the following command:
echo ${TEMP_CONFIG}
in my terminal it gives an empty result.
Any help would be appreciated.
There are at least two problems:
You should pass copy_config.stdout as a variable
- name: set config
shell: "echo $TEMP_CONFIG"
environment:
TEMP_CONFIG: "{{copy_config.stdout}}"
tags: something
You need to register the results of the above task and then again print the stdout, so:
- name: set config
shell: "echo $TEMP_CONFIG"
environment:
TEMP_CONFIG: "{{copy_config.stdout}}"
tags: something
register: shell_echo
- debug:
var: shell_echo.stdout
You never will be able to pass the variable to a non-related process this way. So unless you registered the results in an rc-file (like ~/.bash_profile which is sourced on interactive login if you use Bash) no other shell process would be able to see the value of TEMP_CONFIG. This is how system works.

Loading global environment variables in an Ansible task

I have several global environment variables set in /etc/environment on my target machine that I need to present when running some Ansible tasks. E.g.
MY_VAR=Some global variable
The value of these global variables are not known to Ansible so I can't use the environment functionality.
Example task:
- shell: echo MY_VAR is $MY_VAR
register: my_var
- debug: msg={{ my_var.stdout }}
The output I get is MY_VAR is where I would like it to be MY_VAR is Some global variable. I understand that this is happening because non-interactive Bash shells (which Ansible uses) don't load the environment from /etc/environment, but is there a way to execute my command in the context of that environment?
Note: I don't actually want to retrieve the value of the environment variable (as shown above), I just want it to be present in the context that I execute the shell task in.
Remote environment variables are available via facts using the the ansible_env variable.
This prints out all variables of the user Ansible logs into a remote host with:
- debug: var=ansible_env
If global environment variables not available to the Ansible user, they can be sourced in a task before running your command:
- shell: . /etc/environment && commandhere

Retrieve env variable of target node using ansible

I am trying to get env variale JAVA_HOME value of a target node using ansible.
- name: Copy JAVA_HOME location to variable
command: bash -c "echo $JAVA_HOME"
sudo: yes
register: java_loc
When i use, java_loc.stdout value in another task, it is showing blank value. How can i get that env variable and use it in another task?
I need to copy files to JAVA dir which is present in JAVA_HOME.
Ansible logins via SSH using non-login shell. Probably your problem is that you defined the environment variable on $HOME/.bash_profile or some other file that requires login shell, so you need to add the "-l" flag to "/bin/bash":
---
- name: Copy JAVA_HOME location to variable
command: /bin/bash -l -c "echo $JAVA_HOME"
sudo: no
register: java_loc
- name: show debug
debug: var=java_loc
Please, give it a try and let me know,
If your Java is sufficiently recent to have jrunscript, you can obtain the Java Path from your Java installation, avoiding any dependency on your environment. The code below does this, and doesn't fail if there's no jrunscript.
- name: Detemine Java home
command: 'jrunscript -e java.lang.System.out.println(java.lang.System.getProperty(\"java.home\"));'
changed_when: False
ignore_errors: True
register: locate_java_home
- name: Set a variable for Java home
set_fact:
java_home: "{{ (locate_java_home.rc == 0) | ternary(locate_java_home.stdout, '') }}"
The solution is based on the proposal made here.

Ansible: How to change active directory in Ansible Playbook?

- name: Go to the folder
command: chdir=/opt/tools/temp
When I run my playbook, I get:
TASK: [Go to the folder] *****************************
failed: [host] => {"failed": true, "rc": 256}
msg: no command given
Any help is much appreciated.
There's no concept of current directory in Ansible. You can specify current directory for specific task, like you did in your playbook. The only missing part was the actual command to execute. Try this:
- name: Go to the folder and execute command
command: chdir=/opt/tools/temp ls
This question was in the results for when I was trying to figure out why 'shell' was not respecting my chdir entries when I had to revert to Ansible 1.9. So I will be posting my solution.
I had
- name: task name
shell:
cmd: touch foobar
creates: foobar
chdir: /usr/lib/foobar
It worked with Ansible > 2, but for 1.9 I had to change it to.
- name: task name
shell: touch foobar
args:
creates: foobar
chdir: /usr/lib/foobar
Just wanted to share.
If you need a login console (like for bundler), then you have to do the command like this.
command: bash -lc "cd /path/to/folder && bundle install"
You can change into a directory before running a command with ansible with chdir.
Here's an example I just setup:
- name: Run a pipenv install
environment:
LANG: "en_GB.UTF-8"
command: "pipenv install --dev"
args:
chdir: "{{ dir }}/proj"

Resources