Let's say you have the following playbook which will create the /tmp/foo.txt file.
---
- hosts: localhost
tasks:
- name: create /tmp/foo.txt
ansible.builtin.file:
path: /tmp/foo.txt
state: touch
...
Running this playbook should return something like this. Notice the output is not structured JSON or YAML.
~]$ ansible-playbook testing.yml
PLAY [localhost]
TASK [Gathering Facts]
ok: [localhost]
TASK [file]
changed: [localhost] => (item=touch)
PLAY RECAP
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
stdout_callback can be used to configure stdout to be JSON or YAML. You will need to ensure you have the ansible.posix collection installed. If you are not familiar with collections, check out my article on Getting Started with Collections.
The collections_path or collections_paths directive in your ansible.cfg file can be used to specify the directory or directories that Ansible will use for collections.
[defaults]
collections_path = /usr/local/ansible/collections,/usr/local/ansible_testing/collections
The ansible-galaxy collection list command can be used to list the collection you have installed in your collections paths.
~]$ ansible-galaxy collection list
# /home/john.doe/.ansible/collections/ansible_collections
Collection Version
----------------- -------
amazon.aws 6.1.0
community.docker 1.9.0
community.general 4.0.2
# /usr/share/ansible/collections/ansible_collections
Collection Version
------------------------ -------
redhat.rhel_system_roles 1.0.1
Optionally, you can include the path to a specific directory that contains collections you have installed.
~]$ ansible-galaxy collection list --collections-path /path/to/collections
Collection Version
----------------- -------
amazon.aws 6.1.0
community.docker 1.9.0
community.general 4.0.2
If you do not have the ansible.posix collection installed, the ansible-galaxy collection install command can be used to install a collection. By default, this will install the collection to /home/username/.ansible/collections/ansible_collections/.
ansible-galaxy collection install ansible.posix
There are a number of ways to configure Ansible to use the stdout callback, where the higher option in this list will take precedence over the lower option.
- ANSIBLE_STDOUT_CALLBACK variable
- stdout_callback in ansible.cfg in the present working directory
- stdout_callback in .ansible.cfg in the user home directory (e.g. /home/john.doe/.ansible.cfg)
- stdout_callback in /etc/ansible/ansible.cf
ANSIBLE_STDOUT_CALLBACK ​variable
Here is how you could set the stdout callback using the ANSIBLE_STDOUT_CALLBACK variable on a Linux system. Be aware that you would need to issue the export command before issuing the ansible-playbook command.
export ANSIBLE_STDOUT_CALLBACK=ansible.posix.json
Or you can include this in your users hidden .bash_profile file (e.g. /home/john.doe/.bash_profile) to make this setting permanent.
~]$ cat /home/john.doe/.bash_profile
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
export ANSIBLE_STDOUT_CALLBACK=ansible.posix.json
Or in a playbook using environment.
---
- hosts: localhost
environment:
ANSIBLE_STDOUT_CALLBACK: ansible.posix.json
tasks:
- name: create /tmp/foo.txt
file:
path: /tmp/foo.txt
state: touch
...
stdout_callback directive
You can define the stdout_callback directive in your Ansible configuration file (ansible.cfg), where the higher option in this list will take precedence over the lower options.
- stdout_callback in ansible.cfg in the present working directory
- stdout_callback in .ansible.cfg in the user home directory (e.g. /home/john.doe/.ansible.cfg)
- stdout_callback in /etc/ansible/ansible.cfg
AVOID TROUBLE
The stdout_callback directive must be under the [defaults] header.
[defaults]
stdout_callback = ansible.posix.json
And now when you run your Ansible playbook, the console output should be JSON or YAML.
{
"custom_stats": {},
"global_custom_stats": {},
"plays": [
{
"play": {
"duration": {
"end": "2025-10-07T07:15:51.801833Z",
"start": "2025-10-07T07:15:51.319038Z"
},
"id": "00505687-1254-4d2e-bb06-000000000001",
"name": "localhost",
"path": "/home/john.doe/a:2"
},
"tasks": [
{
"hosts": {
"localhost": {
"_ansible_no_log": false,
"action": "ansible.builtin.file",
"changed": true,
"dest": "/tmp/foo.txt",
"diff": {
"after": {
"atime": 1759821351.78045,
"mtime": 1759821351.78045,
"path": "/tmp/foo.txt",
"state": "touch"
},
"before": {
"atime": 1759821331.4021,
"mtime": 1759821331.4021,
"path": "/tmp/foo.txt",
"state": "file"
}
},
"gid": 100,
"group": "users",
"invocation": {
"module_args": {
"_diff_peek": null,
"_original_basename": null,
"access_time": null,
"access_time_format": "%Y%m%d%H%M.%S",
"attributes": null,
"follow": true,
"force": false,
"group": null,
"mode": null,
"modification_time": null,
"modification_time_format": "%Y%m%d%H%M.%S",
"owner": null,
"path": "/tmp/foo.txt",
"recurse": false,
"selevel": null,
"serole": null,
"setype": null,
"seuser": null,
"src": null,
"state": "touch",
"unsafe_writes": false
}
},
"mode": "0644",
"owner": "c065234",
"secontext": "unconfined_u:object_r:user_tmp_t:s0",
"size": 0,
"state": "file",
"uid": 65234
}
},
"task": {
"duration": {
"end": "2025-10-07T07:15:51.801833Z",
"start": "2025-10-07T07:15:51.332662Z"
},
"id": "00505687-1254-4d2e-bb06-000000000003",
"name": "touch /tmp/foo.txt",
"path": "/home/john.doe/a:5"
}
}
]
}
],
"stats": {
"localhost": {
"changed": 1,
"failures": 0,
"ignored": 0,
"ok": 1,
"rescued": 0,
"skipped": 0,
"unreachable": 0
}
}
}
On a Linux system, you could then pipe the JSON output into the jq command to return specific JSON keys.
~]$ ansible-playbook example.yml | jq .stats
{
"localhost": {
"changed": 1,
"failures": 0,
"ignored": 0,
"ok": 1,
"rescued": 0,
"skipped": 0,
"unreachable": 0
}
}
Here is how you could loop through each task in your playbook.
ansible-playbook example.yml | jq --compact-output .plays[].tasks[] | while read item; do echo "TASK = $item"; done
Did you find this article helpful?
If so, consider buying me a coffee over at 