如何自动部署 Azure Marketplace 预览托管应用?
如果 Partner Center 中该 plan 的类型确实是 Managed application,可以通过 Microsoft.Solutions/applications 进行程序化部署,并用 Marketplace plan 标识指定应用包,无需在部署代码中固定 catalogartifact.azureedge.net 的模板地址。Azure CLI 提供了创建此类实例的命令,Pulumi 也有对应资源。Microsoft 官方部署指南
程序化创建实例和自动定位刚发布的预览包需要分别处理。流水线仍需考虑预览可见性、包版本和发布就绪状态。
先确认 plan 类型和实际部署资源
请在 Partner Center 的 Plan setup 中核对 Plan type,并检查成功部署后是否实际存在 Microsoft.Solutions/applications 资源。原始材料没有提供可查看的截图,无法仅凭描述独立确认这一点。
两种 plan 的部署方式不同:
- Managed application:创建
Microsoft.Solutions/applications实例,应用资源部署到 managed resource group。 - Solution template:执行发布者提供的 ARM template,不能直接套用 Managed Application 的部署方式。
Partner Center 将它们列为不同的 plan 类型。Azure Application plan 类型说明
Microsoft.Resources/deployments 和 Microsoft.Solutions/applications 也可以同时出现在正确的实现中:前者执行 ARM 部署,模板中的资源是后者。可以调整对动态 CDN URI 的依赖,无须因此停用 ARM deployment。
能否查询最新预览模板 URI?
官方 Marketplace Catalog 的 Get Product API 提供 hideKeys 参数,文档说明该参数用于查询预览产品。返回模型中的 artifacts 包含 artifactType 和 uri。因此,可以评估能否通过官方目录 API 获取当前可见的预览元数据以及响应中返回的模板 artifact。Get Product API
请求形式如下,实际参数需要进行 URL 编码:
GET https://catalogapi.azure.com/products/{uniqueProductId}?api-version=2025-05-01&market={market}&skuId={planId}&hideKeys={previewKey}
X-API-Key: {catalogApiKey}
使用时需要注意:
- 该接口要求 Marketplace Catalog API key,不能直接使用普通 ARM access token 认证。
hideKeys使用发布过程中生成的预览 key,不等于订阅 ID,也不替代部署资格检查。- 返回结构允许包含模板 URI,但不能保证每个 Managed Application 预览 plan 都返回 Portal 使用的同一个
templateLink。 - 不能仅凭接口名称就认定它一定返回最后一次上传的包,应核对实际返回的 plan、发布状态和版本信息。
如果没有接口访问资格,或响应缺少所需 artifact,应向 Marketplace 支持确认该 offer 的可用查询方式。不要猜测 artifact ID 或拼接 CDN 路径来获取新模板。
对于真正的 Managed Application,部署本身通常不需要先取得这个 URI。
使用 Azure CLI、PowerShell 或 REST API 部署
Azure CLI 的 az managedapp create 支持 MarketPlace 类型,可通过 publisher、product、plan name 和 plan version 指定 Marketplace 包。Azure CLI 命令参考
以下示例假设已经登录并选择测试订阅,应用所在的资源组已经创建,变量由发布流水线注入:
az managedapp create \
--resource-group "$APP_RG" \
--name "$APP_NAME" \
--location "$LOCATION" \
--kind MarketPlace \
--managed-rg-id "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$MANAGED_RG" \
--plan-publisher "$PUBLISHER_ID" \
--plan-product "$OFFER_ID" \
--plan-name "$PLAN_ID" \
--plan-version "$PACKAGE_VERSION" \
--parameters @app-parameters.json
app-parameters.json 需要使用该应用实际要求的参数名,参数值采用 { "value": ... } 结构。不要直接复制其他 offer 的参数。
Azure PowerShell 可使用 New-AzManagedApplication,设置 -Kind MarketPlace,并通过 -Plan 传入对应的 plan 属性。PowerShell 命令参考
REST 层对应创建 Microsoft.Solutions/applications 资源,主要字段包括:
kind: "MarketPlace"。plan.name、plan.product、plan.publisher、plan.version。properties.managedResourceGroupId。properties.parameters。
具体字段及可用 API version 应以资源定义为准。applications 资源参考
使用 Pulumi 管理部署和销毁
Pulumi Azure Native 提供 azure-native.solutions.Application,支持 kind、plan、managedResourceGroupId 和应用参数,可以直接声明 Marketplace 实例。Pulumi Application 资源参考
下面是 TypeScript 示例。配置中的订阅必须与 Pulumi Azure provider 使用的订阅一致;applicationParameters 需要按真实应用填写,敏感值应使用 secret 配置。
import * as pulumi from "@pulumi/pulumi";
import * as azure from "@pulumi/azure-native";
const config = new pulumi.Config();
const subscriptionId = config.require("subscriptionId");
const location = config.require("location");
const managedRgName = config.require("managedResourceGroupName");
const appRg = new azure.resources.ResourceGroup("preview-app-rg", {
location,
});
const app = new azure.solutions.Application("preview-app", {
resourceGroupName: appRg.name,
applicationName: config.require("applicationName"),
location,
kind: "MarketPlace",
managedResourceGroupId:
`/subscriptions/${subscriptionId}/resourceGroups/${managedRgName}`,
plan: {
publisher: config.require("publisherId"),
product: config.require("offerId"),
name: config.require("planId"),
version: config.require("packageVersion"),
},
parameters: config.requireSecretObject("applicationParameters"),
});
export const applicationId = app.id;
示例使用 Marketplace plan 指定应用,没有引用 CDN URI。applicationDefinitionId 常见于 ServiceCatalog 部署,不应为了绕过 Marketplace 查询而随意填入内部 definition ID。
如何保证测试的是刚发布的预览版本?
建议让发布阶段输出一份部署清单,记录 publisher、offer、plan 和 package version,再由测试阶段读取。
以下标识不要混用:
- artifact ID:发布过程中产生的制品标识。
- package version:Partner Center 技术配置中的包版本,对应部署所需的版本信息。
- API version:Azure 资源 API 的版本,与应用包版本无关。
Partner Center 要求技术配置发生变更时递增包版本,格式为 integer.integer.integer。可以将同一份发布元数据传给部署流水线,避免人工维护两份版本信息。Managed application 技术配置说明
预览测试还需要满足以下条件:
- 测试订阅已经加入 preview audience;预览受众与 private plan 是不同机制。预览受众说明
- 目标预览发布已经就绪,仅完成包上传还不够。
- 测试订阅已满足 Marketplace 条款接受要求;有费用的 plan 还需要满足购买资格。程序化部署前提
- 部署结果能够核对到目标版本。如果 live 与 preview 的解析结果存在歧义,应先确认该 offer 的版本选择行为,不能把部署成功直接当作部署了最新预览包。
plan.version 应使用已发布且对测试订阅可访问的实际值,不要假定 latest 或 * 是受支持的取值。
部署、验证和清理怎样串起来?
流水线可以按以下顺序组织:
- 读取本次发布清单,等待目标预览版本可用。
- 使用独立测试名称执行
pulumi up --yes。 - 等待应用 provisioning state 成功,再执行业务健康检查,并核对应用版本。
- 在流水线的 finally 或等效清理阶段执行
pulumi destroy --yes。 - 检查应用实例和 managed resource group 是否已删除,并处理失败部署留下的资源。
ARM 部署成功只能证明资源预配完成,业务验证仍需由应用自己的检查项完成。
如果采用 CLI,可以通过下面的命令读取实例状态和删除实例:managedapp 命令参考
az managedapp show \
--resource-group "$APP_RG" \
--name "$APP_NAME" \
--query properties.provisioningState \
--output tsv
az managedapp delete \
--resource-group "$APP_RG" \
--name "$APP_NAME"
删除 Managed Application 会连带删除其 managed resource group,清理后仍应核对结果。Managed Applications 概览
如果继续使用 Microsoft.Resources/deployments,清理时要删除实际创建的应用或测试资源,不能只删除 deployment 记录。
这套流水线是基于官方部署接口的实现建议;引用的文档并未提供一套保证适用于所有 Partner Center 预览 offer 的完整 CI/CD 流程。应先验证 plan 类型是否属实,以及目标 package version 能否在 preview audience 的订阅中被正确解析。
备注:内容仅供参考。
