AWS Cost and Usage Report: The Complete Expert-Level Guide
A deep, row-level walk through how the Cost and Usage Report actually captures every billable second of AWS usage — schema internals, amortization mechanics, partitioning strategy, and the FinOps patterns real cost engineering teams build on top of it.
Imagine a household that wanted to know not just “how much was the electric bill this month,” but exactly which appliance, in which room, running at which hour, was responsible for every single cent of it — down to the exact minute the refrigerator’s compressor kicked on. That is the level of granularity the Cost and Usage Report (CUR) gives an organization about its AWS bill: not a summary number, but a line-item ledger of every resource, every hour, every pricing decision that added up to the final invoice. This guide moves past “CUR is a detailed billing export” and into the schema internals, amortization logic, and query-engineering patterns that real cost engineering and FinOps teams build entire cost-visibility platforms on top of.
AAdvanced Core Concepts
This chapter assumes you already know that CUR is “a detailed CSV of your AWS bill.” We go straight into the concepts that matter once you’re building real cost analytics on top of it: the legacy CUR versus the newer Data Exports model, line item types, and the granularity settings that decide whether your report is actually useful.
Legacy CUR vs. AWS Data Exports (CUR 2.0)
The original Cost and Usage Report delivers a single, wide, denormalized CSV or Parquet file per billing period to an S3 bucket you own. AWS Data Exports, the modern successor commonly referred to as CUR 2.0, reframes the same underlying billing data as a queryable, versioned table with a defined schema, supporting more flexible column selection, native Parquet output, and better integration with query engines out of the box — while remaining built on the exact same underlying billing line-item data as legacy CUR.
Legacy CUR
Flat file delivery to S3, gzip CSV or Parquet, one file set per report period, manually configured refresh and versioning behavior.
Data Exports (CUR 2.0)
Table-based export model with a formal schema definition, native support for standard and Parquet formats, and purpose-built for direct Athena/Glue querying.
Line Item Types — The Core of the Schema
Every row in CUR carries a line_item_type that fundamentally changes how that row should be interpreted: Usage rows represent actual on-demand or metered consumption; DiscountedUsage rows represent usage covered by a Reserved Instance or Savings Plan commitment; Fee rows represent the upfront or recurring fee for that commitment itself; Refund and Credit rows adjust the bill downward; and Tax rows capture applicable tax line items separately from the underlying usage.
Resource-Level Granularity
CUR can be configured with resource IDs enabled, which adds the specific resource identifier (an EC2 instance ID, an S3 bucket name, a Lambda function ARN) to every applicable row. Without this setting, cost data is aggregated at the service/usage-type level with no way to trace a specific dollar amount back to a specific running resource — a setting every serious cost-allocation initiative must enable from day one, since it cannot be retroactively applied to historical data already generated without it.
A CUR without resource IDs is like a credit card statement that only says “Restaurants: $340 this month.” A CUR with resource IDs enabled is the itemized receipt from every single meal — which restaurant, which day, which order — the difference between knowing you spent money on food and knowing exactly where every dollar went.
If asked “how would you attribute AWS spend to individual teams,” the strong advanced answer names resource-level CUR data joined against cost allocation tags — not the Billing Console’s cost breakdown UI alone, since the UI cannot express custom attribution logic or join against external metadata the way a queryable CUR dataset can.
BInternal Working
Here we open the hood on what actually happens between AWS metering a resource’s usage and that usage appearing as a queryable row in your CUR files.
From Metering to Line Item
Every AWS service independently meters its own usage internally (an EC2 instance-hour, a Lambda invocation and its duration, an S3 GET request) and reports that raw usage into AWS’s central billing pipeline. The billing pipeline applies the account’s pricing — on-demand rates, any applicable Reserved Instance or Savings Plan discount, negotiated Enterprise Discount Program rates if present — and produces the fully-priced line items that eventually populate CUR. This is why CUR reflects billed cost, not raw usage volume; the same underlying usage can appear at wildly different unblended_cost values depending on which pricing commitment happened to apply that hour.
graph TB
subgraph "AWS Service Layer"
EC2[EC2 Usage Metering]
S3M[S3 Usage Metering]
LAM[Lambda Usage Metering]
end
EC2 --> PIPE[Central Billing Pipeline]
S3M --> PIPE
LAM --> PIPE
PIPE --> PRICE[Apply Pricing: On-Demand / RI / Savings Plan / EDP]
PRICE --> LINEITEM[Generate Priced Line Items]
LINEITEM --> CUR[CUR File Generation]
CUR --> S3OUT[(Delivered to S3 Bucket)]
Amortization: The Detail That Trips Up Every New Cost Analyst
When you buy a one-year Reserved Instance with an upfront payment, the raw billing data treats that upfront payment as one giant cost hitting on the day of purchase, with $0 attributed to the hours it actually covers afterward — clearly useless for month-over-month cost trending. CUR’s amortized cost columns solve this by spreading that upfront fee evenly across every hour of the commitment’s term, so a monthly cost report shows a smooth, representative cost each month rather than one enormous spike on purchase day followed by artificially cheap months afterward.
Using unblended_cost for monthly trend reporting when Reserved Instances or Savings Plans with upfront payments are in play. This produces misleading spikes and troughs. Use the amortized cost columns for any trend analysis spanning a commitment purchase.
Unblended vs. Blended Cost in Consolidated Billing
unblended_cost reflects the actual rate charged for that specific account’s usage in that specific hour. blended_cost, relevant only in AWS Organizations consolidated billing, averages the effective rate for a given usage type across all linked accounts sharing a Reserved Instance or Savings Plan — meaning two accounts running identical usage can show different blended costs depending on what the rest of the organization is doing, which is precisely why most cost engineering work defaults to unblended cost for per-account accuracy.
CData Flow & Lifecycle
The Full Lifecycle of a Report Period
Usage Occurs
Resources across the account (or organization) generate metered usage throughout the billing period.
Initial Report Generation
CUR generates an initial report for the current month within roughly a day of period start, reflecting usage so far.
Periodic Refreshes
Throughout the month, CUR refreshes the report multiple times daily as new usage and any billing corrections come in.
Manifest File Update
Each refresh updates an accompanying JSON manifest describing exactly which report files belong to the current version of that billing period.
Finalization
After the billing period closes and any late-arriving usage or credits settle, the report for that period stabilizes into its final version.
Retroactive Adjustment
Even “final” months can be revised later if AWS applies a retroactive credit or billing correction, re-triggering a new manifest version for that historical period.
Why the Manifest File Is Not Optional Reading
Every report refresh is accompanied by a manifest JSON file listing the exact set of data files that constitute the current, authoritative version of that period’s report. Any pipeline that ingests CUR data by simply globbing “all files in this month’s folder” without consulting the manifest risks double-counting or mixing stale files from a previous version with new ones — the manifest is the single source of truth for “which files actually belong together right now.”
“Why might your CUR-based cost dashboard show numbers that don’t match the Billing Console?” — the strong advanced answer names report refresh timing and manifest versioning as the most likely cause, not a data pipeline bug, since CUR data for the current month is explicitly provisional until the period closes.
DAdvantages, Disadvantages & Trade-offs
Advantages
- Most granular, complete billing dataset AWS offers — every line item that constitutes the actual invoice
- Resource-level granularity enables true cost allocation down to individual EC2 instances, Lambda functions, or S3 buckets
- Native Parquet and Athena/Glue integration make it directly queryable at scale without a separate ETL pipeline
- Amortized cost columns solve the upfront-payment distortion problem automatically, without custom logic
- Forms the raw data foundation underneath most third-party and in-house FinOps tooling
Disadvantages
- Extremely wide schema (dozens to 100+ columns) with real learning curve to use correctly
- Current-period data is provisional and can shift multiple times before finalizing, complicating naive “always show today’s spend” dashboards
- Resource ID granularity cannot be retroactively backfilled onto historical reports generated without it enabled
- Raw file storage and query costs (S3 storage, Athena scan costs) scale with organization size and are easy to underestimate at first
The Trade-off That Matters Most: Granularity vs. Query Cost
Enabling every optional CUR column and resource-level detail maximizes analytical power but also maximizes file size and, downstream, the cost of scanning that data with a query engine like Athena. Advanced teams partition and format the data deliberately (Parquet over CSV, partitioned by billing period) specifically to keep this granularity affordable to query repeatedly rather than treating it as a one-time export.
| Dimension | Legacy CUR | Data Exports (CUR 2.0) |
|---|---|---|
| Delivery model | Flat files to S3 | Schema-defined table export |
| Native Parquet support | Optional, configured manually | First-class, default-friendly |
| Column selection | All-or-nothing per report | Flexible per export definition |
| Recommended for | Existing legacy pipelines | New cost analytics builds |
EPerformance & Scalability
Format Choice Is the Single Biggest Performance Lever
Storing CUR as gzip-compressed CSV is the legacy default but is dramatically slower and more expensive to query than Parquet — a columnar format that lets a query engine like Athena read only the specific columns a query actually needs, instead of decompressing and scanning every column of every row. For any organization running CUR queries regularly rather than as a one-off export, switching to Parquet is close to a mandatory first optimization.
Partitioning by Billing Period
CUR data delivered with proper partitioning (by year and month, and ideally further by day for very large organizations) allows a query engine to skip scanning entire partitions that aren’t relevant to a given query’s date range — the difference between an Athena query that scans a single month’s data versus one that scans every month of history ever generated, which directly translates into both query cost and query latency.
ROW-BASED CSV
BILLING PERIOD
PER LINE ITEM
Query Engineering: Selecting Only What You Need
Because CUR schemas are extremely wide, queries that explicitly select only the columns needed for a given analysis (rather than SELECT *) meaningfully reduce both scan cost and result size on a columnar format — a habit worth enforcing across any team building recurring cost reports on top of CUR data.
“Your CUR-based Athena queries are getting expensive at scale — what would you check?” — the strong advanced answer covers file format (CSV vs. Parquet), partitioning strategy, and whether queries select specific columns versus scanning everything, in that priority order, since format and partitioning typically dwarf any other optimization available.
FHigh Availability & Reliability
Reliability Here Means Data Integrity, Not Uptime
CUR is not a running service with an availability SLA in the traditional sense — it’s a periodic data delivery pipeline. The reliability concern that actually matters is whether your downstream consumption (Athena tables, dashboards, cost allocation reports) correctly handles report refreshes, versioning, and the eventual finalization of a billing period without silently working from stale or partial data.
Protecting the Destination S3 Bucket
Because CUR is the authoritative record used for cost allocation, chargeback, and potentially audit purposes, the destination S3 bucket warrants the same protective controls as any critical data store: versioning enabled, lifecycle policies that don’t prematurely delete historical report versions, and bucket policies that prevent accidental deletion or unauthorized modification of delivered report files.
Pattern
Building an Athena table or downstream pipeline that reads CUR files directly by date-prefix glob pattern instead of consulting the manifest for the current, authoritative file set.
Why It Fails
When a report refreshes mid-month, old and new file versions can coexist briefly in the same S3 prefix; a naive glob-based read risks double-counting rows from two different manifest versions of the same period.
Fix
Always resolve the current file set from the manifest JSON before ingesting, and rebuild the Athena partition metadata for a period whenever its manifest version changes, rather than assuming a static file list.
Handling Retroactive Corrections Gracefully
Because AWS can retroactively adjust even “finalized” historical billing periods (credits, corrections), a robust cost pipeline periodically re-checks the manifest of past periods rather than assuming a month’s data, once ingested, is permanently frozen — otherwise a legitimate credit applied months later silently never reaches downstream reports.
GSecurity
CUR Data Is Sensitive Business Information
A CUR export reveals detailed operational information — which services are used, at what scale, and by implication, aspects of an organization’s architecture and growth trajectory — alongside the financial data itself. Treating the destination S3 bucket with the same access rigor as financial records, rather than as generic operational log data, is the correct default posture.
Bucket Policy and Encryption
The CUR destination bucket should enforce server-side encryption (SSE-S3 or SSE-KMS), block public access unconditionally, and restrict write access to the AWS billing service principal alone — read access should be scoped narrowly to the specific roles and services (Athena, Glue, a BI tool’s execution role) that genuinely need to query the data, not broadly opened to convenience.
Cross-Account Access in Multi-Account Organizations
In an AWS Organizations setup, CUR is typically generated once at the management/payer account level covering all linked accounts, then made queryable by individual account or team owners through carefully scoped cross-account IAM roles or Lake Formation permissions — rather than granting every team direct access to the full organization-wide billing dataset, which would expose other teams’ cost data unnecessarily.
Granting broad read access to the raw CUR S3 bucket or Athena database to “everyone who needs cost visibility.” This exposes every team’s granular spend data to every other team. Use views, Lake Formation row/column-level permissions, or a curated reporting layer to scope visibility appropriately instead.
HMonitoring, Logging & Metrics
Report Delivery Freshness
Tracking the manifest’s last-modified timestamp confirms the pipeline is actually receiving expected refreshes, catching silent delivery failures early.
Row Count Anomalies
A sudden, unexplained drop or spike in row count for a period often indicates an ingestion or manifest-versioning problem rather than a genuine usage change.
Untagged Resource Cost Percentage
The share of total spend lacking cost allocation tags is a critical FinOps health metric — a rising untagged percentage means cost attribution accuracy is silently degrading.
Athena Data Scanned per Query
Monitoring this directly tracks whether partitioning and format optimizations are actually working, and flags queries that regress into full-table scans.
Because CUR itself has no native alerting layer, most of this monitoring is built by the consuming pipeline — S3 event notifications on new manifest delivery, scheduled data-quality checks on row counts and null rates, and dashboards tracking untagged spend percentage over time as a first-class FinOps KPI.
Reconciling CUR Numbers Against the Billing Console
When CUR-derived totals don’t match the Billing Console for the current month, the first check is always whether the CUR data being queried reflects the latest manifest version — current-month figures in both places are provisional and can differ simply due to refresh timing, not a data error in either system.
IDeployment & Cloud Architecture
The Standard Reference Architecture
A mature CUR deployment typically flows: CUR delivered as partitioned Parquet to a dedicated S3 bucket, a Glue crawler (or manually defined Glue table) maintaining up-to-date partition metadata as new periods land, Athena as the query engine for ad hoc and scheduled analysis, and a BI layer (commonly QuickSight, or a purpose-built internal dashboard) presenting curated views to different audiences — engineering leads seeing their own team’s cost breakdown, finance seeing organization-wide trends.
graph LR
BILLING[AWS Billing Pipeline] -->|CUR Files + Manifest| S3B[(S3 Bucket - Parquet, Partitioned)]
S3B --> GLUE[Glue Crawler / Catalog]
GLUE --> ATHENA[Athena Query Engine]
ATHENA --> QS[QuickSight Dashboards]
ATHENA --> CUSTOM[Custom FinOps Tooling]
Managing CUR Configuration as Code
The report definition itself — column selection, format, refresh settings, destination bucket — is defined as infrastructure (CloudFormation or Terraform) so it can be reviewed, versioned, and reliably reproduced if the organization needs to stand up an equivalent pipeline in a disaster-recovery or new-organization scenario.
CUDOS and Pre-Built Dashboard Frameworks
Rather than building cost dashboards entirely from scratch, many organizations start from AWS’s own published reference dashboard frameworks (commonly known by the acronym CUDOS) built specifically on top of CUR data, then customize from that baseline rather than reinventing common cost-visibility views like top spenders, commitment utilization, and untagged resource tracking.
JDesign Patterns & Anti-Patterns
Pattern: Cost Allocation Tags Joined Against Resource IDs
Combining resource-level CUR data with a well-enforced cost allocation tagging strategy (team, environment, project) turns raw billing rows into genuine chargeback and showback reports — the tags supply the “who owns this,” and CUR supplies the “how much did it actually cost.”
Pattern: Cost Categories for Non-Tag-Based Grouping
AWS Cost Categories let you define grouping rules based on account, service, or other billing dimensions independent of tags — useful for organizations where consistent tagging discipline isn’t fully achievable, letting a “which business unit” view be constructed from account structure or usage patterns instead of relying solely on tag compliance.
Pattern: Anomaly Detection Layered on Top of CUR Trends
Combining CUR historical data with AWS Cost Anomaly Detection (or a custom statistical model built on the same underlying data) catches unexpected spend spikes — a misconfigured auto-scaling group, an accidentally-public data transfer pattern — far earlier than a monthly manual review would.
Pattern
Building cost allocation reports entirely from tags while ignoring the meaningful percentage of spend that arrives untagged (shared services, data transfer, support fees).
Why It Fails
Untagged spend either disappears from team-level reports entirely or gets silently misattributed, undermining trust in the chargeback numbers the moment someone reconciles them against the actual invoice total.
Fix
Explicitly track and report the untagged spend percentage as its own line item, and use a documented allocation methodology (even a simple even-split or usage-proportional rule) for genuinely shared costs rather than letting them vanish from the analysis.
KBest Practices & Common Mistakes
Best Practices
- Enable resource-level granularity from the very first report — it cannot be applied retroactively
- Deliver reports as partitioned Parquet, not gzip CSV, for any organization querying the data regularly
- Always resolve the current file set from the manifest rather than globbing S3 prefixes directly
- Use amortized cost columns for any trend analysis spanning Reserved Instance or Savings Plan purchases
- Track untagged spend percentage explicitly as an ongoing FinOps health metric
Common Mistakes
- Treating current-month CUR figures as final before the billing period has actually closed
- Granting broad, unscoped access to the raw CUR dataset instead of curated, permission-scoped views per audience
- Running
SELECT *queries against a wide, high-column-count CUR table routinely, inflating scan costs unnecessarily - Ignoring retroactive billing corrections on already-ingested historical periods
- Building chargeback reports purely from tags without accounting for shared, untagged spend
LReal-World & Industry Examples
Enterprise FinOps Practices
Large enterprises running dedicated FinOps functions build their entire showback/chargeback reporting, savings plan coverage analysis, and anomaly detection stack directly on top of CUR data, because it’s the only AWS data source with the granularity to defend cost attribution decisions down to an individual resource.
SaaS Companies Tracking Cost per Customer
Multi-tenant SaaS providers join resource-level CUR data against their own internal tenant-to-resource mapping to calculate actual infrastructure cost per customer — a calculation that directly informs pricing strategy and identifies unprofitable customer segments.
Consolidated Billing Across Large Organizations
Organizations with dozens or hundreds of linked accounts under AWS Organizations use a single management-account-level CUR export as the authoritative source for organization-wide spend visibility, avoiding the impossible task of manually reconciling separate bills from every individual account.
MFrequently Asked Questions
NSummary & Key Takeaways
What to Remember
- CUR captures every billed line item, distinguished by line item type — Usage, DiscountedUsage, Fee, Refund, Credit, and Tax each mean something different.
- Resource-level granularity must be enabled from the start — it cannot be applied retroactively to historical data.
- Amortized cost, not unblended cost, is correct for trend reporting whenever Reserved Instance or Savings Plan upfront payments are involved.
- The manifest file is the authoritative source of truth for which files belong to a given report version — never rely on naive S3 prefix globbing.
- Parquet with proper partitioning by billing period is the single biggest performance and cost lever for any team querying CUR regularly.
- Current-month data is provisional until the billing period closes, and even closed periods can be retroactively adjusted.
- Access to raw CUR data should be scoped carefully — it’s sensitive financial and operational information deserving the same protection as other business-critical data.