Ansible Set Dynamic Environment Variables - bash

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 }}"

Related

Ansible if else using shell script

I am using following ansible tasks for triggering certain a task based on user's choice.
This is working:
tasks:
- name: Run python script for generating Repos Report
command: python GetRepos.py -o {{ org }} -p {{ pat }}
register: result
- debug: msg="{{result.stdout}}"
when: choice == "Repos"
- name: Run python script for generating projects Report
command: python Getprojects.py -o {{ org }} -p {{ pat }}
register: result
- debug: msg="{{result.stdout}}"
when: choice == "projects"
But I want to use a shell script with if else statement to run this in one task as below:
tasks:
- name: run python script
shell: |
if [choice == "repos"]
then
cmd: python GetRepos.py -o {{ org }} -p {{ pat }}
elif [choice == "projects"]
then
cmd: python Getprojects.py -o {{ org }} -p {{ pat }}
fi
register: cmd_output
- debug: msg="{{cmd_output.stdout}}"
But this does not execute the task; it just ends without error.
Is this the right syntax for shell?
How can I achieve these 2 separate working tasks in just one task using the shell module?
The cmd: in a shell script will try to run cmd: as a command, which you don't want.
Also, the if statement conditions need spaces on either side - otherwise, it would try to run [choice as a command, which you also don't want.
Also prefer to use single equals instead of double equals, to make it more portable (the remote hosts could have various different shells!).
The other issue is that choice as used inside the shell script is just a literal string. You need to add the braces {{ }} to interpolate the value, as done elsewhere in the playbook.
Taking into consideration the above, the following should work for you:
tasks:
- name: run python script
shell: |
if [ "{{ choice }}" = "repos" ]
then
python GetRepos.py -o "{{ org }}" -p "{{ pat }}"
elif [ "{{ choice }}" = "projects" ]
then
python Getprojects.py -o "{{ org }}" -p "{{ pat }}"
fi
register: cmd_output
- debug:
msg: "{{ cmd_output.stdout }}"

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

Ansible: Command module not interpolating shell variable

The following command fails most likely because it fails to interpolate the shell variable packdir
- name: archive_artifacts.yml --> Clear git history from packs directories
command: 'for packdir in {{ packs_dir }}/*; do rm -rf {{ packs_dir }}/"${packdir}"/.git; done'
args:
chdir: "{{ temp_build_directory }}"
packs_dir is a variable in the defaults/main.yml of the specific role:
packs_dir: "packs"
Is there a way of having the command module substituting both ansible and shell variables?
Is there a way of having the command module substituting both ansible and shell variables?
Yes. Make sure the shell variable is present in the environment of the host, user and shell you run the command in.
- hosts: localhost
vars:
env_variable: SHELL
tasks:
- command: "echo ${{ env_variable }}"
register: result
- debug: msg="{{ result.stdout }}"

ansible assign a fact value to a variable

I am new to ansible, I am writing a small playbook where it has to collect the fact value from the destination host and use that as a variable within the play. Can someone help me how to do that.
---
- hosts: all
gather_facts: True
become: true
become_method: sudo
become_user: root
vars:
BUILD_PATH: /opt/services/dev
pre_tasks:
- setup:
filter: ansible_env
- set_fact:
tag: "{{ ansible_env.DATA_AGGREGATOR_ENV }}"
- debug: var=ENV
tasks:
- name: Copy to Build to DATA
shell: cp -pr {{ BUILD_PATH }} {{ ENV }}
Note: DATA_AGGREGATOR_ENV is environmental variable defined in all servers and the value vary from one server to other.
You set the variable tag, but you use the variable ENV.
You have to set the ENV variable, if you want to use the ENV variable.
- set_fact:
ENV: "{{ ansible_env.DATA_AGG_ENV }}"
Or you have to use the tag variable, if you have set the tag variable.
shell: cp -pr {{ BUILD_PATH }} {{ tag }}
btw: the filter line lacks a space.

Resources