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.

DecisionRecommendationWhy
Cost metricAmortised for run rate, actual for the invoiceA one-off purchase is a spike in actual cost and spread out in amortised cost
Comparison windowsThe same number of completed days before and after the increaseA 31-day month costs more than a 30-day month with nothing changed
ScopeThe same accounts, subscriptions and regions in both windowsA 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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 dataLikely causeEvidence that confirms it
Quantity up, cost per unit unchangedMore load or more resources of the same kindApplication metrics, scaling events
New rows with no cost in the before windowNew resource, Region or serviceCreation event in the activity log
Same quantity, Usage up, SavingsPlanCoveredUsage or DiscountedUsage downCommitment expired or no longer covers the usageSavings Plan or reservation end date
Same quantity, PricingModel moves from Reservation to OnDemandAzure reservation expired or its scope changedReservation details and transactions
Same quantity, higher cost per unit, same pricing modelPrice or tier change, different instance type or RegionPrice list, instance type change
Data transfer rows up, no resource IDChanged network path or more outbound trafficFlow logs, architecture change
Storage growing steadily over weeksMissing retention or lifecycle ruleBucket or account metrics, lifecycle rules
One-off spike, fee or Purchase lineA purchase, not new consumptionPurchase 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:

  1. Accept. The increase is expected growth. Update the forecast and budget.
  2. Fix. The increase is avoidable. Create an action with a date and rerun the same query on the next window to confirm costs fall.
  3. 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.