To export cost by service and resource group in Azure, query Cost Management for each subscription with the cost grouped by ServiceName and ResourceGroup, and save the rows. The figures are estimates until the invoice is issued, and new usage usually takes 8 to 24 hours to appear, or up to 72 hours for pay-as-you-go subscriptions.
| Method | Best fit |
|---|---|
| PowerShell | A CSV of this month's cost by service and resource group on 1 row, across your enabled subscriptions. |
| Admin centre | A chart and table of 1 subscription's cost, grouped 1 way at a time. |
| Altivane | Recommended for monthly cost reviews, with this month and last month side by side in a workbook. |
Before you start
Use an account with the Reader or Cost Management Reader role on each subscription: both can read Cost analysis and the Cost Management query API. For a subscription bought through a Cloud Solution Provider, the partner must turn on the cost visibility policy before the subscription's users can see costs. A new subscription can take up to 48 hours before Cost Management works.
Know what the figures cover. Costs are estimated until an invoice is generated, don't include credits, and can be rerated until the invoice closes; support costs aren't included. Azure limits how often the query can be called and answers 429 Too Many Requests when it is called too fast. The example sends a query that only reads, exports a CSV and changes nothing. The script's finally block disconnects the account and clears its saved Azure contexts.
Option 1
Export cost by service and resource group with PowerShell
Install the module once. The script asks Cost Management for each enabled subscription's actual cost this month so far, grouped by service and resource group, and saves 1 row for each pair.
Install-Module -Name Az.Accounts -Repository PSGallery -Scope CurrentUser$ErrorActionPreference = 'Stop'
$body = @{
type = 'ActualCost'
timeframe = 'MonthToDate'
dataset = @{
granularity = 'None'
aggregation = @{ totalCost = @{ name = 'PreTaxCost'; function = 'Sum' } }
grouping = @(
@{ type = 'Dimension'; name = 'ServiceName' }
@{ type = 'Dimension'; name = 'ResourceGroup' }
)
}
} | ConvertTo-Json -Depth 5
Connect-AzAccount -ErrorAction Stop
try {
$subscriptions = @(Get-AzSubscription | Where-Object State -eq 'Enabled')
$rows = [System.Collections.Generic.List[object]]::new()
foreach ($sub in $subscriptions) {
$path = "/subscriptions/$($sub.Id)/providers/Microsoft.CostManagement/query" +
'?api-version=2025-03-01'
$answer = Invoke-AzRestMethod -Path $path -Method POST -Payload $body
if ($answer.StatusCode -ne 200) {
Write-Warning "$($sub.Name) not read: HTTP $($answer.StatusCode)."
continue
}
$result = ($answer.Content | ConvertFrom-Json).properties
if ($result.nextLink) { Write-Warning "$($sub.Name) has more rows than 1 page." }
$names = @($result.columns | ForEach-Object name)
foreach ($row in $result.rows) {
$cell = @{}
for ($i = 0; $i -lt $names.Count; $i++) { $cell[$names[$i]] = $row[$i] }
$rows.Add([pscustomobject]@{
Subscription = $sub.Name
Service = $cell['ServiceName']
ResourceGroup = $cell['ResourceGroup']
Cost = [math]::Round([double]$cell['PreTaxCost'], 2)
Currency = $cell['Currency']
})
}
}
if ($rows.Count -eq 0) {
Write-Output 'No cost was returned for this month.'
} else {
$rows | Sort-Object Subscription, @{ Expression = 'Cost'; Descending = $true } |
Export-Csv -Path './azure-cost.csv' -NoTypeInformation -Encoding utf8
Write-Output "$($rows.Count) rows saved to azure-cost.csv."
}
} finally {
Disconnect-AzAccount | Out-Null
}The CSV holds the subscription, service, resource group, cost and currency, the largest cost first in each subscription. Each subscription is in its own billing currency, so don't add currencies together. For last month, set timeframe to Custom and add a timePeriod with from and to set to its first and last day; some kinds of subscription refuse TheLastMonth. For 1 split only, remove 1 of the 2 groupings. The script reads the columns from Azure's answer rather than assuming their order, and names any subscription it couldn't read or whose answer ran over 1 page, so a gap never looks like zero cost.
Option 2
Group costs in Cost analysis in the Azure portal
- Sign in to the Azure portal with an account that can read the subscription's costs.
- Choose Cost Management + Billing > Cost Management > Cost analysis, then use the Scope pill to choose the subscription.
- Choose Group by > Service name, then switch to the table view and set the granularity to None, so every service is listed rather than the top 10 and Others.
- Choose Download and pick Excel or CSV for a snapshot of the table.
- Choose Group by > Resource group name and download again for the split by resource group.
The download is a snapshot and isn't updated. Cost analysis groups by 1 property at a time in these views, so for service and resource group on 1 row, or several subscriptions in 1 file, use PowerShell or Altivane.
Option 3 ยท Recommended for monthly cost reviews
Export cost by service and resource group with Altivane
- Open Altivane and choose the tenant.
- Run the Azure Cost by Service report, and Azure Cost by Resource Group for the same figures by resource group. Each can take a few minutes.
- Open the Excel workbook and find Azure cost by service: each service's cost this month so far and last month, the change and the currency, for each subscription.
- Read Totals by subscription for each subscription's total for both months.
The reports cover every subscription where you gave Altivane the Reader role, with the change from last month worked out for you, in each subscription's billing currency. A subscription Azure doesn't answer for is left out, named on the workbook's Not read sheet, and the report is marked partial, rather than counted as costing nothing. The reports give the service and resource group splits as 2 tables, where the PowerShell example puts both on 1 row.
Contact usCommon questions
Why don't the figures match the invoice?
Until the invoice is issued, Cost Management shows estimates that leave out credits and can change as charges are rerated. Support costs aren't included. Compare a closed month with its invoice instead of the current one.
Why did the query fail with error 429?
Azure throttled the request because too many cost queries were sent in a short time. Wait for the time Azure gives in its reply, then run the script again, or query fewer subscriptions in each run.
How do I find subscriptions with no budget?
A budget alerts you when costs pass a threshold you set. See how to find subscriptions without a budget in Azure.
Microsoft references
Technical references checked on 6 November 2026. The example scripts have been reviewed and syntax checked; they have not been run against a live tenant.
- Install-Module cmdlet reference
- Connect-AzAccount cmdlet reference
- Invoke-AzRestMethod cmdlet reference
- Disconnect-AzAccount cmdlet reference
- Cost Management Query - Usage REST reference
- Group and filter options in Cost analysis and budgets
- Understand Cost Management data
- Understand and work with scopes
- Customize views in Cost analysis
- Save and share customized views
- Cost Management + Billing frequently asked questions