如何从 Azure subscriptionID 获取 OfferDurableId 等参数?
结论
不能只凭 subscriptionID,通过某个通用的 Azure API 稳定获取 OfferDurableId、Currency、Locale 和 RegionInfo。
这些参数并不都属于订阅资源:
OfferDurableId取决于订阅采用的商业报价或计费协议。Currency通常由计费账户、计费配置文件或协议决定。RegionInfo表示计费市场,不是资源部署区域。Locale用于控制 RateCard 返回内容的本地化格式,通常由调用方指定。
订阅查询 API 可以返回 quotaId、locationPlacementId 等策略信息,但这些字段不一定对应 RateCard 所需的 OfferDurableId 和 RegionInfo,不能直接转换后用于 RateCard 查询。
可以先查询订阅元数据
使用 Azure Resource Manager 的 Subscriptions - Get API:
GET https://management.azure.com/subscriptions/{subscriptionId}?api-version=2022-12-01
Authorization: Bearer {accessToken}
典型响应可能包含:
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000",
"subscriptionId": "00000000-0000-0000-0000-000000000000",
"displayName": "Production",
"state": "Enabled",
"subscriptionPolicies": {
"locationPlacementId": "Public_2014-09-01",
"quotaId": "PayAsYouGo_2014-09-01",
"spendingLimit": "Off"
}
}
这些字段有助于识别订阅类型,但有两个限制:
quotaId不一定是MS-AZR-xxxxP格式的OfferDurableId。locationPlacementId不是 RateCard 要求的计费国家或地区代码。
因此,这个 API 只能辅助判断订阅性质,无法补齐全部 RateCard 参数。
各参数应该从哪里获得
OfferDurableId
OfferDurableId 是报价的持久标识,例如:
MS-AZR-0003P
它由订阅的购买渠道和计费协议决定。对于部分传统 Pay-As-You-Go、EA 或开发测试订阅,可以根据已知报价建立映射。但 Azure 没有提供适用于所有订阅类型的公共 ARM API,无法将任意 subscriptionID 直接转换为 OfferDurableId。
不要假设:
subscriptionPolicies.quotaId == OfferDurableId
两者即使在业务上有关联,格式和含义也可能不同。
Currency
Currency 应从订阅对应的计费账户、计费配置文件或协议中获取,而不是从订阅资源中读取。
如果订阅属于 Microsoft Customer Agreement,可以通过 Microsoft Billing API 查询:
- 订阅对应的计费账户。
- 对应的计费配置文件。
- 计费配置文件中的币种和账单地址。
调用这些 API 通常需要计费范围权限,普通的订阅 Reader 或 Contributor 权限不一定够用。
EA、MCA、Microsoft Partner Agreement 和旧式在线订阅使用的计费 API、资源路径及权限模型各不相同,因此无法用同一套查询逻辑覆盖所有订阅。
Locale
Locale 通常由应用自行指定,例如:
en-US
zh-CN
它主要影响服务名称、说明等内容的本地化格式,不是必须从订阅中读取的固有属性。服务端支持哪些区域设置,应以 RateCard API 实际支持的值为准。
如果系统不需要本地化,可以将它设为应用配置,例如统一使用:
en-US
RegionInfo
RegionInfo 表示订阅报价所属的计费市场,通常采用国家或地区代码,例如:
US
CN
GB
它不是以下任何一种信息:
- Azure Resource Manager 的资源位置;
- 资源组的
location; subscriptionPolicies.locationPlacementId;- 任意资源所在的数据中心区域。
对于 MCA 订阅,可以从计费配置文件的账单地址中获取国家或地区信息,再按照 RateCard 的要求进行映射。其他协议则需要从相应的计费账户或合同资料中获取。
C# 查询订阅元数据示例
下面的代码只能读取订阅策略信息,不能保证获得完整的 RateCard 参数:
using System.Net.Http.Headers;
using System.Text.Json;
public sealed record SubscriptionHints(
string? QuotaId,
string? LocationPlacementId,
string? SpendingLimit);
public static async Task<SubscriptionHints> GetSubscriptionHintsAsync(
HttpClient httpClient,
string subscriptionId,
string accessToken)
{
var url =
$"https://management.azure.com/subscriptions/{subscriptionId}" +
"?api-version=2022-12-01";
using var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
using var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync();
using var document = await JsonDocument.ParseAsync(stream);
JsonElement policies;
if (!document.RootElement.TryGetProperty(
"subscriptionPolicies",
out policies))
{
return new SubscriptionHints(null, null, null);
}
return new SubscriptionHints(
GetString(policies, "quotaId"),
GetString(policies, "locationPlacementId"),
GetString(policies, "spendingLimit"));
}
private static string? GetString(JsonElement element, string propertyName)
{
return element.TryGetProperty(propertyName, out var property)
? property.GetString()
: null;
}
即使查询结果中有 quotaId,也只能把它当作识别订阅的线索,不能直接用作 OfferDurableId。
组装 RateCard 请求
应用从可信的计费配置中取得这些值后,可以按下面的方式构造请求:
using System.Globalization;
static string BuildRateCardUrl(
string subscriptionId,
string offerDurableId,
string currency,
string locale,
string regionInfo)
{
static string EscapeOData(string value) =>
value.Replace("'", "''", StringComparison.Ordinal);
var filter = string.Format(
CultureInfo.InvariantCulture,
"OfferDurableId eq '{0}' and Currency eq '{1}' " +
"and Locale eq '{2}' and RegionInfo eq '{3}'",
EscapeOData(offerDurableId),
EscapeOData(currency),
EscapeOData(locale),
EscapeOData(regionInfo));
return
$"https://management.azure.com/subscriptions/{subscriptionId}" +
"/providers/Microsoft.Commerce/RateCard" +
"?api-version=2015-06-01-preview" +
$"&$filter={Uri.EscapeDataString(filter)}";
}
Currency 也是字符串,应写成:
Currency eq 'USD'
不能写成:
Currency eq USD
还需要对整个 OData $filter 进行 URL 编码,以免空格、引号或特殊字符导致请求解析失败。
更稳妥的实现方案
如果必须继续使用旧版 RateCard API,可以按以下流程处理:
- 调用
Subscriptions - Get,获取订阅基本信息和策略字段。 - 根据租户中的计费关系,识别订阅属于 EA、MCA、MPA 还是旧式在线订阅。
- 使用对应的 Microsoft Billing API 查询计费账户或计费配置文件。
- 从计费资料中获取币种以及计费国家或地区。
- 将
Locale设为应用级配置。 - 如果无法通过公共 API 确定
OfferDurableId,则在首次接入时由计费管理员确认,并按订阅或计费范围缓存。 - 不要只根据
quotaId或订阅显示名称猜测报价。
如果需要查询实际协商价格,而不是继续维护旧 RateCard 集成,通常更适合使用对应协议下的 Price Sheet API。Price Sheet 直接面向计费账户或计费范围,能够反映合同价格,也不依赖 OfferDurableId、Locale 和 RegionInfo 这组旧式筛选参数。
如果只需查询公开零售价,可以使用 Azure Retail Prices API。它不要求 subscriptionID,但返回的是公开零售价,不包含该订阅的折扣、企业协议价格或合作伙伴价格。
注意事项
- 订阅管理权限和计费数据权限属于两套权限体系。
- 拥有订阅
Owner权限,不等于可以读取计费账户或价格表。 - EA、MCA、MPA、CSP 和旧式订阅的 API 路径与数据模型可能不同。
- 不能用订阅的资源区域推断计费市场。
- 同一个计费账户下可能有多个计费配置文件,各自使用不同的币种或账单地址。
- RateCard 属于较早的 Commerce API。新系统应优先评估 Price Sheet 或 Cost Management API。
- API 版本可能因计费协议和 Azure 云环境而异。实际落地时,应使用目标环境当前支持的稳定版本。
备注:内容仅供参考。
