How to set environmental variables using Ansible - ansible

I need to set the variables like JAVA_HOME and update PATH. There are a number of ways of doing this. One way is to update the /etc/environment variable and include a line for JAVA_HOME using the lineinfile module and then run the command source /etc/environment directly on the guest OS (CentOS in my case).
Another way is to execute the export command e.g.
export JAVA_HOME=/usr/java/jre1.8.0_51
export PATH=$PATH:$JAVA_HOME
Is there a cleaner way to do this as all these require manipulating files and running commands directly on the OS to update the environment variables?

Yes, there is a cleaner way. You can set environment variables per task:
tasks:
- shell: echo JAVA_HOME is $JAVA_HOME
environment:
JAVA_HOME: /usr/java/jre1.8.0_51
register: shellout
- debug: var=shellout
Output:
TASK: [shell echo JAVA_HOME is $JAVA_HOME] **********************************
changed: [localhost]
TASK: [debug var=shellout] ****************************************************
ok: [localhost] => {
"var": {
"shellout": {
"changed": true,
"cmd": "echo JAVA_HOME is \"$JAVA_HOME\"",
"delta": "0:00:00.005797",
"end": "2015-08-07 06:32:47.295061",
"invocation": {
"module_args": "echo JAVA_HOME is \"$JAVA_HOME\"",
"module_name": "shell"
},
"rc": 0,
"start": "2015-08-07 06:32:47.289264",
"stderr": "",
"stdout": "JAVA_HOME is /usr/java/jre1.8.0_51",
"stdout_lines": [
"JAVA_HOME is /usr/java/jre1.8.0_51"
],
"warnings": []
}
}
}
If you set the environment variable like above in a task, it is only available for this specific task. In subsequent tasks it does not exist unless you define it again.
Though you can define env vars per play as well:
- hosts:
- localhost
gather_facts: no
environment:
JAVA_HOME: /usr/java/jre1.8.0_51
tasks:
...
Now it's gonna be available for all tasks of this play.
See Setting the Environment and FAQ: How can I set the PATH or any other environment variable for a task or entire playbook? in the docs.
Another example with a script task:
tasks:
- script: /tmp/script.sh
environment:
JAVA_HOME: /usr/java/jre1.8.0_51
register: shellout
- debug: var=shellout
Where the script simply has this content:
#!/bin/sh
echo JAVA_HOME is $JAVA_HOME

I found that a workaround to do this was to use the lineinfile command in Ansible:
- name: Set JAVA_HOME
lineinfile: dest=/etc/environment state=present regexp='^JAVA_HOME' >
line='JAVA_HOME=/opt/jre1.8.0_51/bin'
While this is not ideal, it allows you to create new environmental variables. Of course, you should use variables to construct your directory path. I have included the explicit path to simplify my example.

Update to the lineinfile approach. The JAVA_HOME value should not include the bin directory. The following worked for centos:
- name: Set JAVA_HOME
lineinfile:
dest: /etc/environment
state: present
regexp: '^JAVA_HOME'
line: 'JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk'

Related

How to use the 'export' command in Ansible playbook?

If I run the below command directly in terminal, kubectl is getting enabled. If I use the same command with shell module in Ansible playbook, its executing but its not doing its job of enabling the kubectl.
export KUBECONFIG="/etc/rancher/rke2/rke2.yaml" \
&& export PATH="$PATH:/usr/local/bin:/var/lib/rancher/rke2/bin"
Ansible playbook
---
- name: Copy installer
hosts: FIRST_SERVER
gather_facts: yes
ignore_unreachable: true
any_errors_fatal: true
tasks:
- name: Execute enable kubectl on primary server
when: inventory_hostname in groups['FIRST_SERVER']
shell: |
set -o pipefail
export KUBECONFIG="/etc/rancher/rke2/rke2.yaml"
export PATH="$PATH:/usr/local/bin:/var/lib/rancher/rke2/bin"
args:
executable: /bin/bash
become: yes
Please suggest.
Your example is setting remote environment variables for the task temporary only.
For certain servers I am the following approach of
What do the scripts in /etc/profile.d do?
by using
- name: Provide environment variable script file
template:
src: "{{ item }}.j2"
dest: "/etc/profile.d/{{ item }}"
with_items:
- "environment.sh"
and in example
# /etc/profile.d/environment.sh
export ACCOUNT=$(who am i | cut -d " " -f 1)
export DOMAIN=$(hostname | cut -d "." -f 2-4)
Further Q&A
"Scripts placed in ... get sourced on login"
How to set an environment variable during package installation?
By doing this I am able to set persistent environment variables for specific software and services.

Some environment variables are not visible in Ansible

I am trying to convert an existing shell script into an Ansible role. In this role, I am reading two environment variables but Ansible does not display these even though it is available in the host. Can anyone please help me understand what I am doing wrong?
Note: I cannot hardcode env.sh into my Ansible role as each region will have its own settings.
/etc/synopsys/bin/env.sh:
#!/bin/sh
SITEID="us01-savvis"
MYGLOBAL="/remote/kickstart"
export SITEID MYGLOBAL
Ansible code:
---
- name: Gather Facts
setup:
gather_subset:
- '!all'
- '!any'
- facter
- network
- hardware
async: 300
poll: 20
- name: Check if env.sh exists
stat:
path: /etc/synopsys/bin/env.sh
register: stat_result
- name: Source env.sh file if it exists
shell: "source /etc/synopsys/bin/env.sh"
when: stat_result.stat.exists == True
- name: Printing all the environment​ variables in Ansible
debug:
# msg: "{{ ansible_env }}"
msg: "{{ lookup('env','SITEID','MYGLOBAL','HOME','SHELL') }}"
Ansible output (Note that SITEID and MYGLOBAL are not visible):
TASK [common/run_pkg_checker/v1 : Printing all the environment​ variables in Ansible] *************************************************
ok: [ansible-poc-cos6] => {
"msg": ",,/u/subburat,/usr/local/bin/tcsh"
}
Linux environment variables (SITEID and MYGLOBAL defined):
[root#ansible-poc-cos6 ~]# env |grep MYGLOBAL
MYGLOBAL=/remote/kickstart
[root#ansible-poc-cos6 ~]# env |grep SITEID
SITEID=us01-savvis
First, each Ansible tasks running inside a separate sub-process.
Second, sub-processes can't have effects on their parent processes.
So, for the task which runs source env.sh command, it really does nothing to the Ansible process and the following tasks.
For your question, you can run the source env.sh command first before running ansible command.
Or using ansible --extra-vars or -e option to avoid hard-coding values in your playbook.
source /etc/synopsys/bin/env.sh
ansible-playbook your-playbook.yml
# OR
ansible-playbook -e SITEID=xxx -e MYGLOBAL=yyy your-playbook.yml

Using env variable in ansible_python_interpreter variable

How do I use ${USER} in ansible_python_interpreter variable?
In a deploy.yml I have several tasks and first task installs python into /local/${USER}/venv. Follow up task should use installed python not the one from my own env.
I tried different combinations, those did not work.
deploy.yml
- name: use installed python
host: localhost
vars_files:
- settings.yml
vars:
# commented items did not work neither here nor in settings.yml
# ansible_python_interpreter: "/local/{{lookup('env', 'USER')}}/venv/bin/python"
# ansible_python_interpreter: "/local/${USER}/venv/bin/python"
# venv_dir defined in settings.yml
# ansible_python_interpreter: "{{venv_dir}}/bin/python"
# hardcoded worked:
ansible_python_interpreter: "/local/myuser/bin/python"
The error something like:
"/bin/sh: {{venv_dir}}/bin/python: No such file or directory\n"
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "module_stderr": "/bin/sh: {{venv_dir}}/bin/python: No such file or directory\n", "module_stdout": "", "msg": "MODULE FAILURE"}
settings.yml:
---
root_dir: "/local/{{lookup('env', 'USER')}}"
venv_dir: "{{root_dir}}/venv/"
Note: moving ansible_python_interpreter into settings.yml does not help.
Am I missing something?
I guess ansible_python_interpreter is not templated under the hood.
You can use set_fact workaround:
---
- hosts: localhost
gather_facts: false
tasks:
# default Python here
- shell: echo hello
- set_fact:
ansible_python_interpreter: /local/{{lookup('env', 'USER')}}/venv/bin/python
# modified Python here
- shell: echo hello
Globally, use the interpreter_python key in the [defaults] section of ansible.cfg
interpreter_python=/usr/bin/python2.7

Add binaries to PATH with Ansible

I'm trying to install the Kiex Version manager for the Elixir programming language using Ansible.
These are the plays I use for this:
- name: Kiex Installation
hosts: web
gather_facts: false
remote_user: deployer
tasks:
- shell: \curl -sSL https://raw.githubusercontent.com/taylor/kiex/master/install | bash -s
- name: Add Kiex Bin to Path
lineinfile:
dest: /home/deployer/.bashrc
regexp: '^test -s'
line: '[[ -s "$HOME/.kiex/scripts/kiex" ]] && source "$HOME/.kiex/scripts/kiex"'
- name: Reload Path
shell: source /home/deployer/.bashrc
args:
executable: /bin/bash
- shell: echo $PATH
register: pathul
- debug:
var: pathul
- name: Elixir Installation
hosts: web
gather_facts: false
remote_user: deployer
tasks:
- shell: echo $PATH
register: pathul
- debug:
var: pathul
- name: Install Elixir Version
command: /home/deployer/.kiex/bin/kiex list
args:
executable: /bin/bash
chdir: /home/deployer/
- name: Set Elixir Version as Default
shell: kiex default 1.4
The Installation of Kiex is a success and if I log in to the remote Machine I am able to run it simply by using the kiex command. I can do this because I sourced the binaries in "~/.kiex/scripts/kiex". When I echo the $PATH variable it shows the kiex binaries path /home/deployer/.kiex/bin in it:
$ echo $PATH
/home/deployer/.kiex/bin:/home/deployer/.kiex/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
However the kiex, kiex list and even the /home/deoployer/.kiex/bin/kiex list in the Elixir Installation Play shown above fail with the message:
TASK [Set Elixir Version as Default] *******************************************
fatal: [local-web-2]: FAILED! => {"changed": true, "cmd": "kiex default 1.4", "delta": "0:00:00.002042", "end": "2017-01-26 22:13:32.898082", "failed": true, "rc": 127, "start": "2017-01-26 22:13:32.896040", "stderr": "/bin/sh: 1: kiex: not found", "stdout": "", "stdout_lines": [], "warnings": []}
Also the pathul variable that registered the result of echoing the path via ansible doesn't contain /home/deployer/.kiex/bin:
"stdout": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
How can I make the kiex command work properly via Ansible?
Just use the full, absolute path, like you tried in the Install Elixir Version task, but mind that you have a typo, both, in the example and in the explanation you posted:
command: /home/deoployer/.kiex/bin/kiex list
[ ] even the /home/deoployer/.kiex/bin/kiex list [ ] fail[s]
It should likely be deployer, like in the first play, not deoployer.
There is no reason otherwise for Ansible to fail with "kiex: not found" message, if you provide the correct path.
Explanations regarding other tasks:
Quoting man bash:
When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists.
So your ~/.bashrc is not even read when you execute tasks with Ansible, because it's not an interactive session.
This is for example why your pathul variable does not contain changes applied in the ~/.bashrc.
The following two tasks run separate bash processes. The environment sourced in the first task has no influence on the environment of the second:
- name: Reload Path
shell: source /home/deployer/.bashrc
args:
executable: /bin/bash
- shell: echo $PATH
register: pathul

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.

Resources