Checking for multiple conditions using "when" on single task in ansible - ansible

I want to evaluate multiple condition in ansible using when, here is my playbook:
- name: Check that the SSH Key exists
local_action:
module: stat
path: "/home/{{ login_user.stdout }}/{{ ssh_key_location }}"
register: sshkey_result
- name: Generating a new SSH key for the current user it's not exists already
local_action:
module: user
name: "{{ login_user.stdout }}"
generate_ssh_key: yes
ssh_key_bits: 2048
when: sshkey_result.rc == 1 and ( github_username is undefined or github_username |lower == 'none' )
here is my var file for reference:
---
vpc_region: eu-west-1
key_name: my_github_key
ssh_key_location: .ssh/id_rsa.pub
When I try to execute this playbook, I am getting this error:
TASK: [test | Check that the SSH Key exists] **********************************
ok: [localhost -> 127.0.0.1]
TASK: [test | Generating a new SSH key for the current user it's not exists already] ***
fatal: [localhost] => error while evaluating conditional: sshkey_result.rc == 1 and ( github_username is undefined or github_username |lower == 'none' )
FATAL: all hosts have already failed -- aborting
Can somebody point me out that how we can use multiple conditions with ansible on single task.
Thanks

You can use like this.
when: condition1 == "condition1" or condition2 == "condition2"
Link to official docs: The When Statement.
Also Please refer to this gist:
https://gist.github.com/marcusphi/6791404

Adding to https://stackoverflow.com/users/1638814/nvartolomei answer, which will probably fix your error.
Strictly answering your question, I just want to point out that the when: statement is probably correct, but would look easier to read in multiline and still fulfill your logic:
when:
- sshkey_result.rc == 1
- github_username is undefined or
github_username |lower == 'none'
https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#the-when-statement

The problem with your conditional is in this part sshkey_result.rc == 1, because sshkey_result does not contain rc attribute and entire conditional fails.
If you want to check if file exists check exists attribute.
Here you can read more about stat module and how to use it.

You can use logical operators to combine conditions. When you have multiple conditions that all need to be true (that is, a logical and), you can specify them as a list:
tasks:
- name: Shut down CentOS 6 systems
ansible.builtin.command: /sbin/shutdown -t now
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6"
Link to the doc : https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#the-when-statement

Also you can use default() filter. Or just a shortcut d()
- name: Generating a new SSH key for the current user it's not exists already
local_action:
module: user
name: "{{ login_user.stdout }}"
generate_ssh_key: yes
ssh_key_bits: 2048
when:
- sshkey_result.rc == 1
- github_username | d('none') | lower == 'none'

Related

Ansible assert failure when source list holds more values than target list

I want to assert network configuration with our inventory using a single assert task.
- name: Get running class-map config
vars:
ansible_connection: network_cli
ios_command:
commands:
- 'sh run | in class-map match-any'
register: show_policy
- name: Render template to variable
set_fact:
tem_var: "{{ lookup( 'template', 'test_policy.j2' ).splitlines() }}"
- name: Validate running class-map with acl
ansible.builtin.assert:
that:
- "'{{item}}' in show_policy.stdout_lines[0]"
success_msg: "TEST: {{ UNIT_HOSTNAME }}: VALIDATE RUNNING POLICY: PASSED"
fail_msg: "TEST: {{ UNIT_HOSTNAME }}: VALIDATE RUNNING POLICY: FAILED"
loop: "{{ tem_var }}"
Above assert works fine, but the configuration on the router sometimes holds more configuration than what is expected. I would like to use the same assert task as I am already using. So above assert should be used to also compare the 2 variables
"tem_var" is a variable generated from out inventory files.
Our inventory has for example 8 configuration lines, and the router has 9.
In that case I wan't the assert to fail, if the number of lines in each variable "show_policy" and "tem_var" is not the same.
I have done it succesfully with 2 additional tasks, but it would be nice if the assert module could fail instead.
This is working, but it adds up the number of tasks running:
- name: Consistency check running
set_fact: diff_list="{{ show_policy.stdout_lines[0] | difference(tem_var) }}"
- name: CONSISTENCY CHECK FAILED !! Device has unvalidated config
ansible.builtin.fail:
msg: "CONSISTENCY FAILURE: Please remove {{ diff_list }} from {{ UNIT_HOSTNAME }}"
when: diff_list|default("")|length > 0
How to assert failure when source list holds more values than target list?
According your current question headline and current description
Our inventory has for example 8 configuration lines, and the router has 9.
In that case I wan't the assert to fail, if the number of lines in each variable show_policy and tem_var is not the same.
you would need just to compare the length of both lists.
The minimal example playbook
---
- hosts: localhost
become: false
gather_facts: false
vars:
show_policy:
stdout_lines:
- '1'
- '2'
tem_var:
stdout_lines:
- '1'
- '2'
- '3'
tasks:
- name: Assert failure
ansible.builtin.assert:
that: # the number of lines in each variable is the same
- show_policy.stdout_lines | length == tem_var.stdout_lines | length
success_msg: "VALIDATE RUNNING POLICY: PASSED"
fail_msg: "VALIDATE RUNNING POLICY: FAILED"
will result into an output of
TASK [Assert failure] **********************************************************
fatal: [localhost]: FAILED! => changed=false
assertion: show_policy.stdout_lines | length == tem_var.stdout_lines | length
evaluated_to: false
msg: 'VALIDATE RUNNING POLICY: FAILED'
TASK [Assert failure] **********************************************************
ok: [localhost] => changed=false
msg: 'VALIDATE RUNNING POLICY: PASSED'
depending on the list length.
Further Documentation
assert module – Asserts given expressions are true
Jinja2 Template Designer Documentation - List of Builtin Filters
jinja-filters.length
Follow Ups
If you are interested in the difference between the configuration (list), in example if settings are missing
Ansible: How to compare two list variables?
How to compare lists in Ansible?

Use skip_reason as a condition in a task

Is it possible to use the skip_reason as condition to another task?
Here is the task:
- name: PSP Validation
script: roles/OS_minor_upgrade/files/PSP_validation.sh
ignore_errors: true.
register: PSP_VAL
when: >
not 'VMware' in HWMODEL.stdout
Which output:
TASK [OS_minor_upgrade : PSP Postwork] ******************************************************************************************************************************************************
task path: /home/ansible/linuxpatching_OS_Upgrade/roles/OS_minor_upgrade/tasks/upgrade.yml:264
skipping: [server123] => {
"changed": false,
"skip_reason": "Conditional result was False"
}
Now I want to use the above as condition to execute another task, I tried with the task below but it seem like it is not working.
- name: OSupgrade done
shell: echo {{ inventory_hostname }} "OS Upgrade Done" > OUTGOING-OSUPGRADE-PATCHCOMPLETION/inventory_{{ inventory_hostname }}_{{ '%y%m%d%H%M%S' | strftime }}_Offlineoutput
delegate_to: localhost
when: >
fs_check.rc == 0 and val_etrust.rc == 0 and 'PSP Installation is successfully completed' in PSP_VAL.stdout or 'Conditional result was False' in PSP_VAL.skip_reason
How can this be achieved?
Technically, you can use 'skip_reason' as any other variable, but I STRONGLY suggest you not to.
The reason is that person, reading your code (you, 1 week later) would be in a total loss over such decision.
If you have important information about your host, you can use set_fact module to update your host information. Further tasks can use this to make decisions.
- name: Update vmware info
set_fact:
vmware_flag: ('VMware' in HWMODEL.stdout)
- name: PSP Validation
script: roles/OS_minor_upgrade/files/PSP_validation.sh
failed_when: false
register: PSP_VAL
when: not vmware_flag
- name: OSupgrade done
delegate_to: localhost
copy:
dest: OUTGOING-OSUPGRADE-PATCHCOMPLETION/inventory_{{ inventory_hostname }}_{{ '%y%m%d%H%M%S' | strftime }}_Offlineoutput
content: '{{ inventory_hostname }} "OS Upgrade Done"'
when: (some other conditions) or vmware_flag
There are specific ansible tests to verify the registered result of a task
In your specific case:
when: <...all other conditions...> or PSP_VAL is skipped

Does Ansible have an else condition

So far I can see using a when in ansible to determin whether to run a task but do I have to define 2 tasks to run the alternative option..
example - if I want to run the following task then run the debug task, i need to run two tasks or existStatus will not have been defined for the debug statement. Can I not use some sort of if else statement rather than include 2 separate tasks?
- name: Print user does not exist status
shell: echo 'user does not exist'
when: kafka_configs_result.stdout_lines[1] == '1'
register: existStatus
- name: Print user does not exist status
shell: echo 'user already exists so could not be created'
when: kafka_configs_result.stdout_lines[1] == '0'
register: existStatus
- debug: msg="{{ existStatus.stdout_lines }}"
You can do this in one single task without having to go through an unneeded shell execution. A simple way is to use a test and the ternary filter
I added the var section simply for readability. You can make a long one liner if you wish.
- debug:
vars:
exists_test: "{{ kafka_configs_result.stdout_lines[1] == '1' }}"
msg_exists: "user already exists so could not be created"
msg_notexists: "user does not exist"
msg: "{{ exists_test | ternary(msg_notexists, msg_exists) }}"
You can write something like this to utilize if-loop
- set_fact: build="{% if '<something>' in <something> %}<VALUE>{% else %}<VALUE>{% endif %}"

ansible task with condition

I am using the below task in my play book and trying to set condition using "when" to trigger the task.
the blow one is working
ansible-playbook vdomqury.yml -e adom=In-Adom-01 -e FGT=FGVM04TM19001830 -e vdom= -vvv
if i remove var vdom playbook stopped
ansible-playbook vdomqury.yml -e adom=In-Adom-01 -e FGT=FGVM04TM19001830 -vvv
tasks:
- name: USE CUSTOM TYPE TO QUERY AVAILABLE SCRIPTS
f_query:
adom: "{{ adom }}"
object: "custom"
custom_endpoint: "/pm/config/adom/{{ adom }}/_package/status/{{ FGT }}/{{ vdom }}"
register: results
tags: ADDLAG
when: vdom != ""
- name: USE CUSTOM TYPE TO QUERY AVAILABLE SCRIPTS
f_query:
adom: "{{ adom }}"
object: "custom"
custom_endpoint: "/pm/config/adom/{{ adom }}/_package/status/{{ FGT }}"
register: results
tags: ADDLAG
when: vdom == "" or vdom is not defined
Change the conditions to
when: vdom|default("")|length > 0
respectively
when: vdom|default("")|length == 0
You should check if vdom is defined before making a comparison on it, otherwise the interpreter will unsuccessfully try to fetch its value. This should work (can't check right now): when: vdom is defined and vdom != "".

Ansible: Multiple and/or conditionals in when clause

I am having issues when trying to use multiple and/or conditionals in a when statement to decide whether a task needs to be ran or not. Basically I am making a playbook to do automated system patching with options for security patches, kernel only patches and to specify packages in a var file.
I run the playbook with the following commands and define the variables through extended variables option (-e)
ansible-playbook site.yml -i inventory --ask-vault -u (username) -e "security=true restart=true" -k -K
By default the playbook will update every package on the system except kernel but I would like to skip that action if I specify any of a few variables. The code I have is the following:
- name: Update all packages
yum:
name: "*"
state: latest
exclude: "kernel*"
when: security is not defined or kernel is not defined or specified_packages
is not defined and ansible_os_family == "RedHat"
Ive tried all of the following combinations:
when: (ansible_os_family == "RedHat") and (security is defined or kernel is defined or specified_packages is defined)
when: (ansible_os_family == "RedHat") and (security == true or kernel == true or specified_packages == true ) <- this case throws a not defined error because i don't define all variables every time i run the playbook
when: ansible_os_family == "RedHat"
when: security is defined or kernel is defined or specified_packages is defined
Note: I am aware and have used an extra variable such as "skip" to skip this task and use the when clause when: ansible_os_family == "RedHat" and skip is not defined but would prefer not have my users need to use an extra variable just to skip this default action.
I also am not using tags as I am gathering a list of packages before and after the upgrade to compare and report in the end so I wont be able to run those as they are local action commands. This is why I'm using one role with multiple tasks turned on and off via extended variables. I am open to any suggestion that rewrites the playbook in a more efficient way as I am sort of a noob.
It was such a simple answer!
The following works:
when: not (security is defined or kernel is defined or specified_packages is defined) and ansible_os_family == "RedHat"
As #techraf noted in comments, defined/undefined is a nasty test...
Refactor like this:
when:
- ansible_os_family == "RedHat"
- security|d('') != '' or kernel|d('') != '' or specified_packages|d('') != ''
Update. Reproducible example:
- hosts: localhost
gather_facts: no
tasks:
- debug:
msg: hello
when:
- '"RedHat" == "RedHat"'
- security|d('') != '' or kernel|d('') != '' or specified_packages|d('') != ''
execution:
ansible-playbook -e kernel=true playbook.yml
PLAY [localhost] ***************************************************************
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "hello"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0
versions:
$ pip list | grep -iP 'ansible|jinja'
ansible (2.2.1.0)
Jinja2 (2.8)
I just had a similar problem needing to test two different variables to see if they were "true", but they don't always exist in the output json. The basic logic needed is:
( a is defined ) and ( a == 'present' or a == 'reinstalled' )
in this case, a is "install_vs2022_status.invocation.module_args.state", and the following three different scenarios produced the correct result:
direct:
when: (install_vs2022_status.invocation.module_args.state is defined) and (install_vs2022_status.invocation.module_args.state == 'reinstalled' or install_vs2022_status.invocation.module_args.state == 'present')
distributed:
when: (install_vs2022_status.invocation.module_args.state is defined and install_vs2022_status.invocation.module_args.state == 'present') or (install_vs2022_status.invocation.module_args.state is defined and install_vs2022_status.invocation.module_args.state == 'reinstalled')
broken into lines that imply 'and' or 'intersection', except it stops if the first item evaluates to false.
when:
- install_vs2022_status.invocation.module_args.state is defined
- install_vs2022_status.invocation.module_args.state == 'reinstalled' or install_vs2022_status.invocation.module_args.state == 'present'
The most important factor in each case is that the test for existence happens first, in order to prevent evaluation of a variable that doesn't exist.

Resources