Json_query/jmespath help with JSON output of ansible-playbook execution

I am looking to do some meta analysis of the execution of a playbook by setting stdout_callback = json in ansible.cfg and analysing the output.

Specifically I’d like to extract a list of tasks and whether for each host the task changed anything. AFAIK that should be expressed by the value of changed under the host and task. I was hoping to use Ansible + json_query i.e. a jmespath filter to extract this information.

I see that the output looks like that below:

{
    "plays": [
        {
            "play": {
                "name": "Play 1"
            },
            "tasks": [
                {
                    "hosts": {
                        "host1": {
                            "changed": true
                        },
                        "host2": {
                            "changed": false
                        }
                    },
                    "task": {
                        "name": "Task 1"
                    }
                }
            ]
        }
    ]
}

My desired extraction:

[
  {
    "task": "Task 1",
    "hosts": [
        "host1"
    ]
  }
]

I’ve got as far as plays[].tasks[].{task: task.name, hosts: keys(hosts) | [?hosts[?@] ==true]} but that doesn’t work (as tested in https://play.jmespath.org/). I’m not sure how to filter the hosts based on the value of changed and display the host name. Any help would be appreciated.

I did this a while ago for running Ansible using the chroot_connection on remote servers, I used stdout_callback = ansible.posix.json and run the roles in a loop using the task here and then I used another loop on the results to get the tasks that changed, I expect this isn’t the most elegant solution but it works!

This is the include:

- name: Include /root/chroot-config/run.sh task for each role to configure the chroot
  ansible.builtin.include_tasks: role.yml
  loop: "{{ chroot_config_playbook_roles }}"
  loop_control:
    loop_var: chroot_config_role

And these are the tasks in role.yml:

- name: "Run the chroot.yml playbook for role {{ chroot_config_role }}" 
  ansible.builtin.command: "ansible-playbook -i hosts.yml -c chroot chroot.yml --tags {{ chroot_config_role }}" 
  args:
    chdir: /root/chroot-config
  environment:
    ANSIBLE_CONFIG: "/root/chroot-config/ansible.cfg"
    ANSIBLE_NOCOWS: "1" 
    DEBIAN_FRONTEND: "noninteractive"
    PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
  vars:
    chroot_role_changed_jpquery: 'stats.["{{ chroot_dir }}"]|[0].changed'
  register: chroot_role_run
  changed_when: chroot_role_run.stdout | from_json | json_query(chroot_role_changed_jpquery) != 0

- name: "Tasks changed for role {{ chroot_config_role }}"  # noqa no-handler
  ansible.builtin.debug:
    msg: "{{ chroot_role_task | json_query('task.name') }}" 
  changed_when: false
  when: chroot_role_task | json_query('hosts.*.changed|[0]') | lower | bool
  loop: "{{ chroot_role_run.stdout | from_json | json_query('plays[0].tasks') }}" 
  loop_control:
    extended: true
    label: "Task {{ ansible_loop.index }}" 
    loop_var: chroot_role_task
1 Like