Share via


快速入門:使用 Bicep 建立和發佈 Azure 受控應用程式定義

本快速入門說明如何使用 Bicep 在服務類別目錄中建立和發佈 Azure 受控應用程式定義。 服務類別目錄中的定義可供組織成員使用。

若要建立受控應用程式定義並發佈至您的服務類別目錄,請執行下列工作:

  • 使用 Bicep 來開發您的範本,並將其轉換為 Azure Resource Manager 範本 (ARM 範本)。 此範本會定義受控應用程式所部署的 Azure 資源。
  • 使用 Bicep build 命令將 Bicep 轉換為 JSON。 將檔案轉換成 JSON 之後,建議您確認程式碼的正確性。
  • 部署受控應用程式時,定義入口網站的使用者介面元素。
  • 建立包含必要 JSON 檔案的 .zip 套件。 .zip 套件檔案對於服務類別目錄的受控應用程式定義具有 120 MB 限制。
  • 發佈受控應用程式定義,使其可在您的服務類別目錄中使用。

如果您的受控應用程式定義超過 120 MB,或基於組織的合規性原因而想使用自己的儲存體帳戶,請移至快速入門:自備儲存體來建立及發佈 Azure 受控應用程式定義

您也可以使用 Bicep 從服務類別目錄部署受控應用程式定義。 如需詳細資訊,請移至快速入門:使用 Bicep 部署 Azure 受控應用程式定義

必要條件

若要完成這篇文章中的工作,您需要下列項目︰

建立 Bicep 檔案

每個受控應用程式定義都包含名為 mainTemplate.json 的檔案。 此範本會定義要部署的 Azure 資源,而且與一般 ARM 範本不同。 您可以使用 Bicep 來開發範本,然後將 Bicep 檔案轉換成 JSON。

開啟 Visual Studio Code,建立具有區分大小寫名稱 mainTemplate.bicep 的檔案,並加以儲存。

新增下列 Bicep 程式碼並儲存檔案。 它會定義受控應用程式的資源,以部署 App Service、App Service 方案和儲存體帳戶。

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@2022-03-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: appServicePlanSku
    capacity: appServicePlanCapacity
  }
}

resource appServiceApp 'Microsoft.Web/sites@2022-03-01' = {
  name: appServiceName
  location: location
  properties: {
    serverFarmId: appServicePlan.id
    httpsOnly: true
    siteConfig: {
      appSettings: [
        {
          name: 'AppServiceStorageConnectionString'
          value: appServiceStorageConnectionString
        }
      ]
    }
  }
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: storageAccountType
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
  }
}

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 build

將 Bicep 檔案轉換成 JSON 之後,您的 mainTemplate.json 檔案應該符合下列範例。 在 versiontemplateHashmetadata 屬性中,您可能有不同的值。

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.17.1.54307",
      "templateHash": "1234567891234567890"
    }
  },
  "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": "2022-03-01",
      "name": "[parameters('appServicePlanName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "[variables('appServicePlanSku')]",
        "capacity": "[variables('appServicePlanCapacity')]"
      }
    },
    {
      "type": "Microsoft.Web/sites",
      "apiVersion": "2022-03-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')), '2022-09-01').keys[0].value)]"
            }
          ]
        }
      },
      "dependsOn": [
        "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]",
        "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
      ]
    },
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2022-09-01",
      "name": "[variables('storageAccountName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "[parameters('storageAccountType')]"
      },
      "kind": "StorageV2",
      "properties": {
        "accessTier": "Hot"
      }
    }
  ],
  "outputs": {
    "appServicePlan": {
      "type": "string",
      "value": "[parameters('appServicePlanName')]"
    },
    "appServiceApp": {
      "type": "string",
      "value": "[reference(resourceId('Microsoft.Web/sites', variables('appServiceName')), '2022-03-01').defaultHostName]"
    },
    "storageAccount": {
      "type": "string",
      "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-09-01').primaryEndpoints.blob]"
    }
  }
}

定義入口網站體驗

身為發行者,您可以定義用來建立受控應用程式的入口網站體驗。 createUiDefinition.json 檔案會產生入口網站的使用者介面。 您可以使用下拉式清單和文字輸入框等控制項元素,定義使用者為每個參數提供輸入的方式。

在此範例中,使用者介面會提示您輸入 App Service 名稱前置詞、App Service 方案的名稱、儲存體帳戶前置詞和儲存體帳戶類型。 在部署期間,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 個字元,且只有小寫字母和數字。 在命令中,以您唯一的儲存體帳戶名稱取代包括角括弧 (<>) 的預留位置 <demostorageaccount>

在 Visual Studio Code 中,開啟新的 PowerShell 終端機並登入您的 Azure 訂用帳戶。

Connect-AzAccount

此命令會開啟您的預設瀏覽器,並提示您登入 Azure。 如需詳細資訊,請移至使用 Azure PowerShell 登入

在您連線之後,請執行下列命令。

New-AzResourceGroup -Name packageStorageRG -Location westus3

$storageAccount = New-AzStorageAccount `
  -ResourceGroupName packageStorageRG `
  -Name "<demostorageaccount>" `
  -Location westus3 `
  -SkuName Standard_LRS `
  -Kind StorageV2 `
  -AllowBlobPublicAccess $true

$ctx = $storageAccount.Context

New-AzStorageContainer -Name appcontainer -Context $ctx -Permission blob

Set-AzStorageBlobContent `
  -File "app.zip" `
  -Container appcontainer `
  -Blob "app.zip" `
  -Context $ctx

使用下列命令,將封裝檔案的 URI 儲存在名為 packageuri 的變數中。 您部署受控應用程式定義時,將會使用變數的值。

$packageuri=(Get-AzStorageBlob -Container appcontainer -Blob app.zip -Context $ctx).ICloudBlob.StorageUri.PrimaryUri.AbsoluteUri

建立受控應用程式定義

在本節中,您會從 Microsoft Entra ID 取得身分識別資訊、建立資源群組,以及部署受控應用程式定義。

取得群組識別碼和角色定義識別碼

下一個步驟是選取要為客戶管理資源的使用者、安全性群組或應用程式。 此身分識別會根據指派的角色,取得受控資源群組的權限。 角色可以是任何內建的 Azure 角色,例如擁有者或參與者。

此範例會使用安全性群組,而您的 Microsoft Entra 帳戶應是群組的成員。 若要取得群組的物件識別碼,請使用群組的名稱取代包括角括弧 (<>) 的預留位置 <managedAppDemo>。 您部署受控應用程式定義時,將會使用變數的值。

若要建立新的 Microsoft Entra 群組,請移至管理 Microsoft Entra 群組和群組成員資格

$principalid=(Get-AzADGroup -DisplayName <managedAppDemo>).Id

接下來,取得您想要授與使用者、群組或應用程式存取權的 Azure 內建角色的角色定義識別碼。 您部署受控應用程式定義時,將會使用變數的值。

$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 會防止客戶在此資源群組上執行不想要的作業。 目前唯一支援的鎖定等級是 ReadOnlyReadOnly 指定客戶只能讀取受控資源群組中存在的資源。 獲授與受控資源群組存取權的發行者身分識別不受鎖定等級的限制。

建立參數檔案

受控應用程式定義的部署範本要求為多個參數輸入值。 部署命令會提示您輸入值,您也可以為值建立參數檔案。 在此範例中,我們使用參數檔案將參數值傳遞至部署命令。

在 Visual Studio Code 中,建立名為 deployDefinition.parameters.json 的新檔案,並加以儲存。

將下列內容新增至參數檔案並加以儲存。 然後,使用您的值取代包括角括弧 (<>) 的 <placeholder values>

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "managedApplicationDefinitionName": {
      "value": "sampleBicepManagedApplication"
    },
    "packageFileUri": {
      "value": "<placeholder for the packageFileUri>"
    },
    "principalId": {
      "value": "<placeholder for principalid value>"
    },
    "roleId": {
      "value": "<placeholder for roleid value>"
    }
  }
}

下表描述受控應用程式定義的參數值。

參數
managedApplicationDefinitionName 受控應用程式定義的名稱。 在此範例中,請使用 sampleBicepManagedApplication
packageFileUri 輸入 .zip 封裝檔案的 URI。 使用變數 packageuri 的值。 格式為 https://yourStorageAccountName.blob.core.windows.net/appcontainer/app.zip
principalId 需要管理受控資源群組中資源之權限的發行者主體 ID。 使用變數 principalid 的值。
roleId 具有受控資源群組權限的角色識別碼。 例如擁有者、參與者、讀者。 使用變數 roleid 的值。

若要取得變數值:

  • Azure PowerShell:在 PowerShell 中,輸入 $variableName 以顯示變數的值。
  • Azure CLI:在 Bash 中,輸入 echo $variableName 以顯示變數的值。

部署定義

您部署受控應用程式的定義時,它會在您的服務類別目錄中變成可用。 此處理不會部署受控應用程式的資源。

建立名為 bicepDefinitionRG 的資源群組,並部署受控應用程式定義。

New-AzResourceGroup -Name bicepDefinitionRG -Location westus3

New-AzResourceGroupDeployment `
  -ResourceGroupName bicepDefinitionRG `
  -TemplateFile deployDefinition.bicep `
  -TemplateParameterFile deployDefinition.parameters.json

驗證結果

執行下列命令來確認定義已發佈在您的服務類別目錄中。

Get-AzManagedApplicationDefinition -ResourceGroupName bicepDefinitionRG

Get-AzManagedApplicationDefinition 會列出指定資源群組 (例如 sampleBicepManagedApplication) 中所有可用的定義。

請確定使用者可以存取您的定義

您可以存取受控應用程式定義,但您想確保您組織中的其他使用者可以存取它。 至少在定義上將讀者角色授與給他們。 他們可能已從訂用帳戶或資源群組繼承此存取層級。 若要查看可存取定義和新增使用者或群組的人員,請移至使用 Azure 入口網站指派 Azure 角色

清除資源

如果您要部署定義,請繼續移至後續步驟一節,其連結到使用 Bicep 部署定義的文章。

如果您已完成受控應用程式定義,可以刪除您所建立且名為 packageStorageRGbicepDefinitionRG 的資源群組。

命令會提示您確認要移除資源群組。

Remove-AzResourceGroup -Name packageStorageRG

Remove-AzResourceGroup -Name bicepDefinitionRG

下一步

您已發佈受控應用程式定義。 下一個步驟是了解如何部署該定義的執行個體。