Running Environment variable at Ansible Play level question - ansible

I have following commands declared in main playbook file:
---
- name: Install config
hosts: all
become: yes
become_method: sudo
become_user: root
roles:
- initial_config
environment:
http_proxy: http://{{ProxyHost}}:{{ProxyPort}}
https_proxy: http://{{ProxyHost}}:{{ProxyPort}}
ftp_proxy: http://{{ProxyHost}}:{{ProxyPort}}
Inside my group_vars/ all: I have declared these parameter:
ProxyHost: proxy.test.com
ProxyPort: 9999
no_proxy: 'test.com'
But I am receiving error when i run main playbook locally saying:
ERROR! Syntax Error while loading YAML.
found unexpected end of stream

The play below
shell> cat group_vars/all
ProxyHost: proxy.test.com
ProxyPort: 9999
no_proxy: test.com
shell> cat pb.yml
- hosts: test_01
environment:
http_proxy: "http://{{ ProxyHost }}:{{ ProxyPort }}"
https_proxy: "http://{{ ProxyHost }}:{{ ProxyPort }}"
ftp_proxy: "http://{{ ProxyHost }}:{{ ProxyPort }}"
tasks:
- shell: set | grep proxy
register: result
- debug:
var: result.stdout_lines
gives
shell> ansible-playbook pb.yml
PLAY [test_01] **********
TASK [shell] ************
changed: [test_01]
TASK [debug] ************
ok: [test_01] => {
"result.stdout_lines": [
"SUDO_COMMAND='/bin/sh -c echo BECOME-SUCCESS-hhojceebldohrrfvivcndrtjtvtrzgfg ; http_proxy=http://proxy.test.com:9999 https_proxy=http://proxy.test.com:9999 ftp_proxy=http://proxy.test.com:9999 /usr/local/bin/python3.7 /home/admin/.ansible/tmp/ansible-tmp-1588775688.2160842-43227888427690/AnsiballZ_command.py'",
"ftp_proxy=http://proxy.test.com:9999",
"http_proxy=http://proxy.test.com:9999",
"https_proxy=http://proxy.test.com:9999"
]
}
PLAY RECAP **********
test_01: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Related

Display Ansible playbook with lookups interpolated

I have an Ansible playbook that looks, in part, like this:
...
environment:
F2B_DB_PURGE_AGE: "{{ lookup('env','F2B_DB_PURGE_AGE') }}"
F2B_LOG_LEVEL: "{{ lookup('env','F2B_LOG_LEVEL') }}"
SSMTP_HOST: "{{ lookup('env','SSMTP_HOST') }}"
SSMTP_PORT: "{{ lookup('env','SSMTP_PORT') }}"
SSMTP_TLS: "{{ lookup('env','SSMTP_TLS') }}"
...
Is there any way to run ansible-playbook so that it will show the results of the YAML file after replacing the lookups with their values? That is, I would like to be able to run something like ansible-playbook file.yaml --dry-run and see on standard output (assuming the environment variables were set appropriately):
...
environment:
F2B_DB_PURGE_AGE: "20"
F2B_LOG_LEVEL: "debug"
SSMTP_HOST: "smtp.example.com"
SSMTP_PORT: "487"
SSMTP_TLS: "true"
...
Set the environment for testing
shell> cat env.sh
#!/usr/bin/bash
export F2B_DB_PURGE_AGE="20"
export F2B_LOG_LEVEL="debug"
export SSMTP_HOST="smtp.example.com"
export SSMTP_PORT="487"
export SSMTP_TLS="true"
shell> source env.sh
Given the inventory
shell> cat hosts
localhost ansible_connection=local
Q: "Run something like ansible-playbook file.yaml --dry-run and see environment"
A: The below playbook does the job
shell> cat file.yml
- hosts: all
vars:
my_environment:
F2B_DB_PURGE_AGE: "{{ lookup('env','F2B_DB_PURGE_AGE') }}"
F2B_LOG_LEVEL: "{{ lookup('env','F2B_LOG_LEVEL') }}"
SSMTP_HOST: "{{ lookup('env','SSMTP_HOST') }}"
SSMTP_PORT: "{{ lookup('env','SSMTP_PORT') }}"
SSMTP_TLS: "{{ lookup('env','SSMTP_TLS') }}"
tasks:
- block:
- debug:
msg: |
my_environment:
{{ my_environment|to_nice_yaml|indent(2) }}
- meta: end_play
when: dry_run|d(false)|bool
- debug:
msg: Continue ...
Set dry_run=true
shell> ansible-playbook file.yml -e dry_run=true
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg: |-
my_environment:
F2B_DB_PURGE_AGE: '20'
F2B_LOG_LEVEL: debug
SSMTP_HOST: smtp.example.com
SSMTP_PORT: '487'
SSMTP_TLS: 'true'
TASK [meta] **********************************************************************************
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
By default, the playbook will execute tasks
shell> ansible-playbook file.yml
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
skipping: [localhost]
TASK [meta] **********************************************************************************
skipping: [localhost]
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg: Continue ...
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Optionally, let the playbook gather facts and use the dictionary ansible_env. Use the filer ansible.utils.keep_keys to select your variables
- hosts: all
gather_facts: true
vars:
my_environment_vars:
- F2B_DB_PURGE_AGE
- F2B_LOG_LEVEL
- SSMTP_HOST
- SSMTP_PORT
- SSMTP_TLS
my_environment: "{{ ansible_env|
ansible.utils.keep_keys(target=my_environment_vars) }}"
tasks:
- block:
- debug:
msg: |
my_environment:
{{ my_environment|to_nice_yaml|indent(2) }}
- meta: end_play
when: dry_run|d(false)|bool
- debug:
msg: Continue ...

How to execute on multiple hosts in ansible

I have a script that will execute in two parts. First it will execute on localhost and query a database table to get a hostname. second part of the script should run on the host which was registered in the query before. I am not able to set the host with the set_fact I did in the first part of the code.
this is what iam trying to do:
- hosts: localhost
gather_facts: false
become: yes
become_user: oracle
vars_files:
- vars/main.yml
tasks:
- name: Get new hostname
tempfile:
state: file
register: tf
- name: create sql file
template:
src: get_hostname.sql.j2
dest:"{{ tf.path }}"
mode: 0775
- name: login
command:
argv:
- "sqlplus"
- -s
- "#{{ tf.path }}"
environment:
ORACLE_HOME: "oracle/home"
register: command_out
- set_fact:
NEW_HOST: "{{ command_out.stdout }}"
- hosts: "{{ NEW_HOST }}"
gather_facts: false
become: yes
become_user: oracle
vars_file:
- vars/main.yml
tasks:
- name: debug
command: hostname
register: new_host_out
- debug:
msg: "new host is {{ new_host_out.stdout }}"
Everything works fine in the first part of the code, but errors out at the second part saying it cannot find the NEW_HOST.
Use hostvars to reference such a variable. Create a dummy host to keep this variable. For example, given the inventory
shell> cat hosts
dummy
[test]
test_11
test_12
test_13
The playbook creates the variable. See Delegated facts
shell> cat pb.yml
- hosts: localhost
tasks:
- set_fact:
NEW_HOST: test_12
delegate_to: dummy
delegate_facts: true
- debug:
var: hostvars.dummy.NEW_HOST
- hosts: "{{ hostvars.dummy.NEW_HOST }}"
gather_facts: false
tasks:
- debug:
var: inventory_hostname
gives
shell> ansible-playbook pb.yml
PLAY [localhost] ****************************************************************************
TASK [set_fact] *****************************************************************************
ok: [localhost -> dummy]
TASK [debug] ********************************************************************************
ok: [localhost] =>
hostvars.dummy.NEW_HOST: test_12
PLAY [test_12] ******************************************************************************
TASK [debug] ********************************************************************************
ok: [test_12] =>
inventory_hostname: test_12
PLAY RECAP **********************************************************************************
localhost: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_12 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You can use localhost for this purpose as well. The playbook below works as expected
- hosts: localhost
tasks:
- set_fact:
NEW_HOST: test_12
- hosts: "{{ hostvars.localhost.NEW_HOST }}"
gather_facts: false
tasks:
- debug:
var: inventory_hostname

How to delegate facts to localhost from a play targeting remote hosts

ansible version: 2.9.16 running on RHEL 7.9 python ver = 2.7.5 targeting windows 2016 servers. ( should behave the same for linux target servers too)
EDIT: Switched to using host specific variables in inventory to avoid confusion that Iam just trying to print hostnames of a group. Even here its a gross simplification. Pretend that var1 is obtained dynamically for each server instead of being declared in the inventory file.
My playbook has two plays. One targets 3 remote servers ( Note: serial: 0 i.e Concurrently ) and another just the localhost. In play1 I am trying to delegate facts obtained from each of these hosts to the localhost using delegate_facts and delegate_to. The intent is to have these facts delegated to a single host ( localhost ) so I can use it later in a play2 (using hostvars) that targets the localhost. But strangely thats not working. It only has information from the last host from Play1.
Any help will be greatly appreciated.
my inventory file inventory/test.ini looks like this:
[my_servers]
svr1 var1='abc'
svr2 var1='xyz'
svr3 var1='pqr'
My Code:
## Play1
- name: Main play that runs against multiple remote servers and builds a list.
hosts: 'my_servers' # my inventory group that contains 3 servers svr1,svr2,svr3
any_errors_fatal: false
ignore_unreachable: true
gather_facts: true
serial: 0
tasks:
- name: initialize my_server_list as a list and delegate to localhost
set_fact:
my_server_list: []
delegate_facts: yes
delegate_to: localhost
- command: /root/complex_script.sh
register: result
- set_fact:
my_server_list: "{{ my_server_list + hostvars[inventory_hostname]['result.stdout'] }}"
# run_once: true ## Commented as I need to query the hostvars for each host where this executes.
delegate_to: localhost
delegate_facts: true
- name: "Print list - 1"
debug:
msg:
- "{{ hostvars['localhost']['my_server_list'] | default(['NotFound']) | to_nice_yaml }}"
# run_once: true
- name: "Print list - 2"
debug:
msg:
- "{{ my_server_list | default(['NA']) }}"
## Play2
- name: Print my_server_list which was built in Play1
hosts: localhost
gather_facts: true
serial: 0
tasks:
- name: "Print my_server_list without hostvars "
debug:
msg:
- "{{ my_server_list | to_nice_json }}"
# delegate_to: localhost
- name: "Print my_server_list using hostvars"
debug:
msg:
- "{{ hostvars['localhost']['my_server_list'] | to_nice_yaml }}"
# delegate_to: localhost
###Output###
$ ansible-playbook -i inventory/test.ini delegate_facts.yml
PLAY [Main playbook that runs against multiple remote servers and builds a list.] ***********************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************
ok: [svr3]
ok: [svr1]
ok: [svr2]
TASK [initialize] ***************************************************************************************************************************************************************************
ok: [svr1]
ok: [svr2]
ok: [svr3]
TASK [Build a list of servers] **************************************************************************************************************************************************************
ok: [svr1]
ok: [svr2]
ok: [svr3]
TASK [Print list - 1] ***********************************************************************************************************************************************************************
ok: [svr1] =>
msg:
- |-
- pqr
ok: [svr2] =>
msg:
- |-
- pqr
ok: [svr3] =>
msg:
- |-
- pqr
TASK [Print list - 2] ***********************************************************************************************************************************************************************
ok: [svr1] =>
msg:
- - NA
ok: [svr2] =>
msg:
- - NA
ok: [svr3] =>
msg:
- - NA
PLAY [Print my_server_list] *****************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************
ok: [localhost]
TASK [Print my_server_list without hostvars] ************************************************************************************************************************************************
ok: [localhost] =>
msg:
- |-
[
"pqr"
]
TASK [Print my_server_list using hostvars] **************************************************************************************************************************************************
ok: [localhost] =>
msg:
- |-
- pqr
PLAY RECAP **********************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
svr1 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
svr2 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
svr3 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Playbook run took 0 days, 0 hours, 0 minutes, 13 seconds
###Expected Output###
I was expecting the last two debug statements in Play2 to contain the values of var1 for all the servers something like this:
TASK [Print my_server_list using hostvars] **************************************************************************************************************************************************
ok: [localhost] =>
msg:
- |-
- abc
- xyz
- pqr
Use Special Variables, e.g.
- hosts: all
gather_facts: false
tasks:
- set_fact:
my_server_list: "{{ ansible_play_hosts_all }}"
run_once: true
delegate_to: localhost
delegate_facts: true
- hosts: localhost
gather_facts: false
tasks:
- debug:
var: my_server_list
gives
ok: [localhost] =>
my_server_list:
- svr1
- svr2
- svr3
There are many other ways how to create the list, e.g.
- hosts: all
gather_facts: false
tasks:
- debug:
msg: "{{ groups.my_servers }}"
run_once: true
- hosts: all
gather_facts: false
tasks:
- debug:
msg: "{{ hostvars|json_query('*.inventory_hostname') }}"
run_once: true
Q: "Fill the list with outputs gathered by running complex commands."
A: Last example above shows how to create a list from hostvars. Register the result from the complex command, e.g.
shell> ssh admin#srv1 cat /root/complex_script.sh
#!/bin/sh
ifconfig wlan0 | grep inet | cut -w -f3
The playbook
- hosts: all
gather_facts: false
tasks:
- command: /root/complex_script.sh
register: result
- set_fact:
my_server_list: "{{ hostvars|json_query('*.result.stdout') }}"
run_once: true
delegate_to: localhost
delegate_facts: true
- hosts: localhost
gather_facts: false
tasks:
- debug:
var: my_server_list
gives
my_server_list:
- 10.1.0.61
- 10.1.0.62
- 10.1.0.63
Q: "Why the logic of delegating facts to localhost and keep appending them to that list does not work?"
A: The code below (simplified) can't work because the right-hand-side msl value still comes from the hostvars of the inventory_host despite the fact delegate_facts: true. This merely puts the created variable msl into the localhost's hostvars
- hosts: my_servers
tasks:
- set_fact:
msl: "{{ msl|default([]) + [inventory_hostname] }}"
delegate_to: localhost
delegate_facts: true
Quoting from Delegating facts
To assign gathered facts to the delegated host instead of the current host, set delegate_facts to true
As a result of such code, the variable msl will keep the last assigned value only.

Execute playbook with different sudo users on different hosts

There are two hosts in the host list DEV and QA host
I need to execute playbook with dev_sudo_user for development env and qa_sudo_user for QA environment parallely
inventory details
[hostlist]
host1 ansible_become_user=dev_sudo_user
host2 ansible_become_user=qa_sudo_user
---
- hosts: hostlist
connection: ssh
gather_facts: false
remote_user: abcd
serial: 1
become: true
tasks:
- name: run Script
shell: python apps.py
register: result
- debug: var=result
I am able to get result for one host. I want result for both hosts
you have to use the become and become_user arguments. see example and output below.
hosts files with the variable as you had:
[test_group]
rhel-green become_user=root
rhel-blue become_user=devops
playbook:
- hosts: test_group
gather_facts: false
tasks:
- name: step 1
shell: "id"
register: shell_output
become_user: "{{ become_user }}"
become: true
- debug:
var: shell_output.stdout
execution output:
[root#ansible]# ansible-playbook -i hosts become_loop.yml
PLAY [test_group] ***************************************************************************************************************************************************************************************************
TASK [step 1] *******************************************************************************************************************************************************************************************************
changed: [rhel-blue]
changed: [rhel-green]
TASK [debug] ********************************************************************************************************************************************************************************************************
ok: [rhel-green] => {
"shell_output.stdout": "uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023"
}
ok: [rhel-blue] => {
"shell_output.stdout": "uid=1000(devops) gid=1000(devops) groups=1000(devops) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023"
}
PLAY RECAP **********************************************************************************************************************************************************************************************************
rhel-blue : ok=2 changed=1 unreachable=0 failed=0
rhel-green : ok=2 changed=1 unreachable=0 failed=0
[root#ansible]#

Passing variables between nested playbooks

I have a playbook where I'm spinning up an instance in aws with the ec2 module. To be more flexible I ask via prompt for the hostname. I found in the ec2 examples the code snippet, which allows you to run a second playbook with newly spun up instance for further configuration.
Now I want to set the hostname via module hostname but I cannot access the variable in the second playbook.
This is how my playbook looks like:
---
- hosts: localhost
...
vars_prompt:
- name: var_hostname
prompt: "Please enter the hostname"
private: no
tasks:
- name: Spin up instance
local_action:
module: ec2
...
register: ec2
- name: Add new instance to host group
add_host: hostname={{ item.public_ip }} groupname=launched
with_items: ec2.instances
- hosts: launched
...
tasks:
- name: Set hostname
hostname: name="{{ var_hostname }}"
fatal: [launched] => One or more undefined variables: 'var_hostname' is undefined
Is there a way to pass a variable from one playbook to another one?
I found Ansible best practice for passing vars to nested playbooks? but unfortunately it didn't had a solution which I can use.
You can use set_fact and hostvars together to achieve what you want.
Do set_fact on one group of hosts( i.e localhost), and access them in a different play using hostvars
{{hostvars['localhost']["new_fact"]}}
You can use local files.
1) Write
- name: write public ip
local_action:
template:
dest: /tmp/ansible_master_public_ip.txt
src: templates/public_ip.j2
2) Retrieve with http://docs.ansible.com/ansible/playbooks_lookups.html
hostname: "{{ lookup('file', '/tmp/ansible_master_public_ip.txt') | trim }}"
PS. Ini file lookup also an option if you need more than few variables.
Add the host's variable to the parameters. For example,
- name: Add new instance to host group
add_host:
hostname: "{{ ec2.instances.0.public_ip }}"
groupname: launched
var_hostname: "{{ var_hostname }}"
See examples
Use only the first item from the list because you have only one hostname. There is no reason to iterate the list.
Example of a complete playbook for testing
- hosts: localhost
gather_facts: false
vars_prompt:
- name: var_hostname
prompt: "Please enter the hostname"
private: no
vars:
ec2:
instances:
- public_ip: AAA.BBB.CCC.DDD
tasks:
- name: Add new instance to host group
add_host:
hostname: "{{ ec2.instances.0.public_ip }}"
groupname: launched
var_hostname: "{{ var_hostname }}"
- hosts: launched
gather_facts: false
tasks:
- debug:
var: var_hostname
shell> ansible-playbook pb.yml
Please enter the hostname: host_2
PLAY [localhost] *****************************************************************************
TASK [Add new instance to host group] ********************************************************
changed: [localhost]
PLAY [launched] ******************************************************************************
TASK [debug] *********************************************************************************
ok: [AAA.BBB.CCC.DDD] =>
var_hostname: host_2
PLAY RECAP ***********************************************************************************
AAA.BBB.CCC.DDD: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Use pause instead of vars_prompt if you have more hosts. For example,
- name: Get var_hostname(s)
pause:
prompt: "Please enter the hostname for {{ item.public_ip }}"
echo: yes
loop: "{{ ec2.instances }}"
register: var_hostnames
- name: Add new instances to host group
add_host:
hostname: "{{ item.item.public_ip }}"
groupname: launched
var_hostname: "{{ item.user_input }}"
loop: "{{ var_hostnames.results }}"
loop_control:
label: "{{ item.user_input }}"
Example of a complete playbook for testing
- hosts: localhost
gather_facts: false
vars:
ec2:
instances:
- public_ip: AAA.BBB.CCC.DD1
- public_ip: AAA.BBB.CCC.DD2
tasks:
- name: Get var_hostname(s)
pause:
prompt: "Please enter the hostname for {{ item.public_ip }}"
echo: yes
loop: "{{ ec2.instances }}"
register: var_hostnames
- name: Add new instances to host group
add_host:
hostname: "{{ item.item.public_ip }}"
groupname: launched
var_hostname: "{{ item.user_input }}"
loop: "{{ var_hostnames.results }}"
loop_control:
label: "{{ item.user_input }}"
- hosts: launched
gather_facts: false
tasks:
- debug:
var: var_hostname
shell> ansible-playbook pb.yml
PLAY [localhost] *****************************************************************************
TASK [Get var_hostname(s)] *******************************************************************
[Get var_hostname(s)]
Please enter the hostname for AAA.BBB.CCC.DD1:
host_1^Mok: [localhost] => (item={'public_ip': 'AAA.BBB.CCC.DD1'})
[Get var_hostname(s)]
Please enter the hostname for AAA.BBB.CCC.DD2:
host_2^Mok: [localhost] => (item={'public_ip': 'AAA.BBB.CCC.DD2'})
TASK [Add new instances to host group] *******************************************************
ok: [localhost] => (item=host_1)
ok: [localhost] => (item=host_2)
PLAY [launched] ******************************************************************************
TASK [debug] *********************************************************************************
ok: [AAA.BBB.CCC.DD1] =>
var_hostname: host_1
ok: [AAA.BBB.CCC.DD2] =>
var_hostname: host_2
PLAY RECAP ***********************************************************************************
AAA.BBB.CCC.DD1: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
AAA.BBB.CCC.DD2: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Resources