Chapter 26 · AWS Billing And Cost Management
Subchapter 26.5
references/cur-athena.mdMarkdown7 KBView on GitHub
AWS has three billing data formats. Determine which the customer is using before writing queries:
| Format | Table Name | Status | Key Differences |
|---|---|---|---|
| CUR 2.0 | COST_AND_USAGE_REPORT | Recommended | Fixed schema, nested columns (resource_tags, cost_category, product, discount are key-value maps), Parquet/GZIP only. Created via AWS Data Exports. |
| Legacy CUR | User-defined | Still supported, no deprecation planned | Dynamic schema (columns vary monthly based on usage), tags/categories as separate columns (e.g., resource_tags_user_creator), supports CSV/ZIP/GZIP/Parquet. Created via CUR console or API. |
| FOCUS 1.2 | FOCUS_1_2_AWS | GA | FinOps Open Cost and Usage Specification — cloud-agnostic schema for multi-cloud FinOps. Different column names entirely (e.g., BilledCost, EffectiveCost, ServiceName). Created via AWS Data Exports. |
How to tell which format a customer has: Ask, or check the Data Exports console. If they reference billing_period as a string column, they’re likely on Legacy CUR. If they reference bill_billing_period_start_date as a timestamp, they’re on CUR 2.0.
Key query differences between Legacy CUR and CUR 2.0:
billing_period = '2026-03' (string). CUR 2.0 uses bill_billing_period_start_date = TIMESTAMP '2026-03-01' (timestamp).resource_tags_user_<tagname> as separate columns. CUR 2.0 nests all tags into a resource_tags map column — query with resource_tags['user:tagname'].product_<attribute> as separate columns. CUR 2.0 nests into product map — query with product['attribute'].COST_AND_USAGE_REPORT.aws bcm-data-exports create-export --export '{
"Name":"MyCUR2Export",
"DataQuery":{"QueryStatement":"SELECT * FROM COST_AND_USAGE_REPORT",
"TableConfigurations":{"COST_AND_USAGE_REPORT":{"TIME_GRANULARITY":"DAILY","INCLUDE_RESOURCES":"TRUE"}}},
"DestinationConfigurations":{"S3Destination":{"S3Bucket":"my-cur-bucket","S3Prefix":"cur2","S3Region":"us-east-1",
"S3OutputConfigurations":{"OutputType":"CUSTOM","Format":"PARQUET","Compression":"PARQUET","Overwrite":"OVERWRITE_REPORT"}}},
"RefreshCadence":{"Frequency":"SYNCHRONOUS"}}'Always use PARQUET — 10-100x cheaper Athena queries than CSV. Set INCLUDE_RESOURCES=TRUE only if per-resource analysis needed (dramatically increases data volume).
| Group | Key Columns | Use |
|---|---|---|
| line_item | unblended_cost, resource_id, product_code, usage_amount | Core cost data |
| savings_plan | savings_plan_effective_cost, savings_plan_a_r_n | SP analysis |
| reservation | reservation_a_r_n, effective_cost, unused_quantity | RI analysis |
| pricing | public_on_demand_cost, public_on_demand_rate | On-demand comparison |
| resource_tags | Legacy CUR: resource_tags_user_<tagname> columns; CUR 2.0: resource_tags map — query with resource_tags['user:tagname'] | Tag-based allocation |
CUR 2.0 uses bill_billing_period_start_date as a TIMESTAMP column, not a string. Filter with TIMESTAMP literal or date_trunc:
-- Monthly cost by service
SELECT line_item_product_code AS service, SUM(line_item_unblended_cost) AS cost
FROM cost_and_usage_report
WHERE bill_billing_period_start_date = TIMESTAMP '2026-03-01'
GROUP BY line_item_product_code ORDER BY cost DESC;
-- Top 10 most expensive resources
SELECT line_item_resource_id, line_item_product_code, SUM(line_item_unblended_cost) AS cost
FROM cost_and_usage_report
WHERE bill_billing_period_start_date = TIMESTAMP '2026-03-01' AND line_item_resource_id != ''
GROUP BY line_item_resource_id, line_item_product_code ORDER BY cost DESC LIMIT 10;
-- Data transfer breakdown (uses same regex patterns as cost-explorer.md)
SELECT line_item_product_code, line_item_usage_type,
SUM(line_item_usage_amount) AS usage_gb, SUM(line_item_unblended_cost) AS cost
FROM cost_and_usage_report
WHERE bill_billing_period_start_date = TIMESTAMP '2026-03-01'
AND (
REGEXP_LIKE(line_item_usage_type,
'DataTransfer|AWS-(In|Out)-Bytes|Bytes-(Internet|AWS)|CloudFront-.*-Bytes|DataXfer|-ABytes-')
OR REGEXP_LIKE(line_item_usage_type,
'NatGateway-Bytes|VpcEndpoint-Bytes|TransitGateway-Bytes')
OR (line_item_usage_type LIKE '%DataProcessing-Bytes%'
AND line_item_product_code = 'AWSELB')
)
GROUP BY line_item_product_code, line_item_usage_type ORDER BY cost DESC;
-- SP effective rate vs on-demand
SELECT line_item_product_code,
SUM(savings_plan_savings_plan_effective_cost) AS sp_cost,
SUM(pricing_public_on_demand_cost) AS ondemand_cost,
ROUND(1 - SUM(savings_plan_savings_plan_effective_cost) / NULLIF(SUM(pricing_public_on_demand_cost), 0), 3) AS savings_pct
FROM cost_and_usage_report
WHERE savings_plan_savings_plan_a_r_n IS NOT NULL
AND bill_billing_period_start_date = TIMESTAMP '2026-03-01'
GROUP BY line_item_product_code;Elastic Load Balancing). CUR uses API-style product codes (e.g., AWSELB). Before writing filter queries, run SELECT DISTINCT line_item_product_code to discover available values. If a filtered query returns 0 results, check the product code first.COST_AND_USAGE_REPORT (fixed) — not user-definedresource_tags_user_<tagname> columns. CUR 2.0 uses resource_tags['user:tagname'] map syntax. Neither matches Cost Explorer API, which uses the tag key directly.