fail custom message with item name - ansible

I have the following task which works perfectly as expected. I am wondering if the failure message can be a bit more informative since I am passing no_log: true without which I could see the entire result in the logs. Something like:
More than one access keys available for the cos account {{ item.name }}
- name: Fail if more than one key is available for any of the COS accounts
fail: msg="More than one access keys available for the cos account"
when: (item.json)|length > 1
with_items: '{{ old_existing_creds.results }}'
no_log: true
In fact I noticed I could not even see the msg. The o/p I got is:
TASK [Fail if more than one key is available for any of the COS accounts] *****************************************************************************
skipping: [localhost] => (item=None)
failed: [localhost] (item=None) => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": false}
fatal: [localhost]: FAILED! => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": false}
to retry, use: --limit #/root/deployment/generateSLKeys.retry

You've asked Ansible not to log the result of your task by setting no_log: true, so you're not going to be able to see the result of the fail task. You can hack around this by first creating a new variable that maps names from your old_existing_creds variables to the length of the json attribute, like this:
---
- hosts: localhost
gather_facts: false
vars:
old_existing_creds:
results:
- json: [secret1,secret2]
name: foo
- json: [secret1]
name: bar
tasks:
- name: check length of json array
set_fact:
key_check: "{{ key_check|default({})|combine({item.name: item.json|length}) }}"
loop: "{{ old_existing_creds.results }}"
- debug:
var: key_check
- name: Fail if more than one key is available for any of the COS accounts
fail:
msg: "More than one access keys available for the cos account {{ item.key }}"
when: (item.value > 1)
loop: "{{ key_check|dict2items }}"
This will output:
TASK [Fail if more than one key is available for any of the COS accounts] *********************
failed: [localhost] (item={'key': u'foo', 'value': 2}) => {"changed": false, "item": {"key": "foo", "value": 2}, "msg": "More than one access keys available for the cos account foo"}
skipping: [localhost] => (item={'key': u'bar', 'value': 1})
As you can see, it shows the message from the fail task, which includes the account name, but it does not expose credentials in the log.

Related

How to ignore specific errors in an Ansible task

If have an Ansible task, that can fails sometimes, due to some error during the creation of an user account. Especially if the user account is already in use and the user is logged in. If the task fails with a specific error message, like "user account in use" the play must continue. There is no need to fail then, but only on predefined error messages. The task looks like this.
- name: modify user
user:
state: "{{ user.state | default('present') }}"
name: "{{ user.name }}"
home: "{{ user_base_path }}/{{ user.name }}"
createhome: true
Since it's not a shell command, I cannot simply register a var and check the output of .rc. Also I don't get stderr or stdout, when i register a var and print it in debug mode. That was my first approach on check for the error message. I am running out of ideas, how to filter for a specific error and passing the task, but failing on everything else. ignore_errors: yes is not a good solution, because the task should fail in some cases.
As per ansible doc we get stdout and stderr as return fields.
I would suggest to use flag ignore_errors: yes and catch the return as per this example
---
- hosts: localhost
vars:
user:
name: yash
user_base_path: /tmp
tasks:
- name: modify user
user:
state: "{{ user.state | default('present') }}"
name: "{{ user.name }}"
home: "{{ user_base_path }}/{{ user.name }}"
createhome: true
register: user_status
ignore_errors: yes
- name: stdout_test
debug:
msg: "{{ user_status.err }}"
- name: Fail on not valid
fail:
msg: failed
when: '"user account in use" not in user_status.err'
Output:
PLAY [localhost] *************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************
ok: [localhost]
TASK [modify user] ***********************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "err": "<main> attribute status: eDSPermissionError\n<dscl_cmd> DS Error: -14120 (eDSPermissionError)\n", "msg": "Cannot create user \"yash\".", "out": "", "rc": 40}
...ignoring
TASK [stdout_test] ***********************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "<main> attribute status: eDSPermissionError\n<dscl_cmd> DS Error: -14120 (eDSPermissionError)\n"
}
TASK [Fail on not valid] *****************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "failed"}
PLAY RECAP *******************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=1
You can use when, save the return value by the register and set_fact and then bet what will happen according to that value.

ansible: jump to rescue section of a block depending on boolean host variable

I want to execute a task depending on the content of a host variable in inventory. When the boolean var "cloud" is true, I want my task to fail and to execute the rescue section. For my test system, "cloud" is "false". I added debug tasks to show type and value of "cloud":
- name: Print value of var "cloud"
ansible.builtin.debug:
var: cloud
- name: Print value of var with filter type_debug
ansible.builtin.debug:
var: cloud| type_debug
- name: check if target host is cloud system
set_fact:
dummy: true
failed_when: cloud == "true"
TASK [Print value of var "cloud"] **********************************************
ok: [host.domain.com] => {
"changed": false,
"cloud": false
}
TASK [Print value of var with filter type_debug] *******************************
ok: [uhost.domain.com] => {
"changed": false,
"cloud| type_debug": "bool"
}
TASK [check if target host is cloud system] ************************************
ok: [host.domain.com]
So far so good. For testing purposes, I would like to see the task fail on purpose by negating the check. I tried with
failed_when: not cloud
and
failed_when: cloud == "false"
but it always ends with "ok":
TASK [check if target host is cloud system] ************************************
ok: [host.domain.com]
Why isn't the check failing?
You say "so far so good", but your check is completely incorrect.
failed_when: cloud == "true"
cloud is a boolean, so it will never be equal to the string "true".
You make the same mistake when you attempt to compare to "false"; I cannot reproduce your issue with using failed_when: not cloud under ansible-core 2.11.5.
- hosts: localhost
gather_facts: false
vars:
cloud: false
tasks:
- block:
- name: Debug cloud
ansible.builtin.debug:
msg: "{{ cloud }} / {{ cloud | type_debug }}"
- name: check if target host is cloud system
set_fact:
dummy: true
failed_when: not cloud
rescue:
- debug:
TASK [Debug cloud] *************************************************************
ok: [localhost] => {
"msg": "False / bool"
}
TASK [check if target host is cloud system] ************************************
fatal: [localhost]: FAILED! => {"ansible_facts": {"dummy": true}, "changed": false, "failed_when_result": true}
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "Hello world!"
}
However, this is not how you should be creating a failure. You have two good options, the fail action and the assert action. I tend to prefer assert.
- hosts: localhost
gather_facts: false
vars:
cloud: true
tasks:
- block:
- name: Debug cloud
ansible.builtin.debug:
msg: "{{ cloud }} / {{ cloud | type_debug }}"
- name: check if target host is cloud system
assert:
# There are other ways to check this, but Jinja's built-in false test reads nicely
that: cloud is false
rescue:
- debug:
TASK [Debug cloud] *************************************************************
ok: [localhost] => {
"msg": "True / bool"
}
TASK [check if target host is cloud system] ************************************
fatal: [localhost]: FAILED! => {
"assertion": "cloud is false",
"changed": false,
"evaluated_to": false,
"msg": "Assertion failed"
}
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "Hello world!"
}

In ansible using the value of a previously assigned set_fact in a when conditional for jira task skips even though condition is met

In my ansible playbook, I am trying to close jira tickets based on the condition - if the value of my set_fact is == 0, loop through the list of Jira ticket keys and close them all. The task is running on my defined host in my inventory (not localhost).
Here is my playbook:
# Get the length of x
- name: Find the length of x
set_fact:
length_of_x: "{{ x | length }}"
- name: Printing the length of x
debug:
var: length_of_x
# If x is an empty list, move JIRA tickets to "Closed" status and skip next steps
- name: Close JIRA ticket
jira:
uri: 'https://ip.ip.ip.ip'
username: 'foo'
password: 'foo'
validate_certs: false
project: test
issue: "{{ item.ticket_key }}"
operation: transition
status: Closed
loop: "{{ ticket_key }}"
when: **length_of_x == 0**
run_once: true
register: jira_close_results
- name: Print the complete response
debug: var=jira_close_results
I am expecting that since I have x as an empty list, length_of_x == 0, thus the jira ticket will be closed. Instead I get this:
TASK [Printing x] ******************************************
ok: [host] => {
"x": []
}
TASK [Find the length of x] **********************************
ok: [host]
TASK [Printing the length of x] **********************************
ok: [host] => {
**"length_of_x": "0"**
}
TASK [Close JIRA ticket] *****************************
skipping: [host] => (item=test-100)
TASK [Print the complete response] *********************************************
ok: [host] => {
"jira_close_results": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"item": "test-100",
"skip_reason": "Conditional result was False",
"skipped": true
}
]
}
}
I tried many different ways of defining the when condition but nothing is working, it skips every time. Thank you for your help in advance.
Okay,
Change the when statement to following,
when: length_of_x: "0" # To consider 0 as a string
or
when: length_of_x|int: 0 # To parse length_of_x as integer
Reason, Ansible returns a string for set_fact
Reference

Ansible 2.8: Access a list object element

In Ansible 2.8, I need to deploy and config Bind 9 DNS on Ubuntu Server VMs. I have a:
DNS Ansible role to do the installation and config,
a list of variables per domain zone (as DNS record type, domain name, dns entry,...). Until here, it works, the issues appear when I try to make it accept the next requirement:
potentially, several domain zones to configure in the same call, threfore, I send to it a list with groups of variables (mentioned in 2).
For now, in the shell, I call it with 1 element list, using:
--extra-vars "{"dns_entry_conf":
[domain=example.gal ip=192.168.167.166
nameserver1=example.gal nameserver1_ip=192.168.167.164
dns_record1_type=A ...]}"
Inside the role, the roles/dns/tasks/configure.yml file receives the right value, but the file that follows, doesn't: it says "list object has no attribute", and I started debugging in the configure.yml file, but I am not sure how to access the list object item:
---
- debug:
msg: "{{dns_entry_conf}}"
- debug:
msg: "{{dns_entry_conf | json_query(\"domain\") }}"
The first line prints what it should, but the 2nd does not... How I can access the value
ASK [dns : debug] **********************************************************************************
task path: /etc/ansible/roles/dns/tasks/configure.yml:2
ok: [ubuntuServer16_test] => {
"msg": [
"domain=example.gal ip=192.168.167.166 nameserver1=example.gal nameserver1_ip=192.168.167.164
dns_record1_type=A ...
]
}
TASK [dns : debug] **********************************************************************************
task path: /etc/ansible/roles/dns/tasks/configure.yml:4
ok: [ubuntuServer16_test] => {
"msg": ""
}
In debug, tried with the msg's: "{{ dns_entry_conf.domain }}", "{{ dns_entry_conf.0 }}", "{{dns_entry_conf | json_query(\"domain\") }}", "{{ dns_entry_conf.list | json_query('[*].domain') }}", and others that were sintactically wrong, but it never outputs what I want.
Probably there are more wrong things (I am no Ansible expert), but, for now, just trying to debug and fix one by one, so, I just want to know how I can access the "dns_entry_conf.domain" item, please... some idea?
Option1:
with extra-vars as below:
--extra-vars '{"dns_entry_conf":{"domain":example,"ip":1.2.3.4}}'
Playbook:
- debug:
msg: "{{dns_entry_conf.domain}}"
Output:
ok: [localhost] => {
"msg": "example"
}
Option2:
with extra vars as below:
--extra-vars '{"dns_entry_conf":["domain":example,"ip":1.2.3.4]}'
In Playbook tey as below:
- debug:
msg: "{{dns_entry_conf[0].domain}}"
Output:
ok: [localhost] => {
"msg": "example"
}
Option 3:
Pass the variables in the playbook.
vars:
dns_entry_conf:
domain: example
ip: 1.2.34.4
tasks:
- debug:
msg: "{{dns_entry_conf.domain}}"
Output:
ok: [localhost] => {
"msg": "example"
}

When hostvars data is populated and how it is accessible?

Here is my play:
- name: Tag ec2 instances
hosts: localhost
tasks:
- name: Print Hosts
debug: var=hostvars[item]['ec2_id']
with_inventory_hostnames: all
- name: Print Hosts 2
debug: msg={{hostvars[item]['ec2_id']}}
with_inventory_hostnames: all
- name: Tag Hosts
ec2_tag:
resource: "{{ hostvars[item]['ec2_id'] }}"
region: ap-southeast-2
args:
tags: {mytag: 'myvalue'}
with_inventory_hostnames: all
Can anyone explain why the second task fails with the following error while the first one is successful?
...
ok: [localhost] => (item=172.31.11.37) => {
"hostvars[item]['ec2_id']": "i-xxxxxx",
"item": "172.31.11.37"
}
TASK [Print Hosts 2] ***********************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'ec2_id'"}
debug module with var=hostvars[item]['ec2_id'] will not fail if anything to the right of equal sign is undefined.
While msg={{hostvars[item]['ec2_id']}} will fail if the part in braces can't be templated.
In your example this may fail for localhost because I'm almost sure that ec2_id is not defined for localhost.
To avoid this, you can apply when statement to your loop, as follows:
- name: Print Hosts 2
debug: msg={{hostvars[item]['ec2_id']}}
when: hostvars[item]['ec2_id'] is defined
with_inventory_hostnames: all

Resources