PAYATHON 2026

如何自动部署 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 技术配置说明

预览测试还需要满足以下条件:

  1. 测试订阅已经加入 preview audience;预览受众与 private plan 是不同机制。预览受众说明
  2. 目标预览发布已经就绪,仅完成包上传还不够。
  3. 测试订阅已满足 Marketplace 条款接受要求;有费用的 plan 还需要满足购买资格。程序化部署前提
  4. 部署结果能够核对到目标版本。如果 live 与 preview 的解析结果存在歧义,应先确认该 offer 的版本选择行为,不能把部署成功直接当作部署了最新预览包。

plan.version 应使用已发布且对测试订阅可访问的实际值,不要假定 latest 或 * 是受支持的取值。

部署、验证和清理怎样串起来?

流水线可以按以下顺序组织:

  1. 读取本次发布清单,等待目标预览版本可用。
  2. 使用独立测试名称执行 pulumi up --yes。
  3. 等待应用 provisioning state 成功,再执行业务健康检查,并核对应用版本。
  4. 在流水线的 finally 或等效清理阶段执行 pulumi destroy --yes。
  5. 检查应用实例和 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 的订阅中被正确解析。

备注:内容仅供参考。