Age Reports
Age Reports surfaces AWS resources that have outlived their useful life. Stale EBS snapshots, old S3 objects past their retention window, forgotten RDS automated backups, and unattached volumes all accumulate silently and drive up your cloud bill. Age Reports gives you pre-built, configurable reports for every major aging category — no custom SQL required.
How Age Reports Work
Calabi Cloud Query queries your live AWS resource inventory and computes an age for each resource based on its creation date, last modification date, or last access date depending on the resource type. Resources are then bucketed into configurable age tiers and surfaced in the Age Reports UI with counts, sizes, and direct links to the cloud console.
Built-in Report Types
| Report | Resource | Age field | Default thresholds |
|---|---|---|---|
| EC2 Snapshots | aws_ebs_snapshot | start_time | 30 / 60 / 90 / 180 days |
| RDS Snapshots | aws_rds_db_snapshot | snapshot_create_time | 30 / 60 / 90 days |
| EBS Volumes (unattached) | aws_ebs_volume | create_time | 30 / 60 / 90 days |
| S3 Objects by age tier | aws_s3_object | last_modified | 30 / 60 / 90 / 180 / 365 days |
| AMIs (unused) | aws_ec2_ami | creation_date | 60 / 180 / 365 days |
| CloudWatch Log Groups | aws_cloudwatch_log_group | creation_time | 90 / 180 days |
| Secrets Manager secrets | aws_secretsmanager_secret | last_accessed_date | 30 / 90 days |
| IAM Access Keys | aws_iam_access_key | create_date | 90 / 180 days |
Each report can be run on demand or scheduled. Thresholds can be customised per report in the Configure sub-module.
Configuring Thresholds
Thresholds define the breakpoints between age tiers. Calabi ships with sensible defaults aligned to common cloud hygiene standards, but you can adjust them to match your organization's data lifecycle policy.
To change thresholds:
- Navigate to Cloud Operations > Configure.
- Under Age Report Settings, find the report you want to adjust.
- Edit the day values for each tier (Current, Aging, Stale, Expired).
- Click Save. The change takes effect on the next report run.
Recommended threshold guidelines:
| Policy type | Snapshot threshold | S3 object threshold |
|---|---|---|
| Strict (finance / healthcare) | 30 / 60 / 90 days | 30 / 60 / 90 / 180 days |
| Standard | 30 / 60 / 90 / 180 days | 60 / 90 / 180 / 365 days |
| Relaxed | 60 / 90 / 180 / 365 days | 90 / 180 / 365 days |
Reading an Age Report
Each built-in report displays a summary table with:
- Count — number of resources in each age tier
- Total size — storage consumed (GB or TB) per tier
- Estimated monthly cost — based on AWS pricing for the resource type and region
- Oldest resource — the single oldest resource and its creation date
- Resource list — an expandable table of individual resources with creation date, size, region, and tags
The summary bar chart provides a quick visual of how resources are distributed across age tiers.
Exporting Report Results
Reports can be exported in two formats:
| Format | Contents | How to export |
|---|---|---|
| CSV | Full resource list with all columns | Click Export CSV on any report |
| JSON | Structured resource data for programmatic use | Click Export JSON on any report |
Exports respect the current filter settings — if you have filtered the report to a specific region or tag, the export reflects that filter.
Alerting on Aging Resources
You can configure an alert to fire when any report tier exceeds a count or size threshold.
Example: alert the #cloud-ops Slack channel when unattached EBS volumes in the "Expired" tier (90+ days) exceed 10 volumes.
- Open Cloud Operations > Age Reports.
- Select the EBS Volumes (unattached) report.
- Click Set Alert.
- Set the condition:
Expired tier count > 10. - Choose the notification channel:
Slack — #cloud-ops. - Set the alert schedule:
Daily at 09:00 UTC. - Click Save Alert.
Example SQL for Aging Queries
The built-in reports are powered by Calabi Cloud Query. You can write your own aging queries in Query Resources for custom use cases.
EC2 snapshots older than 90 days
SELECT
snapshot_id,
volume_id,
volume_size,
region,
start_time,
DATE_PART('day', NOW() - start_time) AS age_days,
description,
tags ->> 'Name' AS name
FROM
aws_ebs_snapshot
WHERE
owner_id = '123456789012'
AND DATE_PART('day', NOW() - start_time) > 90
ORDER BY
age_days DESC;
S3 objects by age tier (last 12 months)
SELECT
bucket_name,
CASE
WHEN DATE_PART('day', NOW() - last_modified) <= 30 THEN '0-30 days'
WHEN DATE_PART('day', NOW() - last_modified) <= 90 THEN '31-90 days'
WHEN DATE_PART('day', NOW() - last_modified) <= 180 THEN '91-180 days'
ELSE '180+ days'
END AS age_tier,
COUNT(*) AS object_count,
ROUND(SUM(size) / 1073741824.0, 2) AS total_size_gb
FROM
aws_s3_object
WHERE
bucket_name = 'my-data-bucket'
GROUP BY
bucket_name, age_tier
ORDER BY
age_tier;
Unattached EBS volumes
SELECT
volume_id,
volume_type,
size AS size_gb,
region,
create_time,
DATE_PART('day', NOW() - create_time) AS age_days,
state
FROM
aws_ebs_volume
WHERE
state = 'available' -- 'available' = not attached
AND DATE_PART('day', NOW() - create_time) > 30
ORDER BY
age_days DESC;
IAM access keys older than 90 days
SELECT
u.name AS iam_user,
k.access_key_id,
k.create_date,
k.status,
DATE_PART('day', NOW() - k.create_date) AS key_age_days
FROM
aws_iam_access_key k
JOIN aws_iam_user u ON k.user_name = u.name
WHERE
k.status = 'Active'
AND DATE_PART('day', NOW() - k.create_date) > 90
ORDER BY
key_age_days DESC;
Related Pages
- Query Resources — Write custom SQL against live AWS inventory
- Cost Analytics — Understand the cost impact of aging and orphaned resources
- Configure — Adjust thresholds and configure alert channels