Resource reporting

Ansible modules often interact with external systems such as clouds, hypervisors, and network controllers to manage resources that are not declared in your inventory. Resource reporting gives you structured visibility into what your automation actually touches, even for resources that never appear in the inventory.

You can add resource reporting to your Ansible collection by creating a lightweight YAML query file that maps module return values to a standardized resource taxonomy. Each entry uses a jq expression to extract resource metadata from module output.

How resource reporting works

Resource reporting uses a query file inside your collection to describe the resources that your modules manage. When a module runs, the query file tells any consuming tool how to extract three pieces of information from the module return values:

  • A human-readable resource name.

  • Canonical facts that uniquely identify the resource for deduplication.

  • Metadata that categorizes the resource using a standardized taxonomy.

The result is machine-readable documentation about what your modules manage. For collection developers, resource reporting is like having excellent return value documentation that tools can consume automatically. For users, it creates a consistent, unified picture of automation activity across different vendors and platforms.

Note

The query file format, resource taxonomy, and jq expression pattern are an open schema. Any automation platform, reporting tool, or custom script can consume the query file to extract structured resource data from module output.

The normalized resource taxonomy

The normalized resource taxonomy maps vendor-specific resource types to standard names. For example, a VMware VM and an AWS EC2 instance are both type virtual_machine. An Azure load balancer and an F5 BIG-IP VIP are both type load_balancer.

The taxonomy organizes resources into categories and device types. When you write a query, set the facts.device_type field to the snake_case value from the tables below.

Compute

Resource

device_type value

Virtual machines

virtual_machine

Containers (managed)

container

Hypervisors

hypervisor

Bare metal

bare_metal

Serverless functions

serverless_function

Auto scaling groups

auto_scaling_group

Networking

Resource

device_type value

Switches

switch

Routers

router

Firewalls

firewall

Load balancers

load_balancer

Virtual private clouds

vpc

Subnets

subnet

VPNs

vpn

Gateways

gateway

DNS services

dns_service

Wireless access points

wireless_access_point

SD-WAN

sd_wan

Storage

Resource

device_type value

Object storage

object_storage

Block storage

block_storage

File storage

file_storage

Archive storage

archive_storage

Database

Resource

device_type value

Relational (SQL)

database_relational

NoSQL

database_nosql

Data warehouse

data_warehouse

In-memory or cache

database_cache

DevOps and app integration

Resource

device_type value

CI/CD platforms

ci_cd_platform

Container registries

container_registry

Message queues

message_queue

API endpoints

api_endpoint

Note

If your resource does not fit one of these standard types, open a topic on the Ansible forum to propose a new device type.

Adding resource reporting to a collection

To add resource reporting to your collection, create a query file and write jq expressions that extract resource metadata from your module return values.

Creating the query file

Create the file extensions/audit/event_query.yml in your collection root directory. This is the standard location for embedded query files that ship with your collection.

my_namespace/my_collection/
├── extensions/
│   └── audit/
│       └── event_query.yml
├── plugins/
├── meta/
└── galaxy.yml

Writing the query

The query file maps each module to a jq expression using the Fully Qualified Collection Name (FQCN) as the key. The jq expression runs against the module return values and must output a JSON object with the following fields:

Field

Requirement

Description

name

Required

A human-readable display name for the resource, for example a VM name or switch hostname.

canonical_facts

Required

A dictionary of facts that uniquely identify the resource and deduplicate it across job runs.

facts

Optional

Metadata for categorization. Must include device_type when present.

The following example shows a complete query file entry:

---
# extensions/audit/event_query.yml
community.vmware.vmware_guest:
  query: >-
    {
      name: .instance.hw_name,
      canonical_facts: {
        host_name: .instance.hw_name,
        uuid: .instance.hw_product_uuid
      },
      facts: {
        device_type: "virtual_machine",
        guest_id: .instance.hw_guest_id
      }
    }

Choosing canonical facts

Select fields that are globally unique and stable for the life of the resource.

  • Recommended: UUIDs, serial numbers, permanent MAC addresses.

  • Conditional: Hostnames (if unique in the environment), IP addresses (static only).

  • Avoid: Dynamic IPs, random session IDs.

{
  "canonical_facts": {
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "serial_number": "XYZ-123"
  }
}

Setting the device type

Map your resource to a standard device_type value from the normalized resource taxonomy. You can optionally include a platform field to indicate the underlying platform, for example aws, vmware, or azure.

{
  "facts": {
    "device_type": "virtual_machine",
    "platform": "vmware",
    "guest_id": "rhel8_64"
  }
}

Platform-specific examples

Different platforms require different querying strategies based on how their APIs return data.

VMware (flat structure)

VMware modules return a flat structure where the top-level key defines the node type. Map directly to the returned keys.

---
# extensions/audit/event_query.yml
community.vmware.vmware_guest:
  query: >-
    {
      name: .instance.hw_name,
      canonical_facts: {
        host_name: .instance.hw_name,
        uuid: .instance.hw_product_uuid
      },
      facts: {
        device_type: "virtual_machine",
        guest_id: .instance.hw_guest_id
      }
    }

Azure (hierarchical structure)

Azure resources are hierarchical and use verbose resource IDs. Because the full Azure resource ID contains the resource type, you can use a jq regex to dynamically capture and categorize it.

azure.azcollection.azure_rm_virtualmachine:
  query: >-
    {
      name: .name,
      canonical_facts: {
        id: .id
      },
      facts: {
        device_type: "virtual_machine",
        azure_type: ((.id | capture("/providers/[Mm]icrosoft.(?<resourcetype>[^/]+)/")? |
          .resourcetype) | ascii_downcase)
      }
    }

In this example, Microsoft.Compute in the resource ID is captured and lowercased to "compute".

AWS (implied types)

AWS info modules often return a list of resources, so your jq expression must iterate over them. The resource type is implied by the module you query rather than included in the resource ID.

amazon.aws.ec2_instance_info:
  query: >-
    .instances[] | {
      name: (.tags.Name // .instance_id),
      canonical_facts: {
        instance_id: .instance_id
      },
      facts: {
        device_type: "virtual_machine",
        status: .state.name
      }
    }

To manage the mapping from AWS module to resource type, define a mapping dictionary in your expression:

{
  "ec2_instance": "virtual_machine",
  "rds_instance": "database_relational",
  "ec2_vpc_net": "vpc"
}

Module matching rules

The query file uses module FQCNs as keys. You can target modules with exact matches or wildcard patterns.

Exact match

Target a specific module.

cisco.ios.ios_facts:
  query: >-
    ...

Wildcard match

Target all modules in a collection. Use this carefully because different modules return different data structures.

community.vmware.*:
  query: >-
    ...

Testing your query

Before publishing your collection, verify your jq expression against real module output.

  1. Run a playbook with your module and capture the JSON output. Register the return value or run the module with -vvv to see the full output.

    - name: Gather VM info
      community.vmware.vmware_guest:
        name: my-vm
      register: result
    
  2. Save the module output to a file and test your jq expression from the command line.

    cat module_output.json | jq '{
      name: .instance.hw_name,
      canonical_facts: {
        uuid: .instance.hw_uuid
      },
      facts: {
        device_type: "virtual_machine"
      }
    }'
    
  3. Validate that the output JSON contains a valid name string and non-empty canonical_facts.

See also

Developing collections

Learn how to develop Ansible collections

Collection structure

Directories and files included in a collection

Ansible forum

Got questions? Need help? Want to share your ideas? Visit the Ansible forum