Why can't I capture an environment variable with Ansible? - linux

I am trying to get and print the value of a given environment variable (ENV_VAR):
$ cat ~/.bash_profile
ENV_VAR=Updated
ENV_VAR2=Updated
$ source ~/.bash_profile && echo $ENV_VAR
Updated
I can successfully retrieve it via terminal, however by using the Ansible playbook below I get an error:
# YAML
---
- hosts: all
vars:
env_var: "{{ lookup('env','ENV_VAR') }}"
tasks:
- name: Add/Update an environment variable to the remote user's shell
lineinfile:
dest: ~/.bash_profile
regexp: '^ENV_VAR='
line: "ENV_VAR=Updated2"
- name: Get the value of the environment variable we just added
shell: source ~/.bash_profile && echo $ENV_VAR
register: env_var_updated
- name: Print the value of the environment variable
debug:
msg: "var1: {{ env_var }} - var2 {{ env_var_updated.stdout }}"
Executing:
$ ansible-playbook playbook.yml
PLAY [all] *********************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************
ok: [192.168.0.222]
TASK [Add/Update an environment variable to the remote user's shell] ***********************************************
ok: [192.168.0.222]
TASK [Get the value of the environment variable we just added] *****************************************************
fatal: [192.168.0.222]: FAILED! => {"changed": true, "cmd": "source ~/.bash_profile && echo $ENV_VAR", "delta": "0:00:00.002337", "end": "2020-12-02 10:20:21.963968", "msg": "non-zero return code", "rc": 127, "start": "2020-12-02 10:20:21.961631", "stderr": "/bin/sh: 1: source: not found", "stderr_lines": ["/bin/sh: 1: source: not found"], "stdout": "", "stdout_lines": []}
PLAY RECAP *********************************************************************************************************
192.168.0.222 : ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Results after execution:
$ cat ~/.bash_profile
ENV_VAR=Updated2
ENV_VAR2=Updated
$ source ~/.bash_profile && echo $ENV_VAR
Updated2
I am logged as the same user (In the terminal window and SSH for Ansible)

As shown by the following guide:
https://docs.ansible.com/ansible/2.5/modules/shell_module.html
Ansible shell actually runs in /bin/sh and not /bin/bash. You can specify the shell as /bin/bash through:
- name: Get the value of the environment variable we just added
shell: source ~/.bash_profile && echo $ENV_VAR
register: env_var_updated
args:
executable: /bin/bash

Related

Ansible tasks after delegate_to:127.0.0.1 are skipped

My code is as below, I am reading from a local csv file use those values for tasks on remote hosts
---
- name: Empty Topics
hosts: remote_host
gather_facts: no
vars:
kafka_topics: /bin/kafka-topics
bootstrap_server: "list_of_broker_hosts"
retention_ms: 604800000
command_config: /etc/kafka/client.properties
kafka_log_dirs: /usr/bin/kafka-log-dirs
#ansible_connection: ssh
#ansible_user: ansible
#ansible_become: true
tasks:
- name: "Reading Topic Names"
read_csv:
path: topics_list.csv
register: topics
delegate_to: 127.0.0.1
- name: "Setting Topic Retention to 0"
become: yes
become_user: root
shell:
{{ kafka_topics }} --bootstrap_server {{bootstrap_server}} --alter --topic "{{ item.topic_name }}" --config retention.ms=0 --command_config #{{command_config}}
#touch /tmp/producer_test_1
loop: "{{ topics.list }}"
- name: "waiting for size to go zero "
become: yes
become_user: root
shell:
topic_size=1
while [ $topic_size -ne 0 ]
do
topic_size=`{{kafka_log_dirs}} --command_config {{command_config}} --bootstrap_server {{bootstrap_server}} --topic-list "{{ item.topic_name }}" --describe | grep -oP '(?<=size":)\d+' | awk '{ sum += $1 } END { print sum }' `
sleep 40
done
loop: "{{ topics.list }}"
- name : "Setting Topic Retention to 7"
become: yes
become_user: root
shell:
#{{ kafka_topics }} --bootstrap_server {{bootstrap_server}} --alter --topic "{{ item.topic_name }}" --config retention.ms={{retention_ms}} --command_config {{command_config}}
#touch /tmp/producer_test_2
loop: "{{ topics.list }}"
here the tasks after first task "Reading Topic Names" are skipped , if I remove this task, they succeed but that means I have to hard code values on subsequent tasks
execution log as below. How can I avoid this. current ansible version is 2.9.19 This playbook had previously worked as is on ansible version 2.9.6 in my previous organization, not sure what settings may be changed in my new company.
I tried delegate_to: localhost as well as delegate_to: 127:0:0:1
ansible#localhost[~] $ ansible-playbook empty_topics.pb -i /home/ansible/inv_sit.yml -l all
PLAY [Empty Topics] ***********************************************************************************************************
TASK [Reading Topic Names] ****************************************************************************************************
ok: [remotehost]
TASK [Setting Topic Retention to 0] *******************************************************************************************
TASK [waiting for size to go zero] ********************************************************************************************
TASK [Setting Topic Retention to 7] *******************************************************************************************
PLAY RECAP ********************************************************************************************************************
remotehost : ok=1 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
ansible#localhost[~] $ ansible-playbook empty_topics.pb -i /home/ansible/inv_sit.yml -l all -vvv
ansible-playbook 2.9.19
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3.6/site-packages/ansible
executable location = /usr/bin/ansible-playbook
python version = 3.6.8 (default, Jun 14 2022, 12:54:58) [GCC 8.5.0 20210514 (Red Hat 8.5.0-10)]
Using /etc/ansible/ansible.cfg as config file
host_list declined parsing /home/ansible/inv_sit.yml as it did not pass its verify_file() method
script declined parsing /home/ansible/inv_sit.yml as it did not pass its verify_file() method
Parsed /home/ansible/inv_sit.yml inventory source with ini plugin
Skipping callback 'actionable', as we already have a stdout callback.
Skipping callback 'counter_enabled', as we already have a stdout callback.
Skipping callback 'debug', as we already have a stdout callback.
Skipping callback 'dense', as we already have a stdout callback.
Skipping callback 'dense', as we already have a stdout callback.
Skipping callback 'full_skip', as we already have a stdout callback.
Skipping callback 'json', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'null', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.
Skipping callback 'selective', as we already have a stdout callback.
Skipping callback 'skippy', as we already have a stdout callback.
Skipping callback 'stderr', as we already have a stdout callback.
Skipping callback 'unixy', as we already have a stdout callback.
Skipping callback 'yaml', as we already have a stdout callback.
PLAYBOOK: empty_topics.pb *****************************************************************************************************
1 plays in empty_topics.pb
PLAY [Empty Topics] ***********************************************************************************************************
META: ran handlers
TASK [Reading Topic Names] ****************************************************************************************************
task path: /home/ansible/empty_topics.pb:18
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: ansible
<127.0.0.1> EXEC /bin/sh -c 'echo ~ansible && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/ansible/.ansible/tmp `"&& mkdir "` echo /home/ansible/.ansibl e/tmp/ansible-tmp-1672893960.0795922-3957982-94418260356492 `" && echo ansible-tmp-1672893960.0795922-3957982-94418260356492="` echo /home/ansible/.ansible/tmp/ansible-tmp-1672893960.0795922-3957982-94418260356492 `" ) && sleep 0'
Using module file /usr/lib/python3.6/site-packages/ansible/modules/files/read_csv.py
<127.0.0.1> PUT /home/ansible/.ansible/tmp/ansible-local-3957970ixug96xw/tmpn373dahz TO /home/ansible/.ansible/tmp/ansible-tmp- 1672893960.0795922-3957982-94418260356492/AnsiballZ_read_csv.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/ansible/.ansible/tmp/ansible-tmp-1672893960.0795922-3957982-94418260356492/ /home/ ansible/.ansible/tmp/ansible-tmp-1672893960.0795922-3957982-94418260356492/AnsiballZ_read_csv.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/libexec/platform-python /home/ansible/.ansible/tmp/ansible-tmp-1672893960.0795922-3957982-944 18260356492/AnsiballZ_read_csv.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /home/ansible/.ansible/tmp/ansible-tmp-1672893960.0795922-3957982-94418260356492/ > /dev/ null 2>&1 && sleep 0'
ok: [remotehost] => {
"changed": false,
"dict": {},
"invocation": {
"module_args": {
"delimiter": null,
"dialect": "excel",
"fieldnames": null,
"key": null,
"path": "topics_list.csv",
"skipinitialspace": null,
"strict": null,
"unique": true
}
},
"list": []
}
TASK [Setting Topic Retention to 0] *******************************************************************************************
task path: /home/ansible/empty_topics.pb:23
TASK [waiting for size to go zero] ********************************************************************************************
task path: /home/ansible/empty_topics.pb:31
TASK [Setting Topic Retention to 7] *******************************************************************************************
task path: /home/ansible/empty_topics.pb:42
META: ran handlers
META: ran handlers
PLAY RECAP ********************************************************************************************************************
remotehost : ok=1 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
ansible#localhost[~] $
I tried delegate_to: localhost as well as delegate_to: 127:0:0:1
As suggested by β.εηοιτ.βε, I checked the csv file, although there were contents , it was missing the header row, hence the issue. Now with header row added to the csv file, the code works.

Ansible - passing vars from a playbook to another

I am working with Ansible and I have defined a var (varx) in inventory file (inventory1.yml) like this (only for env2):
env1:
hosts:
env1.domain.com:
env2:
hosts:
env2.domain.com:
vars:
varx: 'valuex'
I am running playbook1 and calling playbook2 like this:
ansible-playbook -i inventory/inventory1.yml playbooks/playbook1.yml --extra-vars "env=env1"
playbook1.yml:
---
roles:
- role1
role1 - main.yml:
---
- name: role1
command: bash script.sh {{ env }} {{ 'varx='~varx if varx is defined else '' }}
Inside script.sh (linux bash) I am calling playbook2:
#!/bin/bash
ENV=$1
varx=$2
do_stuff() {
local arguments= "env=$ENV { 'varx='~varx if varx is defined else '' }}"
ansible-playbook -i inventory/inventory2.yml playbook2.yml --extra-vars "$arguments"
}
do_stuff
The idea is to pass varx from inventory1.yml if it exists in inventory1.yml to script.sh when it runs playbook2.yml. If it doesn't exist in inventory1.yml it will look for it in inventory2.yml.
The code as it is its looking for it in inventory2.yml, so I am not being able to retrieve it from inventory1.yml.
I believe that there is some issue in script.sh.
How can I achieve to bring varx from inventory1.yml and if doesn't exist to capture it from inventory2.yml?
I want to make some changes in role1 so that I can pass the vars to script.sh (in a string for example,or in a better way - using ansible-playbook and passing the vars) and than capturing the vars in script.sh and launching playbook2. Any ideas how I can acomplish that?
Basically what I want to do is something like this:
bash script.sh args
and the arguments should all go inside args variable (args= env varx).
What i have done is:
role1 - main.yml:
---
- name: role1
command: bash script.sh arguments
arguments: "{{ env }} {{ 'varx='~varx if varx is defined else '' }}"
But when It reaches script.sh the varx is not being read.
Minimal reproducible example
Given the inventory
shell> cat inventory1
env1:
hosts:
env1.domain.com:
ansible_host: 10.1.0.61
ansible_user: admin
env2:
hosts:
env2.domain.com:
ansible_host: 10.1.0.62
ansible_user: admin
vars:
varX: valueX
, the playbook
shell> cat pb1.yml
- hosts: all
gather_facts: false
tasks:
- command: "sh -c '/tmp/script.sh {{ env }} {{ _varX }}'"
register: out
vars:
_varX: "varX={{ varX|default('') }}"
- debug:
var: out.stdout
, and the scripts at the remote hosts
shell> ssh admin#10.1.0.61 cat /tmp/script.sh
echo ansible-playbook -i inventory2 pb2.yml -e "$1 $2"
shell> ssh admin#10.1.0.62 cat /tmp/script.sh
echo ansible-playbook -i inventory2 pb2.yml -e "$1 $2"
The playbook gives
shell> ansible-playbook -i inventory1 pb1.yml -e "env=env1"
PLAY [all] ************************************************************************************
TASK [command] ********************************************************************************
changed: [env2.domain.com]
changed: [env1.domain.com]
TASK [debug] **********************************************************************************
ok: [env1.domain.com] =>
out.stdout: ansible-playbook -i inventory2 pb2.yml -e env1 varX=
ok: [env2.domain.com] =>
out.stdout: ansible-playbook -i inventory2 pb2.yml -e env1 varX=valueX
PLAY RECAP ************************************************************************************
env1.domain.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
env2.domain.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Fo me it worked like this:
---
- name: role1
command: bash script.sh
env={{env}}
{{ 'varx='~varx if varx is defined else '' }}
In the shell script I capture vars like this:
#!/bin/bash
for arg in "$#"; do
arguments+="$arg "
done
do_stuff() {
ansible-playbook -i inventory/inventory2.yml playbook2.yml --extra-vars "$arguments"
}
do_stuff

How can I compare a remote file with a reference on controller without failing the playbook when different?

I have a very peculiar problem, in my code I have to compare ansible value with customer expected value and customer expected un_matched value, however the final playbook output should be failed =0 instead of failed =1.
The code is:
- name: show file contents of customer-expects.txt
debug:
msg: "{{ lookup('file', '/customer-expects.txt') }}"
- shell: cat /etc/issue
register: issue
changed_when: false
ignore_errors: yes
- assert:
that:
- lookup('file', '/customer-expects.txt') == issue.stdout
success_msg: "matched! {{ lookup('file', '/customer-expects.txt') }} = {{ issue.stdout }}"
- name: show file contents customer-expects_unmatched.txt
debug:
msg: "{{ lookup('file', '/customer-expects_unmatched.txt') }}"
- shell: cat /etc/issue
register: issue
changed_when: false
ignore_errors: yes
- assert:
that:
- lookup('file', '/customer-expects_unmatched.txt') == issue.stdout
fail_msg: "unmatched! {{ lookup('file', '/customer-expects_unmatched.txt') }} = {{ issue.stdout }}"
success_msg: "matched! {{ lookup('file', '/customer-expects.txt') }} = {{ issue.stdout }}"
Output is:
[root#ansible-master /]# ansible-playbook tab8.role.yml -v
Using /etc/ansible/ansible.cfg as config file
PLAY [This output is for Tab-8 of Function Design document] *********
TASK [Gathering Facts] **********************************************
ok: [ansible-client1]
TASK [issue_tab8 : show file contents of customer-expects.txt] ******
ok: [ansible-client1] => {
"msg": ""
}
TASK [issue_tab8 : shell] *******************************************
ok: [ansible-client1] => {"changed": false, "cmd": "cat /etc/issue", "delta": "0:00:00.005200", "end": "2022-01-25 14:17:57.070688", "rc": 0, "start": "2022-01-25 14:17:57.065488", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
TASK [issue_tab8 : assert] ******************************************
ok: [ansible-client1] => {
"changed": false,
"msg": "matched! = "
}
TASK [issue_tab8 : show file contents customer-expects_unmatched.txt] *
ok: [ansible-client1] => {
"msg": "abc"
}
TASK [issue_tab8 : shell] *******************************************
ok: [ansible-client1] => {"changed": false, "cmd": "cat /etc/issue", "delta": "0:00:00.004603", "end": "2022-01-25 14:17:57.674059", "rc": 0, "start": "2022-01-25 14:17:57.669456", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
TASK [issue_tab8 : assert] ******************************************
fatal: [ansible-client1]: FAILED! => {
"assertion": "lookup('file', '/customer-expects_unmatched.txt') == issue.stdout",
"changed": false,
"evaluated_to": false,
"msg": "unmatched! abc = "
}
PLAY RECAP **********************************************************
ansible-client1 : ok=6 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
You are taking this the wrong way and make it unnecessarily complicated IMO.
You have to understand that laying out a playbook in ansible consists of describing the state in which you want to find the target machine. In this particular situation, you want to describe the state in which you want to find a particular file on the target. This is basically done with a task using the copy module. Once you have done that, knowing if the file is similar or different is just a matter of command line switches or task options.
For more on the below explanation see Validating tasks
Ansible has a check_mode which will let you run an entire playbook just to verify what it would do. You can also unconditionally apply that mode to a single task so that it always runs as a check (check_mode: true), or always makes changes to the remote target however the playbook was called (check_mode: false). We are interested here in the first form.
Similarly, ansible has a diff possibility that will let you see what are the differences between the state you described and the modification it (should have to) apply on the target to reach that state.
An example whith your above scenario. I made the test targeting my local machine only but you can get the exact same result on any remote target.
First let's create the reference file we want to check against in our ansible directory.
mkdir -p files
echo "I'm the reference file" > files/ansible_reference.txt
For the example, I'll now create one identical and one different file on our target so that we can compare both cases:
echo "I'm the reference file" > /tmp/ansible_similar.txt
echo "I'm a different file" > /tmp/ansible_different.txt
This is the playbook compare.yml
---
- name: compare remote file to a local reference
hosts: localhost
gather_facts: false
vars:
local_reference: ansible_reference.txt
remote_files_2_check:
- /tmp/ansible_similar.txt
- /tmp/ansible_different.txt
tasks:
- name: Dry run a copy with diff to check if remote file is aligned
copy:
src: "{{ local_reference }}"
dest: "{{ item }}"
check_mode: true
diff: true
loop: "{{ remote_files_2_check }}"
Which gives:
$ ansible-playbook compare.yml
PLAY [compare remote file to a local reference] *****************************
TASK [Dry run a copy with diff to check if remote file is aligned] **********
ok: [localhost] => (item=/tmp/ansible_similar.txt)
--- before: /tmp/ansible_different.txt
+++ after: /home/user/ansible_project/files/ansible_reference.txt
## -1 +1 ##
-I'm a different file
+I'm the reference file
changed: [localhost] => (item=/tmp/ansible_different.txt)
PLAY RECAP ******************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Notes:
Although Ansible reports changed for the different file this is only what would happen. Since we are in dry-run for the given task, the above will not change your target. You can run it any number of times, it will report the same result as long as you don't change the reference or the target state.
I you are not happy with the default reporting of the task, you can register the result, explore its content and use it in subsequent tasks to meet your exact requirement.
Note that the above may report a difference on the permission of the target file. You may have to tune this in the copy module to avoid false positive.

Unable to fail terminate Ansible playbook upon string match condition

I wish to fail and terminate my ansible playbook when Number=TKT334
Below is my playbook:
---
- name: "Play 1"
hosts: localhost
any_errors_fatal: True
serial: 1
tags: always
tasks:
- name: Setting string variable.
set_fact:
inlinevar: '2020-06-10 20:22:16\tTKT334\tKIRAN\tDeployed\tPRODUCTION'
- name: Setting string variable.
set_fact:
environment: 'PRODUCTION'
- block:
- name: "Search to avoid duplicate CR Numbers user:{{ ansible_user_id }}"
shell: "echo SUCCESSSSSSS"
failed_when: (inlinevar is search( Number ) and environment == "PRODUCTION")
- debug:
msg: This is FINAL inlinevar `{{ inlinevar }}`
rescue:
- name: Print custom conditional debug message
fail:
msg: >-
{{
command_result_app.stdout is search( Number ) |
ternary(
"This CR is already Deployed. Hence retry with a Unique CR Number.",
"The Dtabase is not reachable. Unable to connect To the Database."
)
}}
However, when I run the playbook I do not fail and continues to print the debug:
I was expecting it to fail but it shows ansible run as successful and does not terminate the playbook executions.
See the output below:
ansible-playbook test.yml -e Number=TKT334
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [Play 1] *************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************
ok: [localhost]
TASK [Setting string variable.] ****************************************************************
ok: [localhost]
TASK [Setting string variable.] ****************************************************************
ok: [localhost]
TASK [Search to avoid duplicate CR Numbers user:{{ ansible_user_id }}] ***
changed: [localhost]
TASK [debug] ***********************************************************************************************************************************************************
ok: [localhost] => {
"msg": "This is FINAL inlinevar `2020-06-10 20:22:16\\tTKT334\\t KIRAN\\tDeployed\\tPRODUCTION`"
}
PLAY RECAP *************************************************************************************************************************************************************
localhost : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Can you please suggest?
environment is a special option used to set environment variables for tasks, e.g.
- name: Do something
shell: echo "whatever"
environment:
http_proxy: http://my.server.com:8080
no_proxy: my.domain.com
In your case, environment is always empty after set_fact and environment == "PRODUCTION" is always false.
Rename your variable to something else, don't use environment (see this other question for more discussion), e.g.
[...]
- name: Setting string variable.
set_fact:
deploy_environment: 'PRODUCTION'
- block:
- name: "Search to avoid duplicate CR Numbers user:{{ ansible_user_id }}"
shell: "echo SUCCESSSSSSS"
failed_when: (inlinevar is search( Number ) and deploy_environment == "PRODUCTION")
[...]

ansible-playbook extra vars passing from command line

My playbook looks like:
---
- name: Install and configure AD authentication
hosts: test
become: yes
become_user: root
vars:
hostname: "{{ host_name }}"
vars_prompt:
- name: "bind_password"
prompt: "Password for xxx.local\\user"
private: yes
tasks:
- name: Ansible prompt example.
debug:
msg: "{{ bind_password }}"
- name: Ansible prompt example.
debug:
msg: "{{ host_name }}"
and i am using below command to pass the variable
ansible-playbook hostname_set.yml --extra-vars "host_name= 'xxx.xxx.local'"
but i am not getting exact variable value what i am using for setting up hostname.
Password for xxx.xxx\user:
PLAY [Install and configure AD authentication]
TASK [Gathering Facts]
ok: [x.x.x.x]
TASK [Ansible prompt example.]
ok: [x.x.x.x] => {
"msg": "wel"
}
TASK [Ansible prompt example.]
ok: [x.x.x.x] => {
"msg": ""
}
TASK [Setup the hostname]
changed: [x.x.x.x]
PLAY RECAP
x.x.x.x : ok=4 changed=1 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
You have an extra space in your command line that ruins how ansible is interpreting the extra vars. Just remove it:
--extra-vars "host_name='xxx.xxx.local'"
Note that you don't even need all those quotes. The following should also work as expected:
--extra-vars host_name=xxx.xxx.local

Resources