Ansible: Run a task only when variable is not empty - variables

I have following play book:
test2.yml:
- hosts: localhost
connection: local
gather_facts: false
vars:
dict1:
v1:
- 111
- 222
- 333
v2:
ver: "{{ ver }}"
tasks:
- name: Gather list
set_fact: rblist="{{ pitem }}"
with_dict: "{{ dict1 }}"
when: "pitem.key in ver"
loop_control:
loop_var: pitem
register: plist
- name: lets include the task if the value is not empty
include: test3.yml
when: rblist.value
test3.yml:
---
- name: display value if not empty
debug: msg={{ rblist.value }}
I'd like to invoke test3.yml only when rblist.value is not empty, but its ignoring that and invokes the task even when the value is empty.
When the value is not empty: it works fine
ansible-playbook test2.yml -e "ver=v1"
PLAY [localhost] ************************************************************************************************************************************************************************************************************************
TASK [Gather list] **********************************************************************************************************************************************************************************************************************
ok: [localhost] => (item={u'key': u'v1', u'value': [111, 222, 333]})
TASK [display value if not empty] *******************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
111,
222,
333
]
}
PLAY RECAP ******************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
When the value is empty: seems to behave same as when the list
ansible-playbook test2.yml -e "ver=v2"
PLAY [localhost] ************************************************************************************************************************************************************************************************************************
TASK [Gather list] **********************************************************************************************************************************************************************************************************************
ok: [localhost] => (item={u'key': u'v2', u'value': None})
TASK [display value if not empty] *******************************************************************************************************************************************************************************************************
PLAY RECAP ******************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Following How to test that a registered variable is not empty? and other solutions, I have tried the following
when: rblist.value | length > 0
I get this:
TASK [display value if not empty] *******************************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The conditional check 'rblist.value | length > 0' failed. The error was: Unexpected templating type error occurred on ({% if rblist.value | length > 0 %} True {% else %} False {% endif %}): object of type 'NoneType' has no len()\n\nThe error appears to be in '/stage/ap/ansible/test/test3.yml': line 2, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n---\n - name: display value if not empty\n ^ here\n"}
When I try this:
when: rblist.value != None
I get this:
ansible-playbook test2.yml -e "ver=v2"
PLAY [localhost] ************************************************************************************************************************************************************************************************************************
TASK [Gather list] **********************************************************************************************************************************************************************************************************************
ok: [localhost] => (item={u'key': u'v2', u'value': None})
TASK [display value if not empty] *******************************************************************************************************************************************************************************************************
PLAY RECAP ******************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
I Tried this:
when: rblist.value != ""
Same results as above that is when used != None

You probably have
display_skipped_hosts = false
in your ansible.cfg file.
Where display_skipped_hosts:
Toggle to control displaying skipped task/host entries in a task in the default callback
Source: https://docs.ansible.com/ansible/2.9/reference_appendices/config.html#display-skipped-hosts
With this option set this way, you will not see skipped host in the play itself, but you can see them in your recap:
PLAY RECAP ********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Where the important part is skipped=1.
You will thus still see the task description, but nothing under it, confirming that the task was skipped.

Related

Overwrite vars_prompt variable in playbook with host variable from inventory in Ansible

I want to overwrite some variables in my playbook file from the inventory file for a host that are defined as "vars_prompt". If I understand it correctly, Ansible shouldn't prompt for the variables if they were already set before, however, it still prompts for the variables when I try to execute the playbook.
How can I overwrite the "vars_prompt" variables from the inventory or is this not possible because of the variable precedence definition of Ansible?
Example:
playbook.yml
---
- name: Install Gateway
hosts: all
become: yes
vars_prompt:
- name: "hostname"
prompt: "Hostname"
private: no
...
inventory.yml
---
all:
children:
gateways:
hosts:
gateway:
ansible_host: 192.168.1.10
ansible_user: user
hostname: "gateway-name"
...
Q: "If I understand it correctly, Ansible shouldn't prompt for the variables if they were already set before, however, it still prompts for the variables when I try to execute the playbook."
A: You're wrong. Ansible won't prompt for variables defined by the command line --extra-vars. Quoting from Interactive input: prompts:
Prompts for individual vars_prompt variables will be skipped for any variable that is already defined through the command line --extra-vars option, ...
You can't overwrite vars_prompt variables from the inventory. See Understanding variable precedence. Inventory variables (3.-9.) is lower precedence compared to play vars_prompt (13.). The precedence of extra vars is 22.
Use the module pause to ask for the hostname if any variable is not defined. For example, the inventory
shell> cat hosts
host_1
host_2
and the playbook
hosts: all
gather_facts: false
vars:
hostnames: "{{ ansible_play_hosts_all|
map('extract', hostvars, 'hostname')|
list }}"
hostnames_undef: "{{ hostnames|from_yaml|
select('eq', 'AnsibleUndefined')|
length > 0 }}"
tasks:
- debug:
msg: |
hostnames: {{ hostnames }}
hostnames_undef: {{ hostnames_undef }}
run_once: true
- pause:
prompt: "Hostname"
register: out
when: hostnames_undef
run_once: true
- set_fact:
hostname: "{{ out.user_input }}"
when: hostname is not defined
- debug:
var: hostname
gives
shell> ansible-playbook pb.yml
PLAY [all] ************************************************************************************
TASK [debug] **********************************************************************************
ok: [host_1] =>
msg: |-
hostnames: [AnsibleUndefined, AnsibleUndefined]
hostnames_undef: True
TASK [pause] **********************************************************************************
[pause]
Hostname:
gw.example.com^Mok: [host_1]
TASK [set_fact] *******************************************************************************
ok: [host_1]
ok: [host_2]
TASK [debug] **********************************************************************************
ok: [host_1] =>
hostname: gw.example.com
ok: [host_2] =>
hostname: gw.example.com
PLAY RECAP ************************************************************************************
host_1: ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host_2: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The playbook won't ovewrite variables defined in the inventory. For example
shell> cat hosts
host_1
host_2 hostname=gw2.example.com
gives
TASK [debug] **********************************************************************************
ok: [host_1] =>
hostname: gw.example.com
ok: [host_2] =>
hostname: gw2.example.com
I don't know if you can stop the prompts but you can se a default value directly in vars_prompts. In this way you do not need to type "gateway-name" every time.
vars_prompt:
- name: "hostname"
prompt: "Hostname"
private: no
default: "gateway-name"
Source: https://docs.ansible.com/ansible/latest/user_guide/playbooks_prompts.html

Ansible: How do you properly skip ssh first connection to fresh host?

Context: I'm trying to automate the provision of a fresh new server, but when a new machine is spawned and my ansible playbook is played against it from my provisioning server the usual message pops out:
The authenticity of host '192.168.1.25 (192.168.1.25)' can't be established.
ECDSA key fingerprint is SHA256:QF/AyFhYXaz5bjZ1O+kvceoOjBzmI8M1PYmg3lukYmE.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
I am aware this question has been answered a couple times already but, I do not want to add this line to my .cfg file or give the relative argument when I launch an ansible-playbook command.
Problem: So this answer came to my attention https://stackoverflow.com/a/54735937/18647199
I copy pasted the two tasks in my playbook and if they're by themselves the script runs properly. Skipping the aforementioned prompt (even though it skips it on one server that I still have to made the first connection) see:
TASK [Check known_hosts for 192.168.1.14] **************************************
ok: [192.168.1.16 -> localhost]
ok: [192.168.1.14 -> localhost]
ok: [192.168.1.25 -> localhost]
TASK [Ignore host key for 192.168.1.14 on first run] ***************************
skipping: [192.168.1.14]
skipping: [192.168.1.16]
skipping: [192.168.1.25]
PLAY RECAP *********************************************************************
192.168.1.14 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
192.168.1.16 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
192.168.1.25 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
But if I add just one more task to it, it asks again for the auth prompt that I'm trying to skip.
p.s. using OpenSSH, latest current version.
What I'm trying to run:
---
#all
- hosts: all
#connection: local
become: true
gather_facts: false #otherwise ssh prompt appears
tasks:
- name: Check known_hosts
local_action: shell ssh-keygen -F "{{ inventory_hostname }}"
register: is_known
failed_when: false
changed_when: false
ignore_errors: yes
- name: debug message
debug:
msg: the "{{ inventory_hostname }}"" was tested with output "{{ is_known }}"
- name: Ignore host key for "{{ inventory_hostname }}" on first run
when: is_known.rc == 1
set_fact:
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
- name: Bootstrap check
stat:
path: /home/bot/bootstrapped-ok
register: bootstrap_result
[..] more code
Debug output:
ansible-playbook debug-bootstrap.yml
PLAY [all] *********************************************************************
TASK [Check known_hosts] *******************************************************
ok: [192.168.1.16 -> localhost]
ok: [192.168.1.14 -> localhost]
ok: [192.168.1.25 -> localhost]
TASK [debug message] ***********************************************************
ok: [192.168.1.14] => {
"msg": "the \"192.168.1.14\"\" was tested with output \"{'msg': 'non-zero return code', 'cmd': 'ssh-keygen -F \"192.168.1.14\"', 'stdout': '', 'stderr': 'do_known_hosts: hostkeys_foreach failed: No such file or directory', 'rc': 255, 'start': '2022-04-02 12:30:50.940041', 'end': '2022-04-02 12:30:50.943287', 'delta': '0:00:00.003246', 'changed': False, 'failed': False, 'stdout_lines': [], 'stderr_lines': ['do_known_hosts: hostkeys_foreach failed: No such file or directory'], 'failed_when_result': False}\""
}
ok: [192.168.1.16] => {
"msg": "the \"192.168.1.16\"\" was tested with output \"{'msg': 'non-zero return code', 'cmd': 'ssh-keygen -F \"192.168.1.16\"', 'stdout': '', 'stderr': 'do_known_hosts: hostkeys_foreach failed: No such file or directory', 'rc': 255, 'start': '2022-04-02 12:30:50.937097', 'end': '2022-04-02 12:30:50.941015', 'delta': '0:00:00.003918', 'changed': False, 'failed': False, 'stdout_lines': [], 'stderr_lines': ['do_known_hosts: hostkeys_foreach failed: No such file or directory'], 'failed_when_result': False}\""
}
ok: [192.168.1.25] => {
"msg": "the \"192.168.1.25\"\" was tested with output \"{'msg': 'non-zero return code', 'cmd': 'ssh-keygen -F \"192.168.1.25\"', 'stdout': '', 'stderr': 'do_known_hosts: hostkeys_foreach failed: No such file or directory', 'rc': 255, 'start': '2022-04-02 12:30:50.978944', 'end': '2022-04-02 12:30:50.982119', 'delta': '0:00:00.003175', 'changed': False, 'failed': False, 'stdout_lines': [], 'stderr_lines': ['do_known_hosts: hostkeys_foreach failed: No such file or directory'], 'failed_when_result': False}\""
}
TASK [Ignore host key for "192.168.1.14" on first run] *************************
skipping: [192.168.1.14]
skipping: [192.168.1.16]
skipping: [192.168.1.25]
TASK [Bootstrap check] *********************************************************
The authenticity of host '192.168.1.25 (192.168.1.25)' can't be established.
ECDSA key fingerprint is SHA256:QF/AyFhYXaz5bjZ1O+kvceoOjBzmI8M1PYmg3lukYmE.
Are you sure you want to continue connecting (yes/no/[fingerprint])? ok: [192.168.1.16]
ok: [192.168.1.14]
So it seems like the command shell ssh-keygen -F "{{ inventory_hostname }}" isn't doing what it's supposed to do as if we had to launch that via terminal.
Question: Does anyone know how to implement that "one-time skip" or has a better way to do this for a fully automated provisioning / deploy?
(I tried to create an unique .yml file with scarce results, I hit a wall and have not many ideas left on how to continue a fully automated provisioning)
Just added mine answer to How to ignore ansible SSH authenticity checking? which list lots of options.
This is what we are using for stable hosts (when running the playbook from Jenkins and you simply want to accept the host key when connecting to the host for the first time) in inventory file:
[all:vars]
ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new'
And this is what we have for temporary hosts (in the end this will ignore they host key at all):
[all:vars]
ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
There is also environment variable or you can add it into group/host variables file. No need to have it in the inventory - it was just convenient in our case.
Maybe this could help?

Ansible how to assign new value to extra vars value

Tower: 3.2.3
Ansible 2.4.2
I have a Tower playbook where a value is assigned lets say build_cl: latest. This is defined in the Ansible Tower's survey, which I believe is regarded as extra-vars. I have a task that performs a check, and if condition is right I need to modify the value of build_cl.
So lets say when Tower playbook is kicked off the var is:
build_cl: latest
Then:
- name: "Get latest installed CL on groups['Healthcheck_Host'][0]"
shell: |
grep -oP '(?<=\:)(.*?)(?=\-)' {{ latest_deployed_build_dir.stdout }}/buildinfo.txt
register: latest_deployed_cl
- debug:
var: latest_deployed_cl
- set_fact:
build_cl: "{{ latest_deployed_cl.stdout }}"
cacheable: yes
- debug:
var: build_cl
I have tested and the debug for the first task here returns lets say 123456.
However I try to use the set_fact module, but the second debug output still gives: latest.
Nothing I try seems to be effecting the original value. Help would be greatly appreciated. Thanks
Extra vars (i.e. vars passed on the command line with the -e option), have the highest precedence and cannot be changed during playbook life. set_fact will not throw any error but the value will remain the one passed at launch.
Here is a quick example to illustrate:
---
- name: Immutable extra var demo
hosts: localhost
gather_facts: false
vars:
test_var: value set in playbook var
tasks:
- name: debug var value at playbook start
debug:
var: test_var
- name: change var value
set_fact:
test_var: value set in set_fact
- name: debug var value at playbook end
debug:
var: test_var
Without extra var:
$ ansible-playbook test.yml
PLAY [Immutable extra var demo] ********************************************************************************************************************************************************************************************************
TASK [debug var value at playbook start] ***********************************************************************************************************************************************************************************************
ok: [localhost] => {
"test_var": "value set in playbook var"
}
TASK [change var value] ****************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug var value at playbook end] *************************************************************************************************************************************************************************************************
ok: [localhost] => {
"test_var": "value set in set_fact"
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
With extra var:
$ ansible-playbook test.yml -e "test_var='value set in extra vars'"
PLAY [Immutable extra var demo] ********************************************************************************************************************************************************************************************************
TASK [debug var value at playbook start] ***********************************************************************************************************************************************************************************************
ok: [localhost] => {
"test_var": "value set in extra vars"
}
TASK [change var value] ****************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug var value at playbook end] *************************************************************************************************************************************************************************************************
ok: [localhost] => {
"test_var": "value set in extra vars"
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible variable interpolation not working in brackets

I'm using Ansible to grab an VIP from InfoBlox and running into an issue when trying to build my exclude list. I need to exclude the first 3 IP's as they are reserved, however the CIDR range is a dynamic variable so I need to build this exclude list on the fly. Here's a representation of what I have so far:
---
- name: Assign VIP to FQDN in Grid Manager
hosts: all
connection: local
gather_facts: false
vars:
CIDR: 10.0.0.
tasks:
- set_fact:
EXCLUDES: "{{ EXCLUDES | default([]) + [item] }}"
with_sequence: start=1 end=3 format=''{{ CIDR }}%d''
- set_fact:
formated_excludes: "{{ EXCLUDES |join(', ') }}"
- debug:
msg: "{{ formated_excludes }}"
- set_fact:
VIP: "{{ lookup('nios_next_ip', '10.0.0.0/25', exclude=[formated_excludes]) | first }}"
- name: Print next IP address
debug:
msg: "The next available IP is {{ VIP }}"
Here are the results of the play:
PLAY [Assign VIP to FQDN in Grid Manager] *****************************************************************************************************************
TASK [set_fact] *******************************************************************************************************************************************
ok: [localhost] => (item='10.0.0.1')
ok: [localhost] => (item='10.0.0.2')
ok: [localhost] => (item='10.0.0.3')
TASK [set_fact] *******************************************************************************************************************************************
ok: [localhost]
TASK [debug] **********************************************************************************************************************************************
ok: [localhost] => {
"msg": "'10.0.0.1', '10.0.0.2', '10.0.0.3'"
}
TASK [set_fact] *******************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "An unhandled exception occurred while running the lookup plugin 'nios_next_ip'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Invalid value for exclude: \"'10.0.0.1', '10.0.0.2', '10.0.0.3'\": Invalid IPv4 address"}
PLAY RECAP ************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
The formatting appears to be fine, however the lookup won't accept it. For comparison, using the following works fine:
- set_fact:
VIP: "{{ lookup('nios_next_ip', '10.0.0.0/25', exclude=['10.0.0.1', '10.0.0.2', '10.0.0.3']) | first }}"
Here are the results:
PLAY [Assign VIP to FQDN in Grid Manager] *****************************************************************************************************************
TASK [set_fact] *******************************************************************************************************************************************
ok: [localhost]
TASK [Print next IP address] ******************************************************************************************************************************
ok: [localhost] => {
"msg": "The next available IP is 10.0.0.4"
}
PLAY RECAP ************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Any thoughts on why this variable isn't working would be much appreciated!
Parameter exclude is "List of IP's ...", but the filter join makes a string from the list
- set_fact:
formated_excludes: "{{ EXCLUDES |join(', ') }}"
Use the list EXCLUDES in the parameter
- set_fact:
VIP: "{{ lookup('nios_next_ip', '10.0.0.0/25', exclude=EXCLUDES) | first }}"

Unable to get value of a variable from another Play in Ansible

I wish to obtain the value of "fdet_APP" variable of Play 2 into Play 3.
Below is my playbook which case be used as a testcase:
- name: "Play 1"
hosts: localhost
tasks:
- add_host: name={{ item }}
groups=dest_nodes
ansible_user={{ USER }}
with_items: "{{ Dest_IP.split(',') }}"
- name: "Play 2"
hosts: dest_nodes
user: "{{ USER }}"
tasks:
- set_fact:
fdet_APP: "Yellow"
- name: "Play 3"
hosts: localhost
user: "{{ USER }}"
vars:
dbfiledet: "{{ hostvars['dest_nodes']['fdet_APP'] }}"
tasks:
- debug: msg="{{ dbfiledet.stdout }}"
I get the below error for my attempt:
playbook RUN command:
ansible-playbook variabletest.yml -e "USER=user1 Dest_IP=10.17.44.26,10.17.54.26"
[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 [add_host]
********************************************************************************************************************************************* changed: [localhost] => (item=10.17.44.26) changed: [localhost] =>
(item=10.17.54.26)
PLAY [Play 2]
TASK [Gathering Facts]
************************************************************************************************************************************** ok: [10.17.54.26] ok: [10.17.44.26]
TASK [set_fact]
********************************************************************************************************************************************* ok: [10.17.44.26] ok: [10.17.54.26]
PLAY [Play 3]
TASK [Gathering Facts]
************************************************************************************************************************************** ok: [localhost]
TASK [debug]
************************************************************************************************************************************************ fatal: [localhost]: FAILED! => {"msg": "The task includes an option
with an undefined variable. The error was: \"hostvars['dest_nodes']\"
is undefined\n\nThe error appears to be in 'variabletest.yml': line
36, column 6, but may\nbe elsewhere in the file depending on the exact
syntax problem.\n\nThe offending line appears to be:\n\n\n - debug:
msg=\"{{ dbfiledet.stdout }}\"\n ^ here\nWe could be wrong, but
this one looks like it might be an issue with\nmissing quotes. Always
quote template expression brackets when they\nstart a value. For
instance:\n\n with_items:\n - {{ foo }}\n\nShould be written
as:\n\n with_items:\n - \"{{ foo }}\"\n"}
PLAY RECAP
10.17.44.26 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.17.54.26 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 localhost
: ok=3 changed=1 unreachable=0 failed=1 skipped=0
rescued=0 ignored=0
I'm on the latest version of ansible and python 2.7.5
Can someone suggest what is wrong and how can i get the value for the variable in Play 3 please ?
hostvars are tied to a single Ansible managed host, not an inventory group. Try running debug: var=hostvars to see what I mean. In your case dest_nodes is an inventory group, not a host.
If just want to pull the variable from any host in the group, try:
- debug:
msg: "{{ hostvars[groups.dest_nodes|first]['fdet_APP'] }}"
If you are looking to parse the value for all hosts in the group, then you'll need to implement either a loop or a json_query filter