如何通过 REST API 获取 Azure 单个资源的当前支出?
结论
Azure 没有提供按单个资源实时查询当前支出的计费接口。资源费用需要经过用量采集、计价和入账,无法像 Azure Monitor 指标那样实时更新。
可以调用 Azure Cost Management Query API,在订阅或资源组范围内查询成本,再用 ResourceId 筛选指定资源。查询结果是该资源本月至今已经入账的累计费用,也就是最新可用成本,并非实时金额。
为什么不能实时获取
Azure 资源产生用量后,费用通常要经过以下处理:
- 资源服务上报用量。
- Azure 根据计费规则、合同价格、折扣和预留实例等信息计算费用。
- 成本数据进入 Cost Management。
- Query API、Cost Details 或成本导出功能才能查询这些数据。
各项服务上报用量的频率不同,成本数据可能延迟数小时,个别费用的延迟时间还会更长。即使接口返回了当月累计值,最近几分钟或几小时产生的用量也可能尚未计入。
有些费用也无法准确归属到单个资源 ID。例如,以下费用可能没有对应的 ResourceId,也可能记录在其他关联资源上:
- 带宽、共享网络或跨区域流量费用
- Reservation 或 Savings Plan 相关费用
- Marketplace 项目
- 支持计划、税费和账单级调整
- 某些经典资源或共享平台服务
- 由多个资源共同产生的费用
因此,按资源汇总的金额不一定与订阅总费用完全对应。
使用 Cost Management Query API
一般可以在订阅范围发起查询,并使用完整资源 ID 过滤结果:
POST https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.CostManagement/query?api-version=2023-03-01
Authorization: Bearer {accessToken}
Content-Type: application/json
请求体示例:
{
"type": "ActualCost",
"timeframe": "MonthToDate",
"dataset": {
"granularity": "None",
"aggregation": {
"totalCost": {
"name": "PreTaxCost",
"function": "Sum"
}
},
"filter": {
"dimensions": {
"name": "ResourceId",
"operator": "In",
"values": [
"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-rg/providers/Microsoft.Compute/virtualMachines/example-vm"
]
}
}
}
}
各字段的含义如下:
type: "ActualCost"查询实际成本。timeframe: "MonthToDate"查询当前自然月至今。PreTaxCost表示税前成本。ResourceId必须填写完整的 Azure Resource Manager 资源 ID。granularity: "None"只返回累计值,不按天拆分。
目标云环境是否支持该 api-version,需要以对应环境的 Azure REST API 文档为准。
也可以使用 curl 调用:
curl --request POST \
"https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/providers/Microsoft.CostManagement/query?api-version=2023-03-01" \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"type": "ActualCost",
"timeframe": "MonthToDate",
"dataset": {
"granularity": "None",
"aggregation": {
"totalCost": {
"name": "PreTaxCost",
"function": "Sum"
}
},
"filter": {
"dimensions": {
"name": "ResourceId",
"operator": "In",
"values": [
"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-rg/providers/Microsoft.Compute/virtualMachines/example-vm"
]
}
}
}
}'
返回结果会将列定义与行数据分开,结构类似:
{
"properties": {
"columns": [
{
"name": "PreTaxCost",
"type": "Number"
},
{
"name": "Currency",
"type": "String"
}
],
"rows": [
[
12.34,
"USD"
]
]
}
}
其中,12.34 USD 是该资源在查询时间范围内已经进入 Cost Management 的累计税前成本。
Node.js 示例
以下示例使用 @azure/identity 获取 Azure Resource Manager 访问令牌:
import { DefaultAzureCredential } from "@azure/identity";
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const resourceId =
"/subscriptions/00000000-0000-0000-0000-000000000000" +
"/resourceGroups/example-rg" +
"/providers/Microsoft.Compute/virtualMachines/example-vm";
const credential = new DefaultAzureCredential();
const token = await credential.getToken(
"https://management.azure.com/.default"
);
const endpoint =
`https://management.azure.com/subscriptions/${subscriptionId}` +
"/providers/Microsoft.CostManagement/query" +
"?api-version=2023-03-01";
const response = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${token.token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
type: "ActualCost",
timeframe: "MonthToDate",
dataset: {
granularity: "None",
aggregation: {
totalCost: {
name: "PreTaxCost",
function: "Sum"
}
},
filter: {
dimensions: {
name: "ResourceId",
operator: "In",
values: [resourceId]
}
}
}
})
});
if (!response.ok) {
throw new Error(
`Cost query failed: ${response.status} ${await response.text()}`
);
}
const result = await response.json();
const columns = result.properties?.columns ?? [];
const rows = result.properties?.rows ?? [];
if (rows.length === 0) {
console.log("当前时间范围内没有可用的成本数据");
} else {
const record = Object.fromEntries(
columns.map((column, index) => [
column.name,
rows[0][index]
])
);
console.log(
`已入账成本:${record.PreTaxCost} ${record.Currency ?? ""}`
);
}
不要假定返回列的顺序固定。解析 rows 时,应根据 properties.columns 中的列名找到对应的值。
计费周期不是自然月时
MonthToDate 指自然月至今,不一定与合同账单周期一致。如果需要严格按账单周期统计,可以将 timeframe 改为 Custom,并提供起止时间:
{
"type": "ActualCost",
"timeframe": "Custom",
"timePeriod": {
"from": "2026-09-01T00:00:00Z",
"to": "2026-09-13T23:59:59Z"
},
"dataset": {
"granularity": "None",
"aggregation": {
"totalCost": {
"name": "PreTaxCost",
"function": "Sum"
}
},
"filter": {
"dimensions": {
"name": "ResourceId",
"operator": "In",
"values": [
"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-rg/providers/Microsoft.Compute/virtualMachines/example-vm"
]
}
}
}
}
日期边界需要根据实际账单周期和时区确定,尤其要留意 UTC 与本地时间的差异。
权限和结果解释
调用接口的身份必须有权读取对应范围内的成本数据。例如,在订阅范围查询时,需要授予合适的 Cost Management 读取权限。只有资源本身的读取权限,不一定能读取订阅级成本。
使用查询结果时,还要注意以下情况:
- 新建资源暂时查不到费用,通常说明成本还没有入账,不一定表示费用为零。
- 已删除的资源仍可能在后续查询中出现延迟入账的费用。
ActualCost与AmortizedCost的计算口径不同。涉及 Reservation 或 Savings Plan 时,需要根据业务用途选择。- 查询结果使用账单或成本管理数据中的货币,不能直接假定为美元。
- 标签无法保证资源唯一性。如果能取得完整资源 ID,应优先使用
ResourceId。 - 某些子资源没有独立的计费记录,费用可能计入父资源或其他计量项。
如果业务需要按分钟展示费用,只能结合 Azure Monitor 用量指标和相应价格自行估算。这类结果可以用于预算提醒或趋势展示,但不能替代 Azure Cost Management 提供的正式成本数据。
备注:内容仅供参考。