How to Set Linux Environment Variables With Ansible

How to set Linux environment variables with Ansible

There are multiple ways to do this and from your question it's nor clear what you need.

1. If you need environment variable to be defined PER TASK ONLY, you do this:

- hosts: dev
tasks:
- name: Echo my_env_var
shell: "echo $MY_ENV_VARIABLE"
environment:
MY_ENV_VARIABLE: whatever_value

- name: Echo my_env_var again
shell: "echo $MY_ENV_VARIABLE"

Note that MY_ENV_VARIABLE is available ONLY for the first task, environment does not set it permanently on your system.

TASK: [Echo my_env_var] ******************************************************* 
changed: [192.168.111.222] => {"changed": true, "cmd": "echo $MY_ENV_VARIABLE", ... "stdout": "whatever_value"}

TASK: [Echo my_env_var again] *************************************************
changed: [192.168.111.222] => {"changed": true, "cmd": "echo $MY_ENV_VARIABLE", ... "stdout": ""}

Hopefully soon using environment will also be possible on play level, not only task level as above.
There's currently a pull request open for this feature on Ansible's GitHub: https://github.com/ansible/ansible/pull/8651

UPDATE: It's now merged as of Jan 2, 2015.

2. If you want permanent environment variable + system wide / only for certain user

You should look into how you do it in your Linux distribution / shell, there are multiple places for that. For example in Ubuntu you define that in files like for example:

  • ~/.profile
  • /etc/environment
  • /etc/profile.d directory
  • ...

You will find Ubuntu docs about it here: https://help.ubuntu.com/community/EnvironmentVariables

After all for setting environment variable in ex. Ubuntu you can just use lineinfile module from Ansible and add desired line to certain file. Consult your OS docs to know where to add it to make it permanent.

Permanently set environment variables using Ansible

Thank you.

I wrote this ansible-playbook and it's working.

- hosts: localhost
become_user: root
tasks:

- name: Adding KUBECONFIG variable
delegate_to: localhost
copy:
content: export KUBECONFIG="/etc/config/admin.conf/admin.conf"
dest: /etc/profile.d/kubeconfig.sh

How to use Ansible to add Linux environment variables

The playbook below

shell> cat playbook.yml
- hosts: localhost
vars:
app_env_variables:
- key: APP_NAME
value: App demo
- key: APP_ENV
value: demo
- key: APP_KEY
value: xxxxx
tasks:
- name: customize /tmp/environment
ansible.builtin.lineinfile:
create: true
path: /tmp/environment
state: present
regexp: "^{{ item.key }}="
line: "{{ item.key }}={{ item.value }}"
with_items: "{{ app_env_variables }}"

works as expected

shell> ansible-playbook playbook.yml

PLAY [localhost] *****************************************************************************

TASK [customize /tmp/environment] ************************************************************
changed: [localhost] => (item={'key': 'APP_NAME', 'value': 'App demo'})
changed: [localhost] => (item={'key': 'APP_ENV', 'value': 'demo'})
changed: [localhost] => (item={'key': 'APP_KEY', 'value': 'xxxxx'})

PLAY RECAP ***********************************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

and created the file

shell> cat /tmp/environment 
APP_NAME=App demo
APP_ENV=demo
APP_KEY=xxxxx

I only added the parameter create: true to avoid error

msg: Destination /tmp/environment does not exist !

How to set environment variables in ansible playbook

Your environment variables are definitely getting set. Your existing tasks don't contain any attempt to verify this, so let's add one. For example, if we run this playbook:

- hosts: localhost
tasks:
- name: Setting variables for CPPFLAGS
shell: "echo $CPPFLAGS"
environment:
CPPFLAGS: -I/opt/vbisam-2.0/include
register: cppflags

- debug:
var: cppflags.stdout

We see as output:

PLAY [localhost] *******************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************
ok: [localhost]

TASK [Setting variables for CPPFLAGS] **********************************************************************************************************
changed: [localhost]

TASK [debug] ***********************************************************************************************************************************
ok: [localhost] => {
"cppflags.stdout": "-I/opt/vbisam-2.0/include"
}

PLAY RECAP *************************************************************************************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0

As @techraf hinted in a comment, it's important to understand that setting environment variables using the environment on a task sets them only for that task. That is, if you wanted CPPFLAGS, LDFLAGS, and LD_LIBRARY_PATH all set at the same time, you would need to do something like:

    -  name: Setting variables for CPPFLAGS
shell: "echo $CPPFLAGS"
environment:
CPPFLAGS: -I/opt/vbisam-2.0/include
LDFLAGS: -L/opt/vbisam-2.0/lib
LD_LIBRARY_PATH: /opt/vbisam-2.0/include
register: cppflags

If you need those variables set on multiple tasks, you would need to either apply the same environment keyword to each task, or set environment on the play instead of individual tasks.

How to read environment variable set with shell module in previous task with Ansible

Q: "Set env variable SYSTEM_VERSION by reading a file that is updated during playbook execution."

A: You can set environment in a play, role, block, or task. See Playbook Keywords. If you must read the value during the execution of the playbook set the environment to a block. For example, the playbook

- hosts: test_11
gather_facts: false
tasks:
- file:
state: directory
path: system-versions
- shell: uname -sr > system_version.txt
- fetch:
src: system_version.txt
dest: system-versions
- block:
- command: 'echo $SYSTEM_VERSION'
register: result
- debug:
var: result.stdout
environment:
SYSTEM_VERSION: "{{ lookup('file', system_version_path) }}"
vars:
system_version_path: "system-versions/{{ inventory_hostname}}/system_version.txt"

creates the dictionary system-versions where the files will be stored. The next task creates the file system_version.txt at the remote host

shell> ssh admin@test_11 cat system_version.txt
FreeBSD 13.0-RELEASE

The next task fetches the file and stores it in the directory system-versions/test_11

shell> cat system-versions/test_11/system_version.txt 
FreeBSD 13.0-RELEASE

The block then reads this file at the controller and sets the environment for the tasks in the block

TASK [debug] *********************************************
ok: [test_11] =>
result.stdout: FreeBSD 13.0-RELEASE

The next option is to fetch the files in the 1st play and use them in the 2nd play. For example, given the files at the remote hosts
shell> ssh admin@test_11 cat system_version.txt
System A
shell> ssh admin@test_12 cat system_version.txt
System B

The playbook

- hosts: test_11,test_12
gather_facts: false
tasks:
- file:
state: directory
path: system-versions
run_once: true
- fetch:
src: system_version.txt
dest: system-versions

- hosts: test_11,test_12
gather_facts: false
vars:
system_version_path: "system-versions/{{ inventory_hostname}}/system_version.txt"
environment:
SYSTEM_VERSION: "{{ lookup('file', system_version_path) }}"
tasks:
- command: 'echo $SYSTEM_VERSION'
register: result
- debug:
var: result.stdout

fetches the files in the 1st play

shell> cat system-versions/test_11/system_version.txt 
System A
shell> cat system-versions/test_12/system_version.txt 
System B

and set the environment in the 2nd play. Gives (abridged)

TASK [debug] ***********************************************
ok: [test_11] =>
result.stdout: System A
ok: [test_12] =>
result.stdout: System B

Notes

  • See Ansible architecture on how Ansible works. Quoting:

Ansible works by connecting to your nodes and pushing out scripts called “Ansible modules” to them. Most modules accept parameters that describe the desired state of the system. Ansible then executes these modules (over SSH by default), and removes them when finished. ...

  • You can take a look at what environment a module is working in. For example, given the inventory
shell> cat hosts
[test]
test_11

[test:vars]
ansible_connection=ssh
ansible_user=admin

the playbook

shell> cat playbook.yml
- hosts: test_11
gather_facts: false
tasks:
- command: '/bin/sh -c set'
register: result
- debug:
var: result.stdout

gives when you enable connection debugging (-vvvv)

shell> ansible-playbook -i hosts playbook.yml -vvvv

...
<test_11> ESTABLISH SSH CONNECTION FOR USER: admin
<test_11> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="admin"' -o ConnectTimeout=10 -o ControlPath=/export/home/vlado.config/.ansible/cp/9d96571b11 -tt test_11 '/bin/sh -c '"'"'/usr/local/bin/python3.8 /home/admin/.ansible/tmp/ansible-tmp-1649217448.1346543-1545740-216049530990371/AnsiballZ_command.py && sleep 0'"'"''
<test_11> (0, b'\r\n{"changed": true, "stdout": "BLOCKSIZE=K\nHOME=/home/admin\nIFS=$' \\t\n'\nLANG=C.UTF-8\nLOGNAME=admin\nMAIL=/var/mail/admin\nMM_CHARSET=UTF-8\nOPTIND=1\nPATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/admin/bin\nPPID=26056\nPS1='$ '\nPS2='> '\nPS4='+ '\nPWD=/home/admin\nSHELL=/bin/sh\nSSH_CLIENT='10.1.0.184 50874 22'\nSSH_CONNECTION='10.1.0.184 50874 10.1.0.61 22'\nSSH_TTY=/dev/pts/0\nTERM=xterm-256color\nUSER=admin", "stderr": "", "rc": 0, "cmd": ["/bin/sh", "-c", "set"], "start": "2022-04-06 03:57:29.500158", "end": "2022-04-06 03:57:29.516366", "delta": "0:00:00.016208", "msg": "", "invocation": {"module_args": {"_raw_params": "/bin/sh -c set", "_uses_shell": false, "warn": false, "stdin_add_newline": true, "strip_empty_ends": true, "argv": null, "chdir": null, "executable": null, "creates": null, "removes": null, "stdin": null}}}\r\n', b'OpenSSH_8.2p1 Ubuntu-4ubuntu0.2, OpenSSL 1.1.1f 31 Mar 2020\r\ndebug1: Reading configuration data /home/vlado/.ssh/config\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 2: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 1545744\r\ndebug3: mux_client_request_session: session request sent\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\nShared connection to test_11 closed.\r\n')

  result.stdout: |-
BLOCKSIZE=K
HOME=/home/admin
IFS=$' \t
'
LANG=C.UTF-8
LOGNAME=admin
MAIL=/var/mail/admin
MM_CHARSET=UTF-8
OPTIND=1
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/admin/bin
PPID=26008
PS1='$ '
PS2='> '
PS4='+ '
PWD=/home/admin
SHELL=/bin/sh
SSH_CLIENT='10.1.0.184 50834 22'
SSH_CONNECTION='10.1.0.184 50834 10.1.0.61 22'
SSH_TTY=/dev/pts/0
TERM=xterm-256color
USER=admin
  • Optionally, you can let Ansible collect the facts
shell> cat playbook.yml
- hosts: test_11
gather_facts: true
tasks:
- debug:
var: ansible_env

gives

  ansible_env:
BLOCKSIZE: K
HOME: /home/admin
LANG: C.UTF-8
LOGNAME: admin
MAIL: /var/mail/admin
MM_CHARSET: UTF-8
PATH: /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/admin/bin
PWD: /home/admin
SHELL: /bin/sh
SSH_CLIENT: 10.1.0.184 51008 22
SSH_CONNECTION: 10.1.0.184 51008 10.1.0.61 22
SSH_TTY: /dev/pts/0
TERM: xterm-256color
USER: admin
  • You can see that in *nix the module command and the ssh connection uses SHELL=/bin/sh. If you want to change it use the parameter executable of the module shell.

For example, the playbook

- hosts: test_11
gather_facts: false
tasks:
- shell: 'echo $SHELL'
register: result
- debug:
var: result.stdout
- shell: 'export MYVAR1=test; echo $MYVAR1'
register: result
- debug:
var: result.stdout
- shell: 'echo $MYVAR1'
register: result
- debug:
var: result.stdout

gives

TASK [shell] ********************************************************
changed: [test_11]

TASK [debug] ********************************************************
ok: [test_11] =>
result.stdout: /bin/sh

TASK [shell] ********************************************************
changed: [test_11]

TASK [debug] ********************************************************
ok: [test_11] =>
result.stdout: test

TASK [debug] ********************************************************
changed: [test_11]

TASK [debug] ********************************************************
ok: [test_11] =>
result.stdout: ''
  • You can see that the environment variable hasn't been set persistently. Each module creates a new login. The solution is using the keyword environment. For example
- hosts: test_11
gather_facts: false
environment:
MYVAR1: test
tasks:
- shell: 'echo $MYVAR1'
register: result
- debug:
var: result.stdout

gives as expected

  result.stdout: test

Setting and using environment variable in Ansible

Remember that when you set an environment variable (anywhere, not just in Ansible), it only effects the current process and its children.

When you run something like this:

  - name: Get CWD
shell: "export ACWD=/test_code_demo"
when: platform != 'jenkins'

You are:

  1. Spawning a shell
  2. Setting the environment variable ACWD in that shell
  3. Exiting the shell

At this point, the environment is destroyed. There's no way to set an environment variable in one task and have it effect another task. You can set per-task environment variables using the environment key on your task, like this:

  - name: DEMO
shell: echo $ACWD
environment:
ACWD: '/test_code_demo'

If you need to apply the environment setting to multiple tasks, you can set it on a play instead:

- hosts: localhost
environment:
ACWD: '/test_code_demo'
tasks:
- command: 'echo $ACWD'
register: output1

- command: 'echo $ACWD'
register: output2

- debug:
msg:
- "{{ output1.stdout }}"
- "{{ output2.stdout }}"

How to set existing linux environment variables with ansible?

This is the correct way to extend PATH variable for a single task:

- name: Execute task with extended PATH
shell: echo $PATH
environment:
PATH: "/usr/other/bin:{{ ansible_env.PATH }}"

environment is not an action by itself, it's a keyword to modify actions' (shell in my example) environment.

How to set environment in ansible task only when variable is defined

The op solved it, as request I will try to elaborate a little bit.
I just pointed to check this URL: Check if dict is empty or not

 - name: install redhat-lsb-core
dnf:
name:
- redhat-lsb-core
state: present
environment: "{{ proxy_env | default ({}) }}"
when: proxy_env is defined

With the way op proposed:
Environment uses proxy_env but if it is not defined default filter will set the proxy_env to nothing so it will be able to validate that is empty correctly. (Empty or not defined). With this way he don't need to check if the variables inside the dict are defined or not, you are setting the entire dict default ({}).

Note: the op solved it by itself with my link, so credit to him also ^^.



Related Topics



Leave a reply



Submit