Neatly print Ansible debug output on multiple lines - ansible

I have a file that I want to print as debug msg line.
cat result.txt
## BEGIN :## Role Name: Deployment Checks
## REASON:- ERROR: Deployment Checks output from command FATAL: TEST FAILED.
## END :## Role Name: Deployment Checks ##
## BEGIN :## Role Name: Describe the instance
## REASON:- ERROR: Describe the instance FATAL: TEST FAILED.
## END :## Role Name: Describe the instance ##
I m using the below code to print them on the console
- name: Read result
shell: "cat result.txt | grep 'REASON:-'"
register: result
- name: print checks fail.
debug:
msg:
- "Check Failed!"
- "{{ result.stdout_lines }}"
Which prints below output in an ugly format as single line
TASK [ print checks fail.] **************************************************
fatal: [10.203.116.90]: FAILED! => {"changed": false, "msg": ["Check Failed!", ["REASON:- ERROR: Deployment Checks output from command", "REASON:- ERROR: Describe the instance "]]}
How can I print every reason as a single line? So that output is more readable. Something like below
TASK [ print checks fail.] **************************************************
fatal: [10.203.116.90]: FAILED! => {"changed": false, "msg":
["Check Failed!",
["REASON:- ERROR: Deployment Checks output from command",
"REASON:- ERROR: Describe the instance "]]}

Without a change change of callback, you could do:
- debug:
msg: "{{ ['Check Failed!'] + result.stdout_lines }}"
Which will render in
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": [
"Check Failed!",
"## REASON:- ERROR: Deployment Checks output from command FATAL: TEST FAILED.",
"## REASON:- ERROR: Describe the instance FATAL: TEST FAILED."
]
}
But as pointed by #U880D's answer, the YAML callback is way more convenient to format output properly.
For example, with the YAML output, you could do:
- debug:
msg: |-
Check Failed!
{{ result.stdout }}
Which will render in
TASK [debug] *******************************************************************
ok: [localhost] =>
msg: |-
Check Failed!
## REASON:- ERROR: Deployment Checks output from command FATAL: TEST FAILED.
## REASON:- ERROR: Describe the instance FATAL: TEST FAILED.

You may have a look into Callback plugins and configure it in ansible.cfg in example with stdout_callback: yaml.
By using this
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Read result
shell:
cmd: "grep 'REASON:-' result.txt"
register: result
- name: Show result
debug:
msg: "{{ result.stdout_lines }}"
the result would be
TASK [Show result] ***************************************************************
ok: [localhost] =>
msg:
- '## REASON:- ERROR: Deployment Checks output from command FATAL: TEST FAILED.'
- '## REASON:- ERROR: Describe the instance FATAL: TEST FAILED.'
as required.
According your example it seems that you have currently configured stdout_callback: json, and according Ansible Issue #76556 it can't be specified in the playbook level.
Further Q&A
Ansible stdout formatting
Ansible Documentation
Index of all Callback Plugins

Related

How do I add a custom message if a task does not meet the 'when' condition and is skipped?

In this task, I am searching for a file. If there is no file, the task will be skipped.
My question is, how do I write a custom message to output when the task is skipped?
- name: Search for files
win_find:
paths: C:\dataset\
register: data
- debug:
msg: "Data exists"
when: data | json_query('files[*].exists')
- name: set_fact
set_fact:
exists: "{{ data | json_query('files[*].exists') }}"
In a different playbook:
- name: Run if file exists
block:
- name: read content from file
win_shell: C:\day.txt
register: day
when: hostvars['10.11.18.190']['exists']
- name: print message
debug:
msg: "{{ hostvars['10.12.201.20']['day'] }}"
As there is no file, the task is skipped:
TASK [Run if file exists] *********************
skipping: [10.11.18.190] => {
"changed": false,
"skip_reason": "Conditional result was False"
}
TASK [print message] **************************************************************************************
ok: [10.11.18.190] => {
"msg": {
"changed": false,
"skip_reason": "Conditional result was False",
"skipped": true
}
}
As you can see from the output, the variable hostvars['10.12.201.20']['day'] is showing "changed": false, skip_reason, etc. But I do not want this, I want it to output a message like, "File does not exist".
How can I create a custom message for this variable hostvars['10.12.201.20']['day']?
A: Use the 'Do It Yourself' callback plugin community.general.diy. See
shell> ansible-doc -t callback community.general.diy
(or the online documentation)
For example, if the file /tmp/day.txt does not exist the playbook
shell> cat pb.yml
- hosts: localhost
tasks:
- stat:
path: /tmp/day.txt
register: stat_day
- command: cat /tmp/day.txt
register: day
when: stat_day.stat.exists
vars:
ansible_callback_diy_runner_on_skipped_msg: |
skipping: [{{ inventory_hostname }}]
msg: File does not exist.
ansible_callback_diy_runner_on_skipped_msg_color: green
will display the custom message
shell> ANSIBLE_STDOUT_CALLBACK=community.general.diy ansible-playbook pb.yml
PLAY [localhost] *****************************************************************************
TASK [stat] **********************************************************************************
ok: [localhost]
TASK [command] *******************************************************************************
skipping: [localhost]
msg: File does not exist.
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Optionally, you can use the block/rescue construct. See Handling errors with blocks.
For example, in Linux (I don't have access to Windows atm) when you try to access a nonexistent file by the module command on the localhost
- command: cat /tmp/day.txt
register: day
the command will fail
fatal: [localhost]: FAILED! => changed=true
ansible_facts:
discovered_interpreter_python: /usr/bin/python3
cmd:
- cat
- /tmp/day.txt
delta: '0:00:00.010884'
end: '2023-02-14 07:21:50.664051'
msg: non-zero return code
rc: 1
start: '2023-02-14 07:21:50.653167'
stderr: 'cat: /tmp/day.txt: No such file or directory'
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
Put the command into the block and use the section rescue
- block:
- command: cat /tmp/day.txt
register: day
- debug:
var: day.stdout
rescue:
- debug:
var: ansible_failed_result
Now, if the command fails you'll see
ansible_failed_result:
ansible_facts:
discovered_interpreter_python: /usr/bin/python3
changed: true
cmd:
- cat
- /tmp/day.txt
delta: '0:00:01.007972'
end: '2023-02-14 07:24:43.791343'
failed: true
invocation:
module_args:
_raw_params: cat /tmp/day.txt
_uses_shell: false
argv: null
chdir: null
creates: null
executable: null
removes: null
stdin: null
stdin_add_newline: true
strip_empty_ends: true
msg: non-zero return code
rc: 1
start: '2023-02-14 07:24:42.783371'
stderr: 'cat: /tmp/day.txt: No such file or directory'
stderr_lines:
- 'cat: /tmp/day.txt: No such file or directory'
stdout: ''
stdout_lines: []
You can reduce the output to the standard error
rescue:
- debug:
var: ansible_failed_result.stderr
If the file exists
shell> echo 'This is the content of /tmp/day.txt' > /tmp/day.txt
The next task in the block will display the standard output of the command
day.stdout: This is the content of /tmp/day.txt
Of course, there will be differences in the error messages among the operating systems. Take a look at the data you get and fit the messages to your needs.
I see not direct solution for this problem. But you could do the following:
disable the output of skipped tasks at all, with
[defaults]
display_skipped_hosts = true
In the ansbile.cfg
For details see https://docs.ansible.com/ansible/latest/collections/ansible/builtin/default_callback.html
create a debug task with the message you want to display with an opposite when condition. So it will only run if the other task is skipped.

How to print failure message in Ansible builtin.getent when user not found and continue with rest of tasks

I am trying to run a playbook if a specific system user does not exist and make it fail and print a message if the user exists. Below the how I do the user check:
- name: 'Check system user'
getent:
database: passwd
key: '{{USER}}'
register: result
How do I make it fail and print message if user exists and make it continue with the rest of tasks if user does not exist?
THanks in advance.
Q: "Fail and print a message if the user exists and continue with the rest of the tasks if the user does not exist."
A: For example, given the variable
USER: admin
The tasks below
- getent:
database: passwd
- assert:
that: "{{ USER in getent_passwd }}"
fail_msg: "User {{ USER }} does not exist."
success_msg: "User {{ USER }} exists."
- debug:
msg: Continue
gives
TASK [assert] **************************************************************
ok: [localhost] => changed=false
msg: User admin exists.
TASK [debug] ***************************************************************
ok: [localhost] =>
msg: Continue
Test a missing user. For example
USER: joker
The same tasks will fail and print the message
TASK [assert] **************************************************************
fatal: [localhost]: FAILED! => changed=false
assertion: false
evaluated_to: false
msg: User joker does not exist.
Thanks. I needed it the opposite way so I changed it to:
---
- getent:
database: passwd
- assert:
that: "{{ USER not in getent_passwd }}"
fail_msg: "User {{ USER }} exists."
success_msg: "User {{ USER }} does not exist."
- debug:
msg: Continue
And the result:
TASK [verify : assert] *****************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "User missing does not exist."
}
TASK [verify : debug] ******************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "Continue"
}

Display Debug output in a clean format Ansible

I am trying to display the message output of a debug command in a nice format in Ansible. At the moment this is how the output looks:
TASK [stop : Report Status of Jenkins Process] *******************************************************************************************************************************
ok: [localhost] => {
"msg": "Service Jenkins is Running.\nReturn code from `grep`:\n0\n"
}
TASK [stop : debug] **********************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"changed": false,
"failed": false,
"msg": "Service Jenkins is Running.\nReturn code from `grep`:\n0\n"
}
}
How do I get rid of the '\n' character and replace with a new line?
The code below using the split('\n') does not work.
- name: Checking Jenkins Process
shell: "ps -ef | grep -v grep | grep -v dhclient | grep jenkins"
ignore_errors: yes
register: jenkins_process
- debug:
var: jenkins_process.rc
- name: Report Status of Jenkins Process
fail:
msg: |
Service Jenkins is not found
Return code from `grep`:
{{ jenkins_process.rc }}
when: jenkins_process.rc != 0
register: report
- name: Report Status of Jenkins Process
debug:
msg: |
Service Jenkins is Running.
Return code from `grep`:
{{ jenkins_process.rc }}
when: jenkins_process.rc == 0
register: report
- debug:
msg: "{{ report.split('\n') }}"
- name: Stop Jenkins Service
service:
name: jenkins
state: stopped
Is there a way to display this in a nice way?
You can use the debug callback plugin.
You can specify it on command line:
ANSIBLE_STDOUT_CALLBACK=debug ansible-playbook ...
Or in your default section of your ansible.cfg configuration file:
stdout_callback = debug

Ansible slurp module fails with a variable

When I use an Ansible variable with the src option of the slurp module, the slurp module fails.
I'm trying to build an Ansible playbook to copy the SSH public key from each node in the group to every other node in the group. I cannot use the Ansible lookup() function because that can lookup files only on the Ansible server. Instead, I build the path to the id_rsa.pub with the intent of slurp'ing into memory for the authorized_key function.
My problem is that when I specify an Ansible variable for the src for the slurp module, the playbook fails, even though it lists the correct path to the id_rsa.pub file. If I specify the path instead of using the variable, the slurp module works.
Here is my playbook:
# Usage: ansible-playbook copyPublicKey.yaml --limit <GRP> --extra-vars "userid=<userid>"
---
- hosts: all
remote_user: root
vars:
user_id: "{{ userid }}"
tasks:
- name: Determine the path to the public key file
shell: grep "{{ user_id }}" /etc/passwd | cut -d":" -f6
changed_when: false
register: user_home
- set_fact:
rsa_file: "{{ user_home.stdout_lines | to_nice_yaml | replace('\n', '') }}/.ssh/id_rsa.pub"
- debug:
msg: "Public key file - {{ rsa_file }}"
- slurp:
src: "{{ rsa_file }}"
register: public_key
- debug:
msg: "Public key: {{ public_key }}"
The invocation:
ansible-playbook copyPublicKey.yaml --limit DEV --extra-vars "userid=deleteme2"
The output of the slurp module:
TASK: [slurp ] ****************************************************************
failed: [hana-np-11.cisco.com] => {"failed": true}
msg: file not found: - /usr/sap/DEV/home/deleteme2/.ssh/id_rsa.pub
failed: [hana-np-13.cisco.com] => {"failed": true}
msg: file not found: - /usr/sap/DEV/home/deleteme2/.ssh/id_rsa.pub
failed: [hana-np-14.cisco.com] => {"failed": true}
msg: file not found: - /usr/sap/DEV/home/deleteme2/.ssh/id_rsa.pub
failed: [hana-np-15.cisco.com] => {"failed": true}
msg: file not found: - /usr/sap/DEV/home/deleteme2/.ssh/id_rsa.pub
failed: [hana-np-12.cisco.com] => {"failed": true}
msg: file not found: - /usr/sap/DEV/home/deleteme2/.ssh/id_rsa.pub
FATAL: all hosts have already failed -- aborting
Yet if I specify the actual path in the slurp module:
- slurp:
src: /usr/sap/DEV/home/deleteme2/.ssh/id_rsa.pub
I get the output I expect:
TASK: [slurp ] ****************************************************************
ok: [hana-np-11.cisco.com]
ok: [hana-np-12.cisco.com]
ok: [hana-np-15.cisco.com]
ok: [hana-np-14.cisco.com]
ok: [hana-np-13.cisco.com]
TASK: [debug ] ****************************************************************
ok: [hana-np-11.cisco.com] => {
"msg": "Public key: c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBQkl3QUFBUUVBbHgzM0FUdGlLcWlrblQxMWorNjZKSXVFQW1OWWxZcDdCbHIwZXBzaWRuZ3NNYW9pMjNYL1Bjb0EvdnVxYmpxbmZ0Q1YzQmhUdURYQ3BYY0FwNDF5TEF5dlIvOW8xYi9mR2VtZWtlS296ZDh5Smh5VXFMR3IvMmJ6N0N2NFdaOWVqU0dyMFlzWGNjSFNDRmYzNmJreVBPNUg5NUdZdXpGMUV2RzVVcGM3YVNXWEVpM3JWVGJETEhBVC9YTk0veXhRUEMxRjB5Vi8yRkY1WDg4SXU5U0w2TGxrVnhsMUU3VkozTm40UEQrY3RUbGxFeno3enNETWxDbXpzMW5MaHROWnFuSXRZUkhMd21WUk5VcHJvYlpyUm1YMFJVYmIwNFNVbzdBbXpBNnZNcHR1OE1aUURzUGRMckMwYWxPWnZHMHpEUi9ReDlGalh6MVRXMld5WWhZNllRPT0gYW5zaWJsZS1nZW5lcmF0ZWQgb24gaGFuYS1ucC0xMQo=..."
}
ok: [hana-np-12.cisco.com] => {
"msg": "Public key: c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBQkl3QUFBUUVBd2hPa0FqcEhwbUU4ZEkvemR6d0I1U0htZnlpdXljd2ZmK2lDNW9KaEN4aU5ST0ZKbnVyOFArWno2K2c4Qy8waUdkNGs1ZHIwcE9IY1liWHlMeDNObHhTTWN6RnowZWNSUnMzL1FOOEQzSnBtWlR6T0JaMm1SaG1FY0hGbS9uTkh5eUZyWXlPOHlQNWpqNmxiSUlwU0lMb1BZZGJvM1dxenBGZjhiaDFlVkhRTEo2citVZzNwcUhUeWRzRDZhY3Rtc1ZvWWUvdVV6WExiYkpKbUxxdi9ZeGU4ZW9aUmtONkVqNGtaVDBibDFYUktkM0xTQlZKMHRwa3A1bVgzekxMNGVvWVEzMzMzam1qd2MzU1dWSHVObVl1b1ZsRFEvSzdoR2lFVHd5YUM3VU9hQ29pcEVnUGl5b2o3U1JpNzZCenpxV2hXc2dIbHI0REM3U0p2WFpObk9RPT0gYW5zaWJsZS1nZW5lcmF0ZWQgb24gaGFuYS1ucC0xMgo=..."
}
ok: [hana-np-13.cisco.com] => {
"msg": "Public key: c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBQkl3QUFBUUVBemFzeitlSW9OSnc2Q3psaVVSR2NQbnMyRkVWbDRtd1RqbDJrWkYxcC9uL0d1b3RPS01vMnR2RmQrN3JmY3YrN2VSZXNtM1lldXJzOXRCQXdSVDdvbXdjckpqUkJiM2Q3UHd3MnM1OTJjb0RjdFo3aE9vL3p4S1FCeWtjaXcvejJ3U1pKWUZKdnE4eFloWkxQNmxnK01uVW1Rd0JROURhREc3MVY2VFc5cFdSM0poYk5BZ2s4bWpBYlEyQk1kK2lWOGxoOUorcWIxbGE3RVVRRGZNaUM3R0ZKVmxJUVlpdm02Q045dGZOdnJGRlNaamZ6MEZKeXhQQWd1VW05d2NUMG9lUDdEQTJTVFNZQ0trQklvRmVuTUp3eFFzNDZSclJSenlBNlErN3I3Q3g5WnpPSlQ0OGgzQnpHQkNwYnhNd0R2L1RMNjRNTDN3UGxXVng5NGZvU2NRPT0gYW5zaWJsZS1nZW5lcmF0ZWQgb24gaGFuYS1ucC0xMwo=..."
}
ok: [hana-np-14.cisco.com] => {
"msg": "Public key: c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBQkl3QUFBUUVBd3U5V3FaZHBmSjdUZkFhNkc2V05pTkpraWVjWllObjZ4Mno2WWo3eUhsdDBrVWhoYmVXZWR4and0Ukl5d0h2UUZCU2xoVVo2V1Vhc2w0RlZsdzcrOHRGVkVIMkkwTUQ1YjN5UGFrclFhVXdxZHZ6TDEvVXpQWkJjTGU0SENmTGdLNjAxaXdncXBvdUVoRjFEdjEvVHorcTFvU0dmTTFOVjFPOFBGc1pQVjRZbVpoTC9qTVZBNE5MUzFKdXRoS1VmZlo3TmJOSk42SmdWaE14UW8vQXZIZmZvYktDUGJ1VWxDTFE0cTV6VlVTWkxyV1p3VmdncU44MkVnd0xYS00xU0IvOVQ2YmFMVXZhaDVVN0s1QjEzR0pFVXJWek1NNWZ0clpwdEo4T1N3OXUvMHJLVXlzZ0hRcUZVM0ViN0JkVkUxQVdCRXFtaW1XRFdMV3hUclJmZ053PT0gYW5zaWJsZS1nZW5lcmF0ZWQgb24gaGFuYS1ucC0xNAo=..."
}
ok: [hana-np-15.cisco.com] => {
"msg": "Public key: c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBQkl3QUFBUUVBdWwxTjlWQkNSU3QrOG5jdTRMVUlBb2hxUEkrWmRlcEtINHlhU1BBZWtETXdkaXpLVHZRSElXdC9iVkpXUzNma3BOYjVuTXFtMkR1eFZnKzBtZmRPTTk1Q2ZsUk00ZUNON05Jb25HQTQrUGVyOXRYdlNrdFU4U0huWERsZVNNa3dybUxnQ1dQN2lwbDRTdGt1SUNGaFh1NzBkOHBEN29IeW9BZVVWWVFuYzRkZldHQStVNU1SdWNSaC9mNWhhS25pN1hpRHZ0alVTaDJHN1RpMTlIdHBvYnlQdmdNSjVnRUt2OXRlWGJ3Qk14YXZicEFiRjJVOTRRTmorKzZOYTZIaWUweS9JQzVtWDRvSmgyb2Z6bGwybjA0MHdtQWRkQS9mY1d1L0IvR3FyOWNDZlhXK0hIUU95MEJoUXNBMk54K3A1RU4rbG1iREg1TUNHTW41Y0RLVEpRPT0gYW5zaWJsZS1nZW5lcmF0ZWQgb24gaGFuYS1ucC0xNQo=..."
}
What am I doing wrong? What don't I know about using Ansible variables?
slurp module fails because you provide it incorrect data -- the error message is:
msg: file not found: - /usr/sap/DEV/home/deleteme2/.ssh/id_rsa.pub
Rephrasing:
The file named "- /usr/sap/DEV/home/deleteme2/.ssh/id_rsa.pub" cannot be found.
Quite obviously such a file with a hyphen and a space at the beginning does not exist and the error is valid.
The reason for malformed data is unnecessary to_nice_yaml filter on a user_home.stdout_lines list (hyphen is an element marker in YAML).
You can safely remove it and use the following:
---
- hosts: all
remote_user: root
vars:
user_id: "{{ userid }}"
tasks:
- name: Determine the path to the public key file
shell: grep "{{ user_id }}" /etc/passwd | cut -d":" -f6
changed_when: false
register: user_home
- slurp:
src: "{{ user_home.stdout_lines[0] }}/.ssh/id_rsa.pub"
register: public_key
- debug:
msg: "Public key: {{ public_key }}"
Elements of stdout_lines don't have trailing newlines, so replace('\n', '') is unnecessary, but as it is a list, even though south a single element, you need to address only the first element with [0].
Otherwise you could also get the value with user_home.stdout | replace('\n', '') }}/.ssh/id_rsa.pub.
In this case, the issue is related to incorrect file name (as mentioned by techraf).
But just a note about what I have experienced is that slurp also shows the same error "File not found" when the file resides in a directory whose permissions are not allowing ansible user to read content from it. Though, it should print permission related error but it shows instead "File not found" error.

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