Ansible: How to check a value: key pair in the when condition - ansible

First of all, I have separated my playbooks in this way:
environtments/something/host_vars/host1.yml
roles/sshd/tasks/main.yml
The host's environment variable contains some value: key pairs, in this case:
specialinfo: 'blah'
And the role contains a block, which should be run when this 'specialinfo' value matches to blah:
- name: Check that specialvalue is blah
block:
- name: Do something
...
- name: Do something else
...
when: {{ specialinfo }} == 'blah'
Please help me to achive this, because I'm quite sure that the problem is inside the 'when' condition. If I need a copy (or something like that) inside a name, I can use {{ specialinfo }}
Please also notice that I can't use with_items here, because it can't be used inside the block element.

First of all, you can't use double curly braces in when because the condition itself needs to be a jinja expression. Change the condition to when: specialinfo == 'blah'.
Note that, when condition for block applies to all the tasks inside the block. This means that the same ... == 'blah' condition will be applied to both Do something and Do something else tasks. If you want to control when a particular task to be executed, use when for the task alone like so:
- debug: Do something
msg: is blah
when: specialinfo == 'blah'
- debug: Do something else
msg: is not blah
when: specialinfo != 'blah'

Related

include role if variable is true

I want a role to be included in my Ansible playbook if a variable value is true and not executed if it is false. I thought this should work but the integration test gets run even if run_integration_test is false
- include_role:
name: integration_test
when: "{{ run_integration_test }}"
tags:
- configure_integration
- aws
What is the correct way to do this?
You have an indentation problem: when is a task option, not an argument to include_role. You need to write:
- include_role:
name: integration_test
when: run_integration_test
tags:
- configure_integration
- aws
Notice that when is aligned with tags in this example.
There's a trap here you might hit depending on how you're setting run_integration_test. You might try running something like this:
ansible-playbook -e run_integration_test=false ...
And you would be surprised that the task still runs. In the above command line, we're setting run_integration_test to the string value "false" rather than to boolean false, and a non-empty string evaluates to true. One way to deal with this is to write your when expression like this:
when: "run_integration_test|default(false)|bool"
This will (a) default to false if run_integration_test is unsent, and (b) will cast string values "true" and "false" to the correct boolean value using the bool filter.

Check if arrays are defined and not empty in ansible

I have the following code
- set_fact:
MY_HOSTNAME: "SOME VALUE"
MY_SERVER: "00.00.00.00"
- name: Get MY server
set_fact:
MY_SERVER: "{{ groups[MY_HOSTNAME][0] }}"
when: groups[MY_HOSTNAME] is defined
In the above code, groups[MY_HOSTNAME] is an array. What is the best way to check that groups[MY_HOSTNAME] is defined and also that it is not empty
If it is either of that I want the value 00.00.00.00 to be assigned to MY_SERVER
I don't know if it's version specific, but I'm currently running ansible-2.3.2 on RHEL6 and I had to put quotes around the group name to get it to work for me:
when: groups["GROUP_NAME"] is defined and (groups["GROUP_NAME"]|length>0)
Edit: I couldn't add this as a comment to techraf's answer because I don't have enough reputation.
list | length filter returns the number of elements. If it is zero, the list is empty.
For a conditional value use if or ternary filter (example in this answer).
For a composite conditional (groups[MY_HOSTNAME]| default([])) | length.
You can first check if it is indeed a list and then check if it has more than one element:
when: (groups['MY_HOSTNAME'] is defined) and (groups['MY_HOSTNAME'] | type_debug == 'list') and (groups['MY_HOSTNAME'] | length > 0)
try this one (it checks if variable is defined and if it is a iterable like list)
when: (groups["GROUP_NAME"] is defined) and (groups["GROUP_NAME"] is iterable)

Ansible - 'Undefined' at beginning and end of Key when map'ing a Attribute

When map'ing a attribute in a list of variables, Ansible is adding a 'Undefined' to the beginning and end of the key.
The variables:
vault_config_listener_params:
- address: "0.0.0.0:8200"
- tls_cert_file: "/etc/ssl/certs/wildcard.crt"
- tls_key_file: "/etc/ssl/certs/wildcard.key"
The debug task:
- debug: var=vault_config_listener_params|map(attribute="tls_cert_file")|list
The output:
ok: [id70118] => {
"vault_config_listener_params|map(attribute=\"tls_cert_file\")|list":
"[Undefined, u'/etc/ssl/certs/wildcard.crt', Undefined]"
}
The maping seems to have worked, as the key path has been extracted. But where are the 'Undefined' coming from?
PS: The variables needs to be a list, as they are looped it in another place with jinja2.
First of all, don't use debug's var when printing arbitrary expressions, use msg instead.
As for your question, map is quite dumb and doesn't do what you don't ask it to, so you actually need to select items with specified attributes defined first, and then get its values:
- debug:
msg: "{{ vault_config_listener_params | selectattr('tls_cert_file','defined') | map(attribute='tls_cert_file') | list }}"

Ansible, set_fact using if then else statement

I am trying to set a variable in Ansible with set_fact at runtime based upon another variable. If uses first value no matter what the actual value is. Here is my code example:
- name: Global_vars - get date info
set_fact:
jm_env: "{{lookup('env', 'Environment')}}"
l_env: "{% if '{{jm_env}}==Develop' %}d{% elif '{{jm_env}}==Staging'%}s{% else %}p{% endif %}"
l_env is d no matter what jm_env is set.
Firstly, dictionaries in YAML are not ordered (and the syntax used by Ansible here is a YAML dictionary), so you have no guarantee Ansible would first set jm_env before proceeding to l_env -- you need to split the assignment into two tasks.
Secondly, your test expressions are incorrect -- '{{jm_env}}==Develop' is a string because it is quoted; and testing if 'string' will always evaluate to true (this is the direct reason you always get d in the output).
Use:
- name: Set the jm_env
set_fact:
jm_env: "{{lookup('env', 'Environment')}}"
- name: Set the l_env
set_fact:
l_env: "{% if jm_env=='Develop' %}d{% elif jm_env=='Staging'%}s{% else %}p{% endif %}"
One of the simple way to set fact based condition example as follows:
- name: Set facts for delete operation results
set_fact:
tr_result: "{{ '{\"status\": \"SUCCESS\"}' if (op_result['output'] == 'Deleted') else '{\"status\" : \"FAILED\"}' }}"
Note: Assume op_result is a dict & already defined.
Code has been tested and working well.

Ansible - Find max value and run action based on a result only on one host

I have a group of hosts named "db" with number of nodes that may vary. Each node has a fact ("seqno") which is an integer number.
I need to compare this fact among all hosts and choose maximum value, then run some actions on one (and only one) host that has this maximum value. In case of multiple nodes having the same value, first node should be chosen.
I tried this approach:
- name: find max seqno value
set_fact: seqno_max={{ [hostvars[groups['db'][0]]['seqno'], hostvars[groups['db'][1]]['seqno']] | max }}
- name: find single hostname to use as a node with max seqno
set_fact: seqno_max_host={{ hostvars[item]['inventory_hostname'] }}
with_items: groups['db'][::-1] # reverse list. if two nodes have the same seqno, use first node as starting point.
when: hostvars[item]['seqno'] == seqno_max
- name: Some actions based on a result of previous tasks
action: # Run some actions
when: seqno_max_host == inventory_hostname
But for some reason "max" operator always return the second value. Also this approach is valid only if you have arbitrary specified number of hosts - I would like to have a solution that works for any number of hosts.
EDIT: It turned out that hostvars converted integer back to string, so comparison of them gave unexpected results. Reapplying int filter for each hostvars reference inside max filter helped. Still, questions remains how to fix code above to make it work for any number of hosts - is it possible without writing custom filters or creating temporary templates?
I ended up with a solution which is not beautiful, but works.
- shell: "if [ {{ hostvars[inventory_hostname]['seqno'] }} -lt {{ hostvars[item]['seqno'] }} ]; then echo {{ hostvars[item]['seqno'] }}; fi"
with_items: groups['db']
register: result_c
- set_fact: seqno_max={{ hostvars[inventory_hostname]['seqno'] }}
when: result_c.results | map(attribute='stdout') | join('') == ""

Resources