Loading global environment variables in an Ansible task - bash

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

Related

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

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"

How to export or convert Ansible playbook to a Bash script?

I need to export a rather long Ansible playbook to a Bash script.
The idea is to have a bash script that runs the same commands that Ansible would run from the playbook if the playbook was executed, with all Ansible variables properly resolved.
I need this because I want to run this playbook's commands sequence in an environment where Ansible does not exist or is not used.

ansible script to set gradle home?

How to set gradle home using ansible script. I have a directory gradle_user_home in RHEL. I'm trying to set gradle home to this directory with ansible script. But ansible script says no such file or directory! Below is my script.
- name: Set gradle environment variable
command: export GRADLE_USER_HOME={{gradle_home}}
My vars file:
gradle_home: "/data1/deployment/gradle_user_home"
First of all, export is not a command (ie there is no /bin/export executable). It is a bash built-in. That's why you get a no such file or directory. You can use it in a shell task, not in a command task.
Secondly, this is not the proper way to set environment variables with Ansible.
You can set environment at the play level like this:
---
- hosts: all
environment:
GRADLE_USER_HOME: "{{gradle_home}}"
Or at the task level like this:
- name: Execute gradle build
shell: gradle build
environment:
GRADLE_USER_HOME: "{{gradle_home}}"
You can not export an environment variable in a single command task, because each command task is run in its own shell. After the command finishes the environment variable is lost.
You have to use the shell action to
set the environment variable and
run the command.
Example:
- name: Run the command
shell: >-
export GRADLE_USER_HOME={{gradle_home}}
my_command_which_requires_gradle_user_home

Ansible doesn't load ~/.profile

I'm asking myself why Ansible doesn't source ~/.profile file before execute template module on one host ?
Distant host ~/.profile:
export ENV_VAR=/usr/users/toto
A single Ansible task:
- template: src=file1.template dest={{ ansible_env.ENV_VAR }}/file1
Ansible fail with:
fatal: [distant-host] => One or more undefined variables: 'dict object' has no attribute 'ENV_VAR'
Ansible is not running remote tasks (command, shell, ...) in an interactive nor login shell. It's same like when you execute command remotely via 'ssh user#host "which python"'
To source ~/.bashrc won't work often because ansible shell is not interactive and ~/.bashrc implementation by default ignores non interactive shell (check its beginning).
The best solution for executing commands as user after its ssh interactive login I found is:
- hosts: all
tasks:
- name: source user profile file
#become: yes
#become_user: my_user # in case you want to become different user (make sure acl package is installed)
shell: bash -ilc 'which python' # example command which prints
register: which_python
- debug:
var: which_python
bash: '-i' means interactive shell, so .bashrc won't be ignored
'-l' means login shell which sources full user profile (/etc/profile and ~/.bash_profile, or ~/.profile - see bash manual page for more details)
Explanation of my example: my ~/.bashrc sets specific python from anaconda installed under that user.
Ansible is not running tasks in an interactive shell on the remote host. Michael DeHaan has answered this question on github some time ago:
The uber-basic description is ansible isn't really doing things through the shell, it's transferring modules and executing scripts that it transfers, not using a login shell.
i.e. Why does an SSH remote command get fewer environment variables then when run manually?
It's not a continous shell environment basically, nor is it logging in and typing commands and things.
You should see the same result (undefined variable) by running this:
ssh <host> echo $ENV_VAR
In a lot of places I've used below structure:
- name: Task Name
shell: ". /path/to/profile;command"
when ansible escalates the privilige to sudo it don't invoke the login shell of sudo user
we need to make changes in the way we call sudo like invoking it with -i and -H flags
"sudo_flags=-H" in your ansible.cfg file
If you can run as root, you can use runuser.
- shell: runuser -l '{{ install_user }}' -c "{{ cmd }}"
This effectively runs the command as install_user in a fresh login shell, as if you had used su - *install_user* (which loads the profile, though it might be .bash_profile and not .profile...) and then executed *cmd*.
I'd try not to run everything as root just so you can run it as someone else, though...
If you can modify the configuration of your target host and don't want to change your ansible yaml code. You can try this:
add the variable ENV_VAR=/usr/users/toto into /etc/environment file rather than ~/.profile.
shell: "bash -l scala -version"
by using bash -l will allow ansible to load corresponding bash_profile.
bash: '-i' (interactive shell) won't allow the ansible to run other task.
add the variable ENV_VAR=/usr/users/toto into /etc/environment file rather than ~/.profile.
You really can use /etc/environment, but only if a variable has a static value. If we use variable which gets the value of another variable it doesn't work. For example, if we put this line to /etc/environment
XDG_RUNTIME_DIR=/run/user/$(id -u)
Ansible can see exactly XDG_RUNTIME_DIR=/run/user/$(id -u), not XDG_RUNTIME_DIR=/run/user/1012.
And if we put this line to ~/.bash_profile or ~/.bashrc:
export XDG_RUNTIME_DIR=/run/user/$(id -u)
User can see XDG_RUNTIME_DIR=/run/user/1012 (if user's id is 1012) when he works manually, but Ansible doesn't get variable XDG_RUNTIME_DIR at all.

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.

Resources