
做云成本优化的朋友应该都有过这种体验登录门户点几下鼠标买一台或几台预留实例Reserved Instance简称 RI看起来挺简单。可一旦遇到月度采购上百台、多个订阅统一规划、要在交付流程里自动下单这类场景门户那套点选操作就不够用了。标题问到的“Azure China 如何使用 API 方式购买 RI”恰恰是很多企业内部成本管理平台和自动化运维团队卡住的地方。这里先破个题Azure 资源管理 API 的认证走的是Azure AD 令牌机制不是其他厂商那种“填一个 API Key 然后放到 Header 里”的玩法。上来就问“API Key 填在哪”的基本都是把不同云厂商的 API 风格混在一起了。正确思路是先拿服务主体或用户凭证换一个Bearer Token再拿这个 Token 调用中国区的管理端点。这篇文章我会从环境准备、权限梳理、Token 获取、购买请求构造、订单验证一路讲到底顺便把容易踩的 401、403、400 这些问题一并拆开讲。1. 预留实例购买 API 的基本逻辑1.1 RI 到底解决什么问题预留实例本质上是一份“预付协议”用 1 年或 3 年的预付承诺换来该 SKU 在中国区对应计费周期内的折扣价。对多数企业来说RI 的价值主要体现在三块长期运行的虚拟机成本明显下降尤其是 3 年期双倍折扣账单抵扣是区域性的可以覆盖到同一范围内的所有匹配实例不要求你重新搭建环境预算上从“按月浮动”变成“年初可预估”财务和审批流程更好走。但这里有个容易被忽略的细节在中国区RI 的运营和计费规则跟国际版并不是完全一致的。国际版有些 SKU 在中国区未必上架某些促销折扣也不通用中国区由本地运营方负责合规和计费定价货币是人民币发票、合同、续订流程都走本地流程。因此你从国际版文档里抄一段 API 来跑很可能在 SKU 名称和计费范围上先踩一脚。1.2 API 购买和门户购买怎么选门户购买适合一次性、数量少、临时验证的场景。它的优点是有图形界面点选区域、尺寸、期限的时候不容易写错。缺点也很明显没法批量处理没法嵌进自动化流水线数据不好追溯而且中国区门户上的入口时有时无部分新界面切换后反而找不到预订入口。API 购买的优势在于可以做到“可重复、可脚本化、可审计”。举个例子公司在每月预算冻结后需要给 50 个订阅各买若干台固定规格的 RI。用门户人工操作的话至少要花半天用 API 脚本配合循环几分钟就能完成而且每一步都有请求和返回记录出问题也好对账。对比项门户购买API 购买适合场景临时单次、数量少批量采购、自动化集成可批量操作弱需要人力点击强脚本循环即可权限控制门户授权RBAC 服务主体可审计性操作日志较难导出请求/响应完整记录出错排查界面报错较直观需要理解 HTTP 状态码实际项目中我个人的习惯是小规模验证时用门户一旦确定了 SKU、范围、期限这些参数就马上把购买流程固化到脚本里。毕竟 RI 采购往往不是一次性的动静到期续订和季度优化才是常态。2. 动手前先备好环境和权限2.1 你的订阅到底能不能买预留不是所有订阅都自动支持 RI 购买。中国区最常见的支持类型是企业协议EA订阅、按需付费订阅以及部分 CSP 订阅。如果你登录的是试用订阅或者某些受限的特定订阅类型即使 API 请求构造完全正确也有可能收到 400 或者 403 的报错。一个稳妥的检查方式是在门户里打开“预留”页面看当前订阅下能不能刷出可购买的 SKU。如果门户都搜不到API 自然也别指望能成功。另一个办法是直接用 API 查 catalog这一步在后面的 3.2 小节会详细讲。还需要注意如果订阅是 EA 下的子订阅虽然子订阅的管理员可以发起购买但最终扣费走的是 EA 登记的费用。某些企业会在 EA 层面设置“仅允许企业管理员购买”的开关这种情况下即便你有子订阅的权限请求还会收到策略拦截或审批提示。最好在采购前先让 EA 管理员确认策略。2.2 创建服务主体并分配购买权限推荐用独立服务主体Service Principal来调用购买 API而不是把某个人账号的密码写在脚本里。创建一个专用服务主体给它最小权限即使凭证泄露也能快速吊销不影响实际用户的账号安全。用 Azure CLI 创建服务主体的命令是这样az cloud set --name AzureChinaCloud az login az ad sp create-for-rbac \ --name ri-purchase-automation \ --role Reservation Purchaser \ --scopes /subscriptions/subscription-id这里有一个衔接点--role Reservation Purchaser并不是在所有的环境中都默认存在。如果你创建的时候提示角色不存在可以改用内置角色如“订阅贡献者”或者在门户的自定义角色里把Microsoft.Capacity/reservationOrders/write权限加上。标准的“预留购买者”角色在 Azure 中国区也是有的但不同租户的目录同步状态可能影响可见性所以报错别慌检查角色列表即可。创建完成后会得到appId、password、tenant三个值。注意保存好之后获取 Token 要用。服务主体权限建议只绑定到需要购买 RI 的订阅或资源组上不要一股脑给到整个租户。2.3 找对 Azure 中国区 API Endpoint这是我从新手到大神之间最容易摔的一跤端点写错或者 Token 的 resource 写错。中国区的登录端点和管理端点与国际版完全不一样具体看下表用途国际版端点中国区端点登录认证login.microsoftonline.comlogin.chinacloudapi.cn资源管理 APImanagement.azure.commanagement.chinacloudapi.cn门户地址portal.azure.comportal.azure.cn除了地址不同Token 里的resource参数也必须是https://management.chinacloudapi.cn/。如果你在 login.chinacloudapi.cn 上请求 Token却把 resource 写成 management.azure.com后续调用中国区 API 一定会直接 401 或 400。反过来也一样。另外Azure CLI 和 Azure PowerShell 默认连接国际版。如果你不下命令切换云环境az login登录到的国际版租户和中国区租户是两套完全独立的目录这是很多人迷迷糊糊用上半天才发现的问题。3. 核心购买流程从获取 Token 到提交订单3.1 获取访问令牌确认服务主体创建好后先获取访问令牌。这里用 curl 演示一个最直观的请求curl -X POST https://login.chinacloudapi.cn/tenant-id/oauth2/token \ -d grant_typeclient_credentials \ -d client_idappId \ -d client_secretpassword \ -d resourcehttps://management.chinacloudapi.cn/响应里会包含access_token、expires_in、token_type等字段。access_token默认有效期大约 1 小时自动化脚本里记得处理过期逻辑别把 Token 硬编码进去。比如你可以用 jq 把 Token 抽出来方便下一步调用TOKEN$(curl -s -X POST https://login.chinacloudapi.cn/tenant-id/oauth2/token \ -d grant_typeclient_credentials \ -d client_idappId \ -d client_secretpassword \ -d resourcehttps://management.chinacloudapi.cn/ | jq -r .access_token)这一步是后续所有 API 请求的地基Token 一旦拿不到后面所有操作都会卡在 401。3.2 查询 SKU 目录确定要买什么购买前先确认目标 SKU 在当前订阅、当前区域是否可以购买。这个目录接口非常有用curl -X GET https://management.chinacloudapi.cn/subscriptions/subscription-id/providers/Microsoft.Capacity/catalogs?reservedResourceTypeVirtualMachineslocationchinanorthapi-version2022-11-01 \ -H Authorization: Bearer $TOKEN返回的结果里会列出 SKU 名称、term 类型P1Y/P3Y、费用信息、配额限制等。reservedResourceType根据你要买的东西来变虚拟机是VirtualMachinesSQL 数据库是SqlDatabaseCosmos DB 是CosmosDb。在中国区location参数要填中国区区域名比如chinanorth、chinaeast而不是eastasia这类泛亚区域名。为什么要特意先查目录因为很多报错是在提交订单时才暴露出来的比如 SKU 在该区域不可用、排他范围填错、期限选项不对。提前查一遍目录等于把大部分参数层面的问题提前过滤掉了。我在实际项目里甚至直接写了个小脚本每天把目录拉下来缓存到本地采购时先在缓存里比对再决定是否创建订单。3.3 生成订单 ID 并提交购买请求这是整个流程里最关键的一步。购买预留实例的操作本质上是创建了一个Microsoft.Capacity/reservationOrders资源。这个订单 ID 不是平台给你生成的而是客户端自己生成一个 GUID。这个设计很多人第一次时会忽略随便填一个已有订单的 ID 导致冲突。提交订单的请求如下ORDER_ID$(uuidgen) curl -X PUT https://management.chinacloudapi.cn/subscriptions/subscription-id/providers/Microsoft.Capacity/reservationOrders/$ORDER_ID?api-version2022-11-01 \ -H Authorization: Bearer $TOKEN \ -H Content-Type: application/json \ -d { sku: { name: Standard_D2s_v3 }, location: chinanorth, properties: { displayName: ri-automation-demo, billingScopeId: /subscriptions/subscription-id, term: P1Y, quantity: 1, appliedScopeType: Shared, reservedResourceType: VirtualMachines, instanceFlexibility: On } }我把几个常用参数拆开解释一下参数取值示例说明sku.nameStandard_D2s_v3要预留的虚拟机 SKUlocationchinanorth中国区区域名必须和目录查询一致termP1Y 或 P3Y预留期限1 年或 3 年quantity1实例数量appliedScopeTypeShared / SingleShared 表示共享范围Single 表示指定订阅范围reservedResourceTypeVirtualMachines预留资源类型instanceFlexibilityOn / Off是否允许规格调整后仍享受折扣displayName自定义字符串订单显示名称建议规范化命名如果你要买排他范围内的 RI需要在properties里再加一段appliedScopePropertiesappliedScopeProperties: { subscriptionId: /subscriptions/target-subscription-id }不加这个字段即使用了 Single 范围服务端也可能报参数不完整。请求提交之后返回的响应里一般会直接带上订单状态和预留实例列表。3.4 验证订单状态考虑轮询机制提交订单后不能直接把响应丢掉就完事还要做验证。查询订单状态用curl -X GET https://management.chinacloudapi.cn/subscriptions/subscription-id/providers/Microsoft.Capacity/reservationOrders/$ORDER_ID?api-version2022-11-01 \ -H Authorization: Bearer $TOKEN重点看properties.provisioningState字段。常见取值有Succeeded、Pending、Failed、Cancelled。正常购买一般在几秒到几十秒内完成。由于平台内部存在计费系统同步、范围绑定等操作偶尔会有延迟。自动化脚本里建议加上轮询逻辑比如每 30 秒查一次最多查 10 次超时再告警。for i in {1..10}; do STATE$(curl -s -X GET https://management.chinacloudapi.cn/subscriptions/subscription-id/providers/Microsoft.Capacity/reservationOrders/$ORDER_ID?api-version2022-11-01 \ -H Authorization: Bearer $TOKEN | jq -r .properties.provisioningState) echo order state: $STATE if [ $STATE Succeeded ]; then break fi sleep 30 done轮询只是保证脚本能拿到最终结果不要指望它能改变失败结论。如果状态是 Failed还是得回到请求参数和权限里排查。4. 用 PowerShell 和 Azure CLI 包一层减少手写 REST 的负担4.1 把 Azure CLI 切到中国云环境有人觉得手写 REST 太累有现成的 Azure CLI 和 PowerShell 模块为什么不用确实能用但前提是要先把工具切换到中国云环境。Azure CLIaz cloud set --name AzureChinaCloud az login az account set --subscription subscription-idAzure PowerShellConnect-AzAccount -EnvironmentName AzureChinaCloud切环境这个动作很容易被漏掉。很多人在本机上用着国际版 Azure CLI登录后再调用中国区 API发现请求地址一直打在国际版浪费大半天排查。工具不会自动识别租户对应哪个云必须显式切换。切好之后az rest或Invoke-AzRestMethod这类通用接口也能用了。它们本质上还是对 REST API 的封装但省去了手工拼 Token 的麻烦。4.2 用 Invoke-AzRestMethod 提交购买如果团队更习惯 PowerShell可以用当前登录上下文直接发起 REST 请求$subscriptionId subscription-id $orderId [guid]::NewGuid().ToString() $body { sku { name Standard_D2s_v3 } location chinanorth properties { displayName ri-automation-demo billingScopeId /subscriptions/$subscriptionId term P1Y quantity 1 appliedScopeType Shared reservedResourceType VirtualMachines instanceFlexibility On } } | ConvertTo-Json -Depth 6 Invoke-AzRestMethod -Method Put -Path /subscriptions/$subscriptionId/providers/Microsoft.Capacity/reservationOrders/$orderId?api-version2022-11-01 -Payload $body用Invoke-AzRestMethod的好处是它会把当前 Az 会话的 Token 自动拿过来你不用手动刷新 Token也不用担心服务主体密码过期的问题。这里有一个细节ConvertTo-Json -Depth 6不能省否则嵌套的properties会被截断平台上收到的就是一个缺了参数的请求。4.3 给自动化采购脚本留个口子实际部署时我不建议把购买逻辑和业务逻辑全揉在一个脚本里。可以分成三层参数层维护一个 CSV 或 JSON 文件记录 SKU、区域、数量、期限、范围执行层脚本读取参数文件循环调用购买 API并记录每个订单 ID报表层把订单结果汇总成表格邮件或企业微信通知。这样做的好处是下次采购只需改参数文件不用动代码订单 ID 和结果配对记录方便对账万一某个 SKU 失败也不会影响其他 SKU 执行。下面是一个执行层的简化思路$orders Import-Csv ri-orders.csv foreach ($order in $orders) { $orderId [guid]::NewGuid().ToString() $body { sku { name $order.Sku } location $order.Location properties { displayName $order.DisplayName billingScopeId /subscriptions/$subscriptionId term $order.Term quantity [int]$order.Quantity appliedScopeType $order.Scope reservedResourceType $order.ResourceType } } | ConvertTo-Json -Depth 6 Invoke-AzRestMethod -Method Put -Path .../reservationOrders/$orderId?api-version2022-11-01 -Payload $body }把参数文件和脚本拆开后续维护成本会低很多。中国区客户里很多自动化团队最终都是走这条路线。5. 常见问题与错误排查实录5.1 HTTP 401 认证失败401 是使用 API 时最高频的报错。绝大多数原因不是“没权限”而是 Token 本身没拿到或没带对。常见情况有请求头写成了Authorization: Bearer 空字符串Token 是从国际版资源端点获取的而请求打到了中国区Token 过期脚本没有刷新的逻辑服务主体的client_secret失效或重置。排查思路很简单先把 Token 解码成 JSON 看看里面的aud和exp。如果aud不是https://management.chinacloudapi.cn/那就不是中国区 API 认可的 Token。如果exp已经过了重新获取即可。有一个比较容易迷惑的现象同样的 Token 在Get-AzVM这类 PowerShell 命令里是好的但手工 REST 请求却 401。这是因为 Az 模块在切换云环境后会自动选择对应的管理端点而你手工 REST 请求可能还在指向国际版端点或者你手工把resource参数写成国际版地址。对照 2.3 小节的端点表自查即可。5.2 HTTP 403 有 Token 但权限不够403 说明 Token 能认证但当前身份没有执行该操作的权限。先查服务主体是否已经被分配了购买预留权限。门户上是这样检查的进入订阅的“访问控制 (IAM)”找到服务主体看角色列表里是否有“预留购买者”或“订阅贡献者”。另外即使订阅本身有权限企业协议层也可能有独立策略。某些 EA 会把“购买预留实例”的权限限定在企业管理员范围内子订阅管理员即使登录成功也会被 403。这种问题靠改请求解决不了需要去 EA 后台或联系管理员调整策略。5.3 HTTP 400 参数错误400 多是请求体里的参数不对。常见几个坑term写错。只支持P1Y和P3Y有些文档写12或36肯定失败location写了英文全称或国际版区域名比如East Asia或eastasia中国区要写chinanorth、chinaeastsku.name大小写或格式不对。VM SKU 一般是Standard_D2s_v3这种带下划线的格式重复使用同一个reservationOrderId第二次提交会报订单已存在加了appliedScopeTypeSingle却没带appliedScopeProperties。排查方法就一条把 catalog 接口返回的合法值和你请求里的参数做一一对照。不要靠猜目录接口给出的值才是平台实际认可的值。5.4 订单一直 Pending 或 Failed订单状态如果长时间 Pending先看是不是同一时间提交了太多订单。平台内部对 RI 购买的并发有配额限制量大时可以分批提交中间加一点延迟。Failed 则要细分原因。最常见的是区域库存或 SKU 下架。中国区有些 SKU 上新晚于国际版有些热门规格在特定区域可能短期售罄或不可购买。碰到这种情况要么换区域要么换相近规格。另外如果某个 SKU 在当前订阅里已经购买过一定数量的 RI继续买也不会失败但能看到的折扣会随数量阶梯变化这点不是错误是计费规则。5.5 中国区特有的注意点中国区的 RI 购买还有一些本地化差异。第一金额单位是人民币订单和发票里的币种不是美元财务系统对接时要注意单位换算和汇率字段。第二中国区预留给账单开具、发票索取流程走本地运营方API 只负责资源订单不负责财务开票开票还要去门户或线下流程。第三部分国际版 API 版本在中国区可能滞后用新版本号请求反而报“不支持”可以先查一下该版本在中国区是否可用或者换一个接近的稳定版本。这些点听起来琐碎但对真正上线生产的团队来说反而比接口本身更影响进度。6. 我的实际使用体会与后续扩展6.1 实际踩过的坑我在替客户做中国区成本优化平台时第一次用 API 购买 RI 就掉进了“Token resource 写错”的坑。当时服务主体配好了、订阅权限也给了却在调用 catalog 时一直拿 401。后来才发现是拿 Token 时把 resource 写成了国际版管理端点换成https://management.chinacloudapi.cn/之后一切都通了。第二个比较痛的点是自动化脚本里的幂等问题。RI 购买请求不是天然幂等的万一网络超时后脚本重试但第一次请求其实已经在服务端成功了第二次提交就会因为同一个订单 ID 报冲突。所以脚本里一定要做“先查后建”提交前把订单 ID 记录到本地重试前先 GET 一下看订单是否已存在。第三个经验是关于参数文件管理的。RI 采购涉及 SKU、区域、范围、期限、数量这几个维度一旦量大配置错一个 SKU 名就意味着几千块花错地方。我把所有采购参数做成可评审的 JSON 文件每次采购前先给采购负责人看 diff。这招在合规要求严格的企业里特别受欢迎。6.2 之后还可以怎么扩展API 购买 RI 只是成本自动化的第一步。购买完成后建议接着做三件事定期用 Billing API 拉取使用量和摊销成本核对 RI 的抵扣率是否达到预期监控预留订单的到期日期在到期前 30 天自动生成续订提醒把 RI 的购买记录和内部 CMDB 关联起来确保运维同事清楚哪些资源已经被预留覆盖避免重复采购。我在中国区项目的最终落地形态通常是一张状态面板左边是已购 RI 的订单明细右边是当月资源用量和折扣覆盖率。API 购买的订单进入这张面板后采购、财务、运维三方的信息就打通了。相比之下门户手点出来的 RI 很难沉淀成这样的数据资产。如果你正准备把 RI 采购纳入自动化流程我的建议是从最小范围试起先选一个订阅、一个 SKU、一台实例完整跑通“Token 获取 → 目录查询 → 订单提交 → 状态验证”四步再逐步扩大到批量场景。API 方式真正舒服的地方不在于省一次点击而在于每一次购买都有记录、都可追溯、都能和别人协作评审。跑顺之后你会发现省下来的不仅是 20% 到 50% 的账单还有每个月在门户里反复点选的时间。