快速入门:使用 Bicep 创建和发布 Azure 托管应用程序定义
此快速入门介绍如何使用 Bicep 以在服务目录中创建和发布 Azure 托管应用程序定义。 你的服务目录中的定义可供组织成员使用。
若要创建托管应用程序定义并将其发布到服务目录,请执行以下任务:
- 使用 Bicep 开发模板,并将其转换为 Azure 资源管理器模板(ARM 模板)。 该模板定义托管应用程序部署的 Azure 资源。
- 使用 Bicep
build
命令将 Bicep 转换为 JSON。 将文件转换为 JSON 后,请验证代码的准确性。 - 部署托管应用程序时,请定义门户的用户界面元素。
- 创建包含必需 JSON 文件的 .zip 包。 该 .zip 包文件对于服务目录的托管应用程序定义具有 120 MB 的限制。
- 发布托管应用程序定义,使其在服务目录中可用。
如果你的托管应用程序定义超过 120 MB,或者出于组织的合规性原因你想要使用自己的存储帐户,请转到快速入门:使用自己的存储来创建和发布 Azure 托管应用程序定义。
还可以使用 Bicep 从服务目录部署托管应用程序定义。 有关详细信息,请转到快速入门:使用 Bicep 部署 Azure 托管应用程序定义。
先决条件
若要完成本文中的任务,需要准备好以下项:
- 具有活动订阅且有权访问 Microsoft Entra 资源(如用户、组或服务主体)的 Azure 帐户。 如果没有帐户,请在开始之前创建一个试用版帐户。
- 包含最新 Azure 资源管理器工具扩展的 Visual Studio Code。 对于 Bicep 文件,请安装适用于 Visual Studio Code 的 Bicep 扩展。
- 安装最新版本的 Azure PowerShell 或 Azure CLI。
创建 Bicep 文件
每个托管应用程序定义均包含一个名为 mainTemplate.json 的文件。 该模板定义了要部署的 Azure 资源,与常规 ARM 模板没有什么不同。 可以使用 Bicep 开发模板,然后将 Bicep 文件转换为 JSON。
打开 Visual Studio Code,创建一个名称区分大小写的文件 mainTemplate.bicep 并保存它。
添加以下 Bicep 代码并保存文件。 它定义用于部署应用程序服务、应用服务计划和存储帐户的托管应用程序资源。
param location string = resourceGroup().location
@description('App Service plan name.')
@maxLength(40)
param appServicePlanName string
@description('App Service name prefix.')
@maxLength(47)
param appServiceNamePrefix string
@description('Storage account name prefix.')
@maxLength(11)
param storageAccountNamePrefix string
@description('Storage account type allowed values')
@allowed([
'Premium_LRS'
'Standard_LRS'
'Standard_GRS'
])
param storageAccountType string
var appServicePlanSku = 'F1'
var appServicePlanCapacity = 1
var appServiceName = '${appServiceNamePrefix}${uniqueString(resourceGroup().id)}'
var storageAccountName = '${storageAccountNamePrefix}${uniqueString(resourceGroup().id)}'
var appServiceStorageConnectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=${environment().suffixes.storage};Key=${storageAccount.listKeys().keys[0].value}'
resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
name: appServicePlanName
location: location
sku: {
name: appServicePlanSku
capacity: appServicePlanCapacity
}
}
resource appServiceApp 'Microsoft.Web/sites@2023-12-01' = {
name: appServiceName
location: location
properties: {
serverFarmId: appServicePlan.id
httpsOnly: true
siteConfig: {
appSettings: [
{
name: 'AppServiceStorageConnectionString'
value: appServiceStorageConnectionString
}
]
}
}
}
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' = {
name: storageAccountName
location: location
sku: {
name: storageAccountType
}
kind: 'StorageV2'
properties: {
accessTier: 'Hot'
allowSharedKeyAccess: false
minimumTlsVersion: 'TLS1_2'
}
}
output appServicePlan string = appServicePlan.name
output appServiceApp string = appServiceApp.properties.defaultHostName
output storageAccount string = storageAccount.properties.primaryEndpoints.blob
将 Bicep 转换为 JSON
使用 PowerShell 或 Azure CLI 生成 mainTemplate.json 文件。 转到用于保存 Bicep 文件的目录,然后运行 build
命令。
bicep build mainTemplate.bicep
若要了解详细信息,请转到 Bicep 生成。
将 Bicep 文件转换为 JSON 后,mainTemplate.json 文件应与以下示例一致。 但 metadata
属性中的 version
和 templateHash
值可能不同。
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.27.1.19265",
"templateHash": "1262990362980206722"
}
},
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"appServicePlanName": {
"type": "string",
"maxLength": 40,
"metadata": {
"description": "App Service plan name."
}
},
"appServiceNamePrefix": {
"type": "string",
"maxLength": 47,
"metadata": {
"description": "App Service name prefix."
}
},
"storageAccountNamePrefix": {
"type": "string",
"maxLength": 11,
"metadata": {
"description": "Storage account name prefix."
}
},
"storageAccountType": {
"type": "string",
"allowedValues": [
"Premium_LRS",
"Standard_LRS",
"Standard_GRS"
],
"metadata": {
"description": "Storage account type allowed values"
}
}
},
"variables": {
"appServicePlanSku": "F1",
"appServicePlanCapacity": 1,
"appServiceName": "[format('{0}{1}', parameters('appServiceNamePrefix'), uniqueString(resourceGroup().id))]",
"storageAccountName": "[format('{0}{1}', parameters('storageAccountNamePrefix'), uniqueString(resourceGroup().id))]"
},
"resources": [
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2023-12-01",
"name": "[parameters('appServicePlanName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[variables('appServicePlanSku')]",
"capacity": "[variables('appServicePlanCapacity')]"
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2023-12-01",
"name": "[variables('appServiceName')]",
"location": "[parameters('location')]",
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]",
"httpsOnly": true,
"siteConfig": {
"appSettings": [
{
"name": "AppServiceStorageConnectionString",
"value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};Key={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2023-04-01').keys[0].value)]"
}
]
}
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
]
},
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-04-01",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('storageAccountType')]"
},
"kind": "StorageV2",
"properties": {
"accessTier": "Hot",
"allowSharedKeyAccess": false,
"minimumTlsVersion": "TLS1_2"
}
}
],
"outputs": {
"appServicePlan": {
"type": "string",
"value": "[parameters('appServicePlanName')]"
},
"appServiceApp": {
"type": "string",
"value": "[reference(resourceId('Microsoft.Web/sites', variables('appServiceName')), '2023-12-01').defaultHostName]"
},
"storageAccount": {
"type": "string",
"value": "[reference(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2023-04-01').primaryEndpoints.blob]"
}
}
}
定义门户体验
作为发布者,你需要定义创建托管应用程序的门户体验。 createUiDefinition.json 文件生成门户的用户界面。 使用控件元素(如下拉框和文本框)来定义用户如何为每个参数提供输入。
在此例中,用户界面会提示你输入应用程序服务名称前缀、应用服务计划的名称、存储帐户前缀和存储帐户类型。 在部署期间,mainTemplate.json 中的变量使用 uniqueString
函数将包含 13 个字符的字符串追加到名称前缀,以便名称在 Azure 中是全局唯一的。
打开 Visual Studio Code,创建一个名称区分大小写的文件 createUiDefinition.json 并保存它。
将以下 JSON 代码添加到文件并保存它。
{
"$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#",
"handler": "Microsoft.Azure.CreateUIDef",
"version": "0.1.2-preview",
"parameters": {
"basics": [
{}
],
"steps": [
{
"name": "webAppSettings",
"label": "Web App settings",
"subLabel": {
"preValidation": "Configure the web app settings",
"postValidation": "Completed"
},
"elements": [
{
"name": "appServicePlanName",
"type": "Microsoft.Common.TextBox",
"label": "App Service plan name",
"placeholder": "App Service plan name",
"defaultValue": "",
"toolTip": "Use alphanumeric characters or hyphens with a maximum of 40 characters.",
"constraints": {
"required": true,
"regex": "^[a-z0-9A-Z-]{1,40}$",
"validationMessage": "Only alphanumeric characters or hyphens are allowed, with a maximum of 40 characters."
},
"visible": true
},
{
"name": "appServiceName",
"type": "Microsoft.Common.TextBox",
"label": "App Service name prefix",
"placeholder": "App Service name prefix",
"defaultValue": "",
"toolTip": "Use alphanumeric characters or hyphens with minimum of 2 characters and maximum of 47 characters.",
"constraints": {
"required": true,
"regex": "^[a-z0-9A-Z-]{2,47}$",
"validationMessage": "Only alphanumeric characters or hyphens are allowed, with a minimum of 2 characters and maximum of 47 characters."
},
"visible": true
}
]
},
{
"name": "storageConfig",
"label": "Storage settings",
"subLabel": {
"preValidation": "Configure the storage settings",
"postValidation": "Completed"
},
"elements": [
{
"name": "storageAccounts",
"type": "Microsoft.Storage.MultiStorageAccountCombo",
"label": {
"prefix": "Storage account name prefix",
"type": "Storage account type"
},
"toolTip": {
"prefix": "Enter maximum of 11 lowercase letters or numbers.",
"type": "Available choices are Standard_LRS, Standard_GRS, and Premium_LRS."
},
"defaultValue": {
"type": "Standard_LRS"
},
"constraints": {
"allowedTypes": [
"Premium_LRS",
"Standard_LRS",
"Standard_GRS"
]
},
"visible": true
}
]
}
],
"outputs": {
"location": "[location()]",
"appServicePlanName": "[steps('webAppSettings').appServicePlanName]",
"appServiceNamePrefix": "[steps('webAppSettings').appServiceName]",
"storageAccountNamePrefix": "[steps('storageConfig').storageAccounts.prefix]",
"storageAccountType": "[steps('storageConfig').storageAccounts.type]"
}
}
}
若要了解详细信息,请转到 CreateUiDefinition 入门。
将文件打包
将这两个文件添加到名为 app.zip 的包文件中。 这两个文件必须都位于该 .zip 文件的根级别。 如果文件位于文件夹中,则当你创建托管应用程序定义时,你会收到一条错误消息,指出所需文件不存在。
将 app.zip 上传到 Azure 存储帐户,以便在部署托管应用程序的定义时使用它。 存储帐户名称在 Azure 中必须是全局唯一的,长度必须为 3-24 个字符,仅包含小写字母和数字。 在命令中,将占位符<pkgstorageaccountname>
(包括尖括号 (<>
)) 替换为唯一的存储帐户名称。
在 Visual Studio Code 中,打开新的 PowerShell 终端并登录到 Azure 订阅。
Connect-AzAccount -Environment AzureChinaCloud
此命令会打开默认浏览器,并提示登录到 Azure。 有关详细信息,请转到使用 Azure PowerShell 登录。
连接后,运行以下命令。
New-AzResourceGroup -Name packageStorageGroup -Location chinanorth3
$pkgstorageparms = @{
ResourceGroupName = "packageStorageGroup"
Name = "<pkgstorageaccountname>"
Location = "chinanorth3"
SkuName = "Standard_LRS"
Kind = "StorageV2"
MinimumTlsVersion = "TLS1_2"
AllowBlobPublicAccess = $true
AllowSharedKeyAccess = $false
}
$pkgstorageaccount = New-AzStorageAccount @pkgstorageparms
$pkgstorageparms
变量使用 PowerShell splatting 来提高用于创建新存储帐户的命令中使用的参数值的可读性。 Splatting 可用于使用多个参数值的其他 PowerShell 命令。
创建存储帐户后,将角色分配存储 Blob 数据参与者添加到存储帐户范围中。 分配对 Microsoft Entra 用户帐户的访问权限。 根据 Azure 中的访问级别,你可能需要管理员分配的其他权限。 有关详细信息,请参阅分配用于访问 Blob 数据的 Azure 角色。
向存储帐户添加角色后,需要几分钟才能在 Azure 中激活。 然后,可以创建创建容器和上传文件所需的上下文。
$pkgstoragecontext = New-AzStorageContext -StorageAccountName $pkgstorageaccount.StorageAccountName -UseConnectedAccount
New-AzStorageContainer -Name appcontainer -Context $pkgstoragecontext -Permission blob
$blobparms = @{
File = "app.zip"
Container = "appcontainer"
Blob = "app.zip"
Context = $pkgstoragecontext
}
Set-AzStorageBlobContent @blobparms
使用以下命令将包文件的 URI 存储在名为 packageuri
的变量中。 将在部署托管应用程序定义时使用该变量的值。
$packageuri=(Get-AzStorageBlob -Container appcontainer -Blob app.zip -Context $pkgstoragecontext).ICloudBlob.StorageUri.PrimaryUri.AbsoluteUri
创建托管应用程序定义
在本部分中,你将从 Microsoft Entra ID 获取标识信息、创建资源组并部署托管应用程序定义。
获取组 ID 和角色定义 ID
下一步是选择用于为客户管理资源的用户、安全组或应用程序。 此标识对受管理资源组的权限与所分配的角色相对应。 角色可以是任何 Azure 内置角色,例如所有者或参与者。
此示例使用安全组,并且 Microsoft Entra 帐户应是组的成员。 若要获取组的对象 ID,请将占位符<managedAppDemo>
((包括尖括号 (<>
)) 替换为组的名称。 将在部署托管应用程序定义时使用该变量的值。
若要创建新的 Microsoft Entra 组,请转到管理 Microsoft Entra 组和组成员身份。
$principalid=(Get-AzADGroup -DisplayName <managedAppDemo>).Id
接下来,获取希望将其访问权限授予用户、组或应用程序的 Azure 内置角色的角色定义 ID。 将在部署托管应用程序定义时使用该变量的值。
$roleid=(Get-AzRoleDefinition -Name Owner).Id
创建定义部署模板
使用 Bicep 文件在服务目录中部署托管应用程序定义。
打开 Visual Studio Code,创建一个名为 deployDefinition.bicep 的文件并保存它。
添加以下 Bicep 代码并保存文件。
param location string = resourceGroup().location
@description('Name of the managed application definition.')
param managedApplicationDefinitionName string
@description('The URI of the .zip package file.')
param packageFileUri string
@description('Publishers Principal ID that needs permissions to manage resources in the managed resource group.')
param principalId string
@description('Role ID for permissions to the managed resource group.')
param roleId string
var definitionLockLevel = 'ReadOnly'
var definitionDisplayName = 'Sample Bicep managed application'
var definitionDescription = 'Sample Bicep managed application that deploys web resources'
resource managedApplicationDefinition 'Microsoft.Solutions/applicationDefinitions@2021-07-01' = {
name: managedApplicationDefinitionName
location: location
properties: {
lockLevel: definitionLockLevel
description: definitionDescription
displayName: definitionDisplayName
packageFileUri: packageFileUri
authorizations: [
{
principalId: principalId
roleDefinitionId: roleId
}
]
}
}
有关模板属性的详细信息,请转到 Microsoft.Solutions/applicationDefinitions。
受管理资源组上的 lockLevel
可防止客户对此资源组执行不良操作。 目前,ReadOnly
是唯一受支持的锁定级别。 ReadOnly
指定客户只能读取受管理资源组中存在的资源。 授予对受管理资源组的访问权限的发布者标识不受该锁定级别控制。
创建参数文件
托管应用程序定义的部署模板需要用户输入多个参数。 部署命令会提示输入值,你也可以为值创建参数文件。 在此示例中,我们使用参数文件将参数值传递给部署命令。
在 Visual Studio Code 中,创建名为 deployDefinition-parameters.bicepparam 的新文件并保存它。
将以下内容添加到参数文件并保存它。 然后,将 <placeholder values>
(包括大括号(<>
)) 替换为你的值。
using './deployDefinition.bicep'
param managedApplicationDefinitionName = 'sampleBicepManagedApplication'
param packageFileUri = '<placeholder for the packageFileUri>'
param principalId = '<placeholder for principalid value>'
param roleId = '<placeholder for roleid value>'
下表描述了托管应用程序定义的参数值。
参数 | 值 |
---|---|
managedApplicationDefinitionName |
托管应用程序定义的名称。 对于此例,请使用 sampleBicepManagedApplication。 |
packageFileUri |
输入 .zip 包文件的 URI。 使用 packageuri 变量的值。 格式为 https://yourStorageAccountName.blob.core.chinacloudapi.cn/appcontainer/app.zip 。 |
principalId |
需要权限来管理受管理资源组中的资源的发布者主体 ID。 使用 principalid 变量的值。 |
roleId |
对受管理资源组具有访问权限的角色 ID。 例如“所有者”、“参与者”、“读者”。 使用 roleid 变量的值。 |
若要获取变量值,请执行以下操作:
- Azure PowerShell:在 PowerShell 中,输入
$variableName
以显示变量的值。 - Azure CLI:在 Bash 中,输入
echo $variableName
以显示变量的值。
部署定义
部署托管应用程序的定义时,它将在服务目录中可用。 此过程不会部署托管应用程序的资源。
创建一个名为 bicepDefinitionGroup 的资源组并部署托管应用程序定义。
New-AzResourceGroup -Name bicepDefinitionGroup -Location chinanorth3
$deployparms = @{
ResourceGroupName = "bicepDefinitionGroup"
TemplateFile = "deployDefinition.bicep"
TemplateParameterFile = "deployDefinition-parameters.bicepparam"
Name = "deployDefinition"
}
New-AzResourceGroupDeployment @deployparms
验证结果
运行以下命令,验证定义是否在服务目录中发布。
Get-AzManagedApplicationDefinition -ResourceGroupName bicepDefinitionGroup
Get-AzManagedApplicationDefinition
列出了指定资源组中的所有可用定义,例如 sampleBicepManagedApplication。
请确保用户可以访问你的定义
你可以访问托管应用程序定义,但你希望确保组织中的其他用户可以访问它。 至少授予他们对定义的读者角色。 他们可能已从订阅或资源组继承了此级别的访问权限。 若要检查谁有权访问定义并添加用户或组,请转到使用 Azure 门户分配 Azure 角色。
清理资源
如果要部署定义,请继续阅读链接到使用 Bicep 部署定义的文章的后续步骤部分。
如果已完成托管应用程序定义,则可以删除创建的名为 packageStorageGroup 和 bicepDefinitionGroup 的资源组。
命令会提示你确认删除资源组。
Remove-AzResourceGroup -Name packageStorageGroup
Remove-AzResourceGroup -Name bicepDefinitionGroup
后续步骤
已发布托管应用程序定义。 下一步是了解如何部署该定义的实例。