A cost increase in AWS or Azure is explained only when you have narrowed it to a resource or a rate, separated volume from price, and tied the change to an event you can show. Starting from the total and looking for a deployment that fits nearly always finds one, and often the wrong one. This guide gives a sequence you can run today in AWS Cost Explorer, CUR 2.0, Azure Cost Management and Azure exports: rule out data artefacts, narrow, classify, prove, then decide.
Fix three things before the first query
Write these down before comparing numbers. Without them, two people end up arguing about two different increases.
| Decision | Recommendation | Why |
|---|---|---|
| Cost metric | Amortised for run rate, actual for the invoice | A one-off purchase is a spike in actual cost and spread out in amortised cost |
| Comparison windows | The same number of completed days before and after the increase | A 31-day month costs more than a 30-day month with nothing changed |
| Scope | The same accounts, subscriptions and regions in both windows | A newly connected account looks like growth |
The glossary entry for amortised cost explains the metric.
Step 1: Is the increase real?
Some increases exist only in the presentation. Check these before you ask engineering anything:
- Open month. Cost Explorer shows usage up to the previous day, and data for the current billing period can arrive later than 24 hours. Azure refreshes estimated charges for the open period six times a day, usually 8 to 24 hours behind for EA and MCA, and typically closes the period up to 72 hours after it ends. Charges can be rerated until the invoice.
- Tiered prices in Azure. Until an invoice exists, Azure Cost Management estimates tiered products at the highest tier. An open-month increase may shrink once the invoice is issued.
- Fees on the first of the month. AWS adds the recurring fee for No Upfront and Partial Upfront Savings Plans on the first day of each billing period. In the daily view, Cost Explorer shows the unused portion of commitment fees on the first of the month or on the purchase date. A spike on the 1st is a metric question first.
- Credits and tax. CUR 2.0 gives credits, refunds and tax their own line item types (
Credit,Refund,Tax). Azure Cost Management includes neither credits nor taxes. If the invoice rises while cost analysis does not, one possible reason is a credit that has run out. - Currency. Cost Explorer shows costs in US dollars only. If you are invoiced in euros, or report in sterling, the amount can move with no change in usage. Keep FX as its own line rather than letting it hide inside a service.
If an increase remains after these checks, in the same scope, on the same metric and over equal windows, it is real.
Step 2: Narrow one dimension at a time
Move from coarse to fine and only go down a level once one row explains most of the difference: account or subscription, then service, then usage type or meter, then region, then resource, then tag.
In AWS. Cost Explorer has Compare in the Report parameters panel. It analyses the change between two selected months and highlights the largest drivers across services, accounts and Regions, including usage and discount changes. The Top trends widget on the Billing and Cost Management home page lists the ten largest variations between the previous two months. For a mid-month jump, switch Cost Explorer to daily granularity and group by service, then by usage type.
In Azure. Open cost analysis for your scope, set granularity to Daily, filter to the service in question and group by Resource. Microsoft documents exactly this path for unexpected virtual machine charges. The cost-by-resource view exists only at subscription and resource group scope.
Worked query for CUR 2.0 in Athena
The console shows the increase but not always whether volume or price moved. This query compares two equal windows per account, service, usage type and line item type. Replace the table name, partitions and timestamps with your own.
WITH base AS (
SELECT line_item_usage_account_id AS account_id,
line_item_product_code AS product,
line_item_usage_type AS usage_type,
line_item_line_item_type AS line_type,
line_item_usage_amount AS qty,
line_item_unblended_cost AS cost,
CASE
WHEN line_item_usage_start_date >= TIMESTAMP '{before_start}'
AND line_item_usage_start_date < TIMESTAMP '{before_end}' THEN 'before'
WHEN line_item_usage_start_date >= TIMESTAMP '{after_start}'
AND line_item_usage_start_date < TIMESTAMP '{after_end}' THEN 'after'
END AS win
FROM cur2
WHERE billing_period IN ('{period_1}', '{period_2}')
)
SELECT account_id, product, usage_type, line_type,
SUM(IF(win = 'before', qty, 0)) AS qty_before,
SUM(IF(win = 'after', qty, 0)) AS qty_after,
SUM(IF(win = 'before', cost, 0)) AS cost_before,
SUM(IF(win = 'after', cost, 0)) AS cost_after,
SUM(IF(win = 'after', cost, 0))
- SUM(IF(win = 'before', cost, 0)) AS delta
FROM base
WHERE win IS NOT NULL
GROUP BY 1, 2, 3, 4
ORDER BY delta DESC
LIMIT 25;
Grouping by line_item_line_item_type does the real work: it separates usage at On-Demand rates (Usage), usage covered by a Savings Plan (SavingsPlanCoveredUsage) and Reserved Instance usage (DiscountedUsage). Once you have the usage type, rerun the query with line_item_resource_id in place of the line type. That column exists only if the export was created with the INCLUDE_RESOURCES table configuration, and it stays blank for data transfer, API requests, discounts, credits and tax.
Worked query for an Azure cost export
For Azure, create a Cost and usage details (amortized) export as CSV or Parquet into a storage account and query it with whatever engine you already use, for example a SQL engine that reads Parquet. The column names apply to EA and MCA; casing can differ by agreement type. If Date arrives as text, convert it to a date first.
SELECT SubscriptionName, ResourceGroup, MeterCategory, MeterName,
PricingModel, ChargeType,
SUM(CASE WHEN Date BETWEEN '{before_start}' AND '{before_end}' THEN Quantity ELSE 0 END) AS qty_before,
SUM(CASE WHEN Date BETWEEN '{after_start}' AND '{after_end}' THEN Quantity ELSE 0 END) AS qty_after,
SUM(CASE WHEN Date BETWEEN '{before_start}' AND '{before_end}' THEN CostInBillingCurrency ELSE 0 END) AS cost_before,
SUM(CASE WHEN Date BETWEEN '{after_start}' AND '{after_end}' THEN CostInBillingCurrency ELSE 0 END) AS cost_after
FROM azure_amortized
GROUP BY SubscriptionName, ResourceGroup, MeterCategory, MeterName, PricingModel, ChargeType
ORDER BY cost_after - cost_before DESC;
Because MeterName is in the grouping, quantities on each row share a unit. PricingModel distinguishes OnDemand, Reservation, Spot and SavingsPlan. In actual rather than amortised cost, a VM receiving a reservation benefit would show zero cost, and the purchase would sit as Purchase in a single month.
Step 3: Classify the pattern
You now have a row with quantity and cost before and after. Match it to a pattern. The table is a working hypothesis, not a diagnosis: it names the evidence that would confirm the suspicion.
| Pattern in the data | Likely cause | Evidence that confirms it |
|---|---|---|
| Quantity up, cost per unit unchanged | More load or more resources of the same kind | Application metrics, scaling events |
| New rows with no cost in the before window | New resource, Region or service | Creation event in the activity log |
Same quantity, Usage up, SavingsPlanCoveredUsage or DiscountedUsage down | Commitment expired or no longer covers the usage | Savings Plan or reservation end date |
Same quantity, PricingModel moves from Reservation to OnDemand | Azure reservation expired or its scope changed | Reservation details and transactions |
| Same quantity, higher cost per unit, same pricing model | Price or tier change, different instance type or Region | Price list, instance type change |
| Data transfer rows up, no resource ID | Changed network path or more outbound traffic | Flow logs, architecture change |
| Storage growing steadily over weeks | Missing retention or lifecycle rule | Bucket or account metrics, lifecycle rules |
One-off spike, fee or Purchase line | A purchase, not new consumption | Purchase record, amortised view |
Mixed patterns are common. Split volume and price effects per row, as the variance walk in the monthly cloud cost review describes.
Step 4: Prove the cause with an event
Coincidence in time is not evidence. Find the event that changed exactly the resource or rate you isolated.
- AWS CloudTrail Event history shows the last 90 days of management events per Region at no charge: resources created, modified and deleted. A search covers one account and one Region and allows a single attribute filter plus the time range. Data events are not included.
- The Azure activity log keeps control-plane events such as creating a VM for 90 days at no charge. Microsoft notes it is the only place that records who created a resource. It does not typically capture read operations.
That sets a hard limit. If the increase started more than 90 days ago and you have neither a CloudTrail trail nor a diagnostic setting for the activity log, the provider data may no longer prove the cause. Investigate promptly, and set up durable logging before you need it.
Add your own sources: deployment history, infrastructure-as-code changes, feature flags and traffic metrics. A confirmed driver has a query and an event; a probable one has only one of the two.
Common mistakes
- Part month against full month. Month to date is not comparable with the whole previous month. Compare equal windows or daily averages.
- Switched metric. Half the team looks at actual cost, the other half at amortised. The gap is often larger than the increase.
- Aggregation hides movement. A flat total can contain a fast-growing service offset by decreases elsewhere. Sort by difference, not by total.
- Monthly comparison for a daily jump. Cost Explorer's Compare works on months. A jump on the 17th needs daily granularity.
- Shared resources. A shared cluster or NAT gateway shows the increase but not who caused it. The evidence then lives in application or network metrics, not in billing data.
Record the decision
Every investigation ends in one of three decisions with an owner:
- Accept. The increase is expected growth. Update the forecast and budget.
- Fix. The increase is avoidable. Create an action with a date and rerun the same query on the next window to confirm costs fall.
- Keep investigating. The cause is probable but unproven. Narrow the scope, set a date and label the amount unexplained.
Record the query, the evidence and the decision in your monthly review log. A second look a month later shows whether the explanation holds.
What Costfluent covers
Costfluent reads AWS, Azure and Google Cloud billing data through read-only connectors, and Kubernetes cost through an agent in the cluster. In cost reports you group by provider, account, service, resource, Region, tag or segment, filter to the comparison window, save the view for the next investigation and download it as CSV. Cost insights highlight significant changes and growing services, state the comparison period and lead into the cost rows behind them. They show where to look and do not claim a root cause the product has not established. Following a change to its dimensions and evidence, with the explanation recorded, is change investigation, which is Planned. See cost reports, and the glossary for what a cost insight is. Which AWS export to create for the queries is covered in CUR 2.0 or FOCUS.
Sources and method
Checked on 10 October 2026: AWS: Comparing your costs between time periods, AWS: Exploring your data using Cost Explorer, AWS CUR 2.0: Line item columns, AWS CUR 2.0: Table configurations, AWS CloudTrail: Working with event history, Microsoft: Understand Cost Management data, Microsoft: Common cost analysis uses, Microsoft: Understand cost details fields, Microsoft: Create and manage Cost Management exports and Microsoft: Activity log in Azure Monitor. The sequence, queries and pattern table are Costfluent recommendations.



