Some environment variables are not visible in Ansible - bash

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

Related

Ansible set environment variable from a file and access it within the playbook

I have the below Ansible script which runs on localhost
- name: set env
shell: ". /tmp/testenv"
- name: get env
debug:
msg: "{{ lookup('env','TEST') }}"
In above script I'm trying to source the file and access the environment variables using the lookup. But it seems like environment variable is not set. Is there anyway I can get this to work?
The shell command is one process and the lookup looks in the environment of the Ansible process, which is a different one. You have to echo the environment variable in your shell command and register the result.
- hosts: localhost
connection: local
gather_facts: no
tasks:
- name: set env
shell: "echo 'export TEST=test' > /tmp/testenv"
- name: check env
shell: ". /tmp/testenv; echo $TEST"
register: result
- name: get env
debug:
msg: "{{ result.stdout }}"

Ansible - environment variables from .env file

I am trying to setup a playbook which will run the command to check status of the service installed in the target machine. The command will only work only if the .env file executed. The command to execute the .env file is .<space>./.env_file_name and the file contains list of environment variables like export JAVA_HOME=/optware/java/jdk/1.2.
I tried to execute the environment file before running the command with the below playbook, but it is not working.
- hosts: name
tasks:
- name: `execute env file`
command: . ./.env_file_name
register: result
Is there any playbook to run the executable environment file to set the environments present on the target machine and then run our command??
First, the . ./.env_file_name syntax is a shell syntax and cannot work with the command module, you need to use the shell module.
Secondly, the shell environment context is reset at every task as each is an ssh command round-trip (so a new shell session), and loading the environment variables in one task will not not make them available for next tasks.
Depending on your context, you have some options:
1. Inventory environment variables
The best option is to have the environment at your inventory side in a variable with different value for each group/host through group_vars/host_vars, then to use it for the environment keyword
# host_vars/my_host.yml
---
env_vars:
VAR1: key1
VAR2: key2
- hosts: my_host
tasks:
- name: Display environment variables
command: env
environment: "{{ env_vars }}"
Pros:
full ansible solution
will work for environment of every module
Cons:
need to know the environment variables at ansible side
2. Loading environment variables for every tasks
If your tasks are all shell/command (which I don't advise, as it's better to use appropriate ansible module whenever possible), you can simply load the env file every time with shell module
- hosts: my_host
tasks:
- name: Display environment variables
shell: |
. ./.env_file_name && env
- name: Do another action
shell: |
. ./.env_file_name && do_something_else
Pros:
no need to know the environment variables at ansible side
Cons:
limited to tasks with shell module
3. Load environment variables from env_file into ansible fact
This option is to parse the env file once and for all and load it in an ansible fact to use with the environment keyword.
- hosts: my_host
tasks:
- name: Get env file content
slurp:
src: ./.env_file_name
register: env_file_content
- name: Parse environment
set_fact:
env_vars: "{{ ('{' + (env_file_content.content | b64decode).split('\n') | select | map('regex_replace', '([^=]*)=(.*)', '\"\\1\": \"\\2\"') | join(',') + '}') | from_json }}"
- name: Display environment variables
command: env
environment: "{{ env_vars }}"
Or, if the env file need to be executed instead of directly parsed:
- hosts: my_host
tasks:
- name: Get env file content
shell: . ./.env_file_name && env
register: env_file_result
- name: Parse environment
set_fact:
env_vars: "{{ ('{' + env_file_result.stdout_lines | map('regex_replace', '([^=]*)=(.*)', '\"\\1\": \"\\2\"') | join(',') + '}') | from_json }}"
- name: Display environment variables
command: env
environment: "{{ env_vars }}"
Pros:
will work for environment of every module
no need to know the environment variables at ansible side
Cons:
could fail on bad formatting of file

How to set different environment variable for each client in Ansible playbook

I am trying to run a slightly different bash script on each ansible client, because each node needs to have a pre-set ID which has already been established. In other words: The bash script looks for an environment variable, and I need that environment variable to be individually set for each Ansible host.
I have a list of identities in a file, which correspond to the host number.
For example, in my inventory (/etc/ansible/hosts) I have:
[servergroupexample]
ansible-host-1 ansible_host=1.2.3.4
ansible-host-2 ansible_host=5.6.7.8
And in a file in /root/IDs.txt I have:
ID-asjdajdj399jad
ID-iajadijasijada
Where the line number in the IDs.txt corresponds to the ansible host number (e.g. ansible-host-2 should receive an environment variable which is set to the second line of IDs.txt)
So it should be possible to grep for the number in the ansible host file (e.g. "ansible-host-1" returns "1") and then that 1 indicates which line to read from the IDs.txt and set as the environment variable on the host itself before running the script. How do I do that?
Thanks!
Q: "The bash script looks for an environment variable, and I need that environment variable to be individually set for each Ansible host."
A: Run once and create a dictionary from the lists with the dict and zip filters. Then use my_ID to set the environment of the command for each host. For example
- hosts: servergroupexample
tasks:
- set_fact:
my_ID: "{{ dict(groups.servergroupexample|
zip(lookup('file', 'IDs.txt').splitlines())) }}"
run_once: true
- command: echo $MYID
register: result
environment:
MYID: "{{ my_ID[inventory_hostname] }}"
- debug:
var: result.stdout
with the inventory and IDs.txt
$ cat hosts
[servergroupexample]
test_01
test_02
$ cat IDs.txt
ID-asjdajdj399jad
ID-iajadijasijada
gives
ok: [test_01] =>
result.stdout: ID-asjdajdj399jad
ok: [test_02] =>
result.stdout: ID-iajadijasijada

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 Set Dynamic Environment Variables

I know about Ansible's environment: command at the top of playbook, but I don't think that will work for me seeing how I don't know the variables value prior to the execution of the playbook. I'm trying to retrieve package versions and PHP Modules and log them to a file. I want to use regex to capture the version and store it to an environment variable. Then I want to write that variable equals that variable's value to an environment file with a shell command. I also want to pull an array from the environment and loop through that. Ansible doesn't seem to persist the shell environment and the environment variable gets wiped out between commands. This is simple in Bash. Is this possible in Ansible? I'm trying:
---
- hosts: all
become: yes
vars:
site_variables:
code_directory: /home/
dependency_versions:
WGET_VERSION: placeholder
PHP_MODULES: placeholder
tasks:
- name: Retrieve Environment
shell: export WGET_VERSION=$(wget --version | grep -o 'Wget [0-9]*.[0-9]*\+')
shell: export PHP_MODULES=$(php -m)
shell: echo "export {{ item }}={{ lookup('env', item ) }}" >> {{ site_variables.code_directory }}/.env.log
with_items:
- WGET_VERSION
- name: Write PHP Modules Out
shell: export PHP_MODULES=$(php -m)
shell: export PHP_MODULES=$(echo {{ lookup('env', 'PHP_MODULES') }} | sed 's/\[PHP Modules\]//g')
shell: export PHP_MODULES=$(echo {{ lookup('env', 'PHP_MODULES') }} | sed 's/\[Zend Modules\]//g')
shell: export PHP_MODULES=({{ lookup('env', 'PHP_MODULES') }})
shell: echo "# - {{ item.0 }}" >> {{ site_variables.code_directory }}/.env.log
with_items:
- "{{ lookup('env', 'PHP_MODULES') }}"
There's a lot going on here.
First, lookup always runs on the ansible control host, while the script that you pass to the shell module is running on the remote server. So you will never be able to get a remote environment variable using lookup.
For details: https://docs.ansible.com/ansible/playbooks_lookups.html
Secondly, environment variables don't propagate from a child to parent. If you have a script that does this...
export MYVARIABLE=foo
...and you run that script, your current environment will not suddenly have a variable named MYVARIABLE. This is just as true for processes spawned by Ansible as it is for processes spawned by your shell.
If you want to set an ansible variable, consider using the register keyword to get the value:
- hosts: localhost
gather_facts: false
tasks:
- name: get wget version
command: wget --version
register: wget_version_raw
- name: extract wget version
set_fact:
wget_version: "{{ wget_version_raw.stdout_lines[0].split()[2] }}"
- name: show wget version
debug:
msg: "wget version is: {{ wget_version }}"

Resources