Reports YAML Documentation with Examples
Each report is defined as YAML code with three main sections: report properties, widgets, and optional filters.
Report Properties
A report starts with the report definition section with the following properties:
| Property | Description |
|---|---|
title | The report title displayed in the main heading and in the list of reports |
shortDescription | Brief description displayed as a subtitle in the report |
longDescription | Markdown-formatted text displayed in the Tips for using this report popup |
logo | One of the built-in images for report cards (see values below) |
tags | Array of labels for filtering reports in the main Reports view |
filters | Optional array of report-level filters (see Filters) |
widgets | Array of visualizations displayed in the report (see Widgets) |
report:
title: <<Your report title>>
shortDescription: <<Your report short description>>
longDescription: |
## What does this report show?
<<Your report long description in markdown>>
logo: <<tags | aws | azure | github | limits | users | ip | identity | cloud | end_of_life | cost_saving | k8s | high_risk | okta | default>>
tags:
- tag1
- tag2
filters:
- type: generic
...
widgets:
- title: "first widget"
...
Widgets
A widget has the following properties:
title: The title of the widget
| Property | Description |
|---|---|
title | The widget title |
display | Display options including type (see widget types below) and optional width |
query | ClickHouse SQL query that populates the widget |
- title: <<sample widget title>>
display:
type: <<pie_chart | table | label | bar_chart | xy_chart>>
query: |
select if(length(tags) = 0, 'Untagged Resources', 'Tagged Resources') AS tags_status, count(*)
from cloud_assets
where resource_category != '' and supports_tags = true
group by tags_status
order by tags_status ascLabel
A label shows text or a single number.
- title: label widget
display:
type: label
query: |
select 1
A label widget
Table
The table widget displays the query result as-is.
- title: table widget
display:
type: table
query: |
select 'EU' as region, 2 as count, toDate(now()) as date union all
select 'US' as region, 3 as count, toDate(now()) as date
A table widget
Pie Chart
The pie chart visualizes the first two columns of the query result. The first column must be a string, the second must be a number.
Optionally, display a total in the center of the pie chart using the total property.
- title: pie chart widget
display:
type: pie_chart
total:
show: true
label: pie chart inner label
query: |
select 'slice 1' as slice, 10 as value union all
select 'slice 2' as slice, 20 as value union all
select 'slice 3' as slice, 5 as value
A pie chart widget with the sum of all values displayed
Bar Chart
The bar chart visualizes the first two columns of the query result. The first column must be a string, the second must be a number.
Use the entryLabel property to customize the label shown on hover.
- title: bar chart widget
display:
type: bar_chart
entryLabel: average
query: |
select 'row 1' as x, 2 as y union all
select 'row 2' as x, 3 as y
A bar chart widget
Bar Chart with Multiple Series
To visualize multiple series, put the values and series names in a single column of the map type.
- title: bar chart with multiple series
display:
type: bar_chart
width: 100%
query: |
WITH
toDate('2024-01-01') AS start_date,
toDate('2024-01-05') AS end_date
SELECT
date,
map(
'series 1', randUniform(10, 50),
'series 2', randUniform(0, 100),
'series 3', randUniform(10, 30)
),
FROM
(
SELECT addDays(start_date, number) AS date
FROM numbers(dateDiff('day', start_date, end_date) + 1)
)
ORDER BY
date;
Multi-series bar chart using the map column type
A practical example using Historical Snapshots data, showing storage bucket counts by day and cloud provider:
SELECT
date,
map(
'aws', sumIf(bucket_count, cloud = 'aws'),
'gcp', sumIf(bucket_count, cloud = 'gcp')
) AS storage_counts
FROM (
SELECT
toDate(_cq_snapshot_time) AS date,
'aws' AS cloud,
count() AS bucket_count
FROM snapshot_aws_s3_buckets
GROUP BY date
UNION ALL
SELECT
toDate(_cq_snapshot_time) AS date,
'gcp' AS cloud,
count() AS bucket_count
FROM snapshot_gcp_storage_buckets
GROUP BY date
)
GROUP BY date
ORDER BY date;X-Y Chart
The X-Y chart is a line chart that displays multiple data series over the same X axis. Unlike other widgets, it accepts an array of queries instead of a single query. Each query has a title (series name) and the SQL query.
- title: XY-chart
display:
type: xy_chart
width: 100%
queries:
- title: Random series 1
query: |
WITH
toDate('2024-01-01') AS start_date,
toDate('2024-01-05') AS end_date
SELECT
date,
rand() / 1000000000 as random_normal
FROM
(
SELECT addDays(start_date, number) AS date
FROM numbers(dateDiff('day', start_date, end_date) + 1)
)
ORDER BY
date;
- title: Random series 2
query: |
WITH
toDate('2024-01-01') AS start_date,
toDate('2024-01-05') AS end_date
SELECT
date,
rand() / 1000000000 AS random_normal
FROM
(
SELECT addDays(start_date, number) AS date
FROM numbers(dateDiff('day', start_date, end_date) + 1)
)
ORDER BY
date;
An X-Y chart widget with a hover detail displayed
Filters
Add top-level filters to a report to let users interactively filter widget data. Each filter is defined by its type, label, expression, and optionally the available values.
| Property | Description |
|---|---|
type | Filter type: generic (dropdown), date (date picker), or search (free-form input) |
filterLabel | Label displayed to the user |
filterExpression | Placeholder expression used in widget SQL queries. CloudQuery replaces it when the user selects a value |
defaultValue | Optional default value applied on first load |
To connect a filter to a widget, include the filterExpression in the widget SQL WHERE clause.
Each filter expression needs to be unique within the report. There cannot be two filters with the same filter expression, such as account_id = '?' . If you need to have two filters filtering on the same column name, consider using the as keyword in the SQL queries to rename the columns to match the specific filter expression.
For example, you can define the filter expression as account = '?' . Then in the SQL query, define the where clause using this exact expression:
...
query: |
select account, name from cloud_assets
where 1=1 and account = '?'
...Use 1=1 before the filter expression so that when no filter value is selected, the widget shows unfiltered results.
The examples below show each filter type with a matching widget.
Generic Filter with Statically Defined Options
This filter is a dropdown with a static list of options defined in the code. Each option has a label displayed to the user and value used in the SQL query.
- type: generic
filterLabel: Option
filterExpression: account_id = '?'
options:
- label: account 1
value: 1
- label: account 2
value: 2
- label: account 3
value: 3
A generic filter with statically defined options
An example of a table widget using this filter:
- title: Widget with a generic option filter
display:
type: table
query: |
select account_id as account_id_filtered, account_name from (
select 1 as account_id, 'account 1' as account_name union all
select 2 as account_id, 'account 2' as account_name
)
where 1=1 and account_id = '?'Generic Filter with Options from a SQL Query
This filter looks the same as above, but loads options dynamically from a SQL query. You can use any CloudQuery table to populate the values.
- type: generic
filterLabel: Account
filterExpression: account_id = '?'
query: |
select value, label from (
select 1 as value, 'account 1' as label union all
select 2 as value, 'account 2' as label union all
select 3 as value, 'account 3' as label
) order by valueMultiple Select Filter
Building on the above filters, you can enable multi-select functionality by adding the multiple: true attribute. This allows users to select multiple values from the dropdown, filtering results that match any of the selected options.
An example filter configuration:
- type: generic
filterLabel: Accounts
filterExpression: account_id IN (?)
multiple: true
options:
- label: account 1
value: 1
- label: account 2
value: 2
- label: account 3
value: 3An example widget using this multi-select filter:
- title: Widget with a multiple generic option filter
display:
type: table
query: |
select account_id as account_id_filtered, account_name from (
select 1 as account_id, 'account 1' as account_name union all
select 2 as account_id, 'account 2' as account_name union all
select 3 as account_id, 'account 3' as account_name
)
where 1=1 and account_id IN (?)Date Selector
The date selector provides a calendar dropdown with preset buttons for past intervals (7 days ago, 14 days ago, and so on).
- type: date
filterLabel: Event Date
filterExpression: event_time < '?'
defaultValue: now() - interval 30 DAY
A date selector filter
An example widget using the filter above:
- title: widget filtered by Event Date
display:
type: table
query: |
select event_time from (
select now() as event_time union all
select now() - interval 10 day as event_time
)
where 1=1 and event_time < '?'Date Selector with Future Dates
Add mode: future to change the preset buttons to point to future dates:
- type: date
filterLabel: Expiration
filterExpression: expiration_date < '?'
defaultValue: now() + interval 30 DAY
mode: future
A date selector filter with future mode
Free-Form Search Filter
This free-form search input lets users type in any value.
- type: search
filterExpression: ip_address LIKE '%?%'
filterLabel: IP Address
A free-form search filter
An example widget using this filter:
- title: widget using search without default value
display:
type: table
query: |
select ip_address from (
select '192.168.1.1' as ip_address union all
select '192.168.2.1' as ip_address union all
select '192.168.3.1' as ip_address
)
where 1=1 and ip_address LIKE '%?%'Free-Form Search Filter with a Default Value
Add a defaultValue property to the above to always load a fresh report with this filter set automatically.
- type: search
filterExpression: average_load >= '?'
filterLabel: minimum average load (%)
defaultValue: 50An example widget using this filter:
- title: widget using search with default value
display:
type: table
query: |
select ip_address, average_load from (
select '192.168.1.1' as ip_address, 30 as average_load union all
select '192.168.1.2' as ip_address, 40 as average_load union all
select '192.168.2.1' as ip_address, 50 as average_load union all
select '192.168.3.1' as ip_address, 60 as average_load
)
where 1=1 and average_load >= '?'Advanced Options
Setting Widget Width
Use the width property to set the widget width as a percentage of the view port. Available on all widget types.
- title: label widget
display:
type: label
width: 50%
query: |
select 1Adding Search to a Table Widget
Add a search_string column to the SQL query. The column is not displayed in the table. Instead, a search box appears in the table header that filters rows based on this column. Use concat() to make multiple columns searchable.
- title: table widget with search
display:
type: table
query: |
select 'EU' as region, 2 as count, toDate(now()) as date, concat('eu','abc') as search_string union all
select 'US' as region, 3 as count, toDate(now()) as date, concat('us','def') as search_string
A table widget with the search functionality enabled
Groups
Wrap widgets in collapsible groups to organize the report into sections.
title: The title of the group
description: The subtitle displayed below the title.
widgets: An array of widgets.
groups:
- title: first group
description: first group with widgets
widgets:
- title: label widget
display:
type: label
query: |
select 1
A report with groups
Next Steps
- Full Report Example - Complete YAML report example
- Built-in Report Templates - Browse ready-to-use templates
- SQL Console - Test queries before adding them to reports