使用 Bicep 编写部署脚本

借助 deploymentScripts 资源,用户可以在 Bicep 部署中执行脚本并查看执行结果。

  • 将用户添加到目录。
  • 执行数据平面操作;例如,复制 blob 或种子数据库。
  • 查找并验证许可证密钥。
  • 创建自签名证书。
  • 在 Microsoft Entra ID 中创建对象。
  • 从自定义系统中查找 IP 地址块。

部署脚本的优点包括:

  • 它们易于进行编码、使用和调试。 你可以在最喜欢的开发环境中开发部署脚本。 脚本可以嵌入 Bicep 文件或外部脚本文件中。
  • 可以指定脚本语言和平台。 当前,支持 Linux 环境中的 Azure PowerShell 和 Azure CLI 部署脚本。
  • 可以允许将命令行参数传递给脚本。
  • 可以指定脚本输出,并将其传递回部署。

部署脚本资源仅在 Azure 容器实例可用的区域中可用。 有关详细信息,请参阅 Azure 区域中 Azure 容器实例的资源可用性

警告

部署脚本服务需要两个额外的资源用于脚本运行和故障排除:存储帐户和容器实例。 通常,服务会在部署脚本完成后清理这些资源。 这些资源在被删除之前会产生费用。

有关定价信息,请参阅容器实例定价Azure 存储定价。 若要了解详细信息,请参阅清理部署脚本资源

培训资源

如果希望通过分步指南了解部署脚本,请参阅使用部署脚本扩展 Bicep 和 ARM 模板

配置最低权限

对于部署脚本 API 2020-10-01 或更高版本,部署脚本的执行会涉及两个主体:

  • 部署主体:此主体用于部署 Bicep 文件。 它会创建运行部署脚本资源所需的底层资源,即存储账户和 Azure 容器实例。 若要配置最小特权权限,请将具有以下属性的自定义角色分配给部署主体:

    {
      "roleName": "deployment-script-minimum-privilege-for-deployment-principal",
      "description": "Configure least privilege for the deployment principal in deployment script",
      "type": "customRole",
      "IsCustom": true,
      "permissions": [
        {
          "actions": [
            "Microsoft.Storage/storageAccounts/*",
            "Microsoft.ContainerInstance/containerGroups/*",
            "Microsoft.Resources/deployments/*",
            "Microsoft.Resources/deploymentScripts/*"
          ],
        }
      ],
      "assignableScopes": [
        "[subscription().id]"
      ]
    }
    

    如果未注册 Azure 存储和 Azure 容器实例资源提供程序,请务必添加 Microsoft.Storage/register/actionMicrosoft.ContainerInstance/register/action

  • 部署脚本主体:只有在部署脚本需要向 Azure 进行身份验证并调用 Azure CLI 或 PowerShell 时,此主体才是必需的。 可通过两种方法指定部署脚本主体:

    如果使用托管标识,则部署主体需要分配给托管标识资源的内置“托管的标识操作员”角色。

目前,没有针对配置部署脚本权限而定制的内置角色。

创建部署脚本

以下示例文件演示了一个简单的 Bicep 文件,其中包含部署脚本资源。 该脚本采用一个字符串参数,并另外创建一个字符串。

param name string = 'John Dole'
param location string = resourceGroup().location

resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
  name: 'inlineCLI'
  location: location
  kind: 'AzureCLI'
  properties: {
    azCliVersion: '2.52.0'
    arguments: name
    scriptContent: 'echo "The argument is ${name}."; jq -n -c --arg st "Hello ${name}" \'{"text": $st}\' > $AZ_SCRIPTS_OUTPUT_PATH'
    retentionInterval: 'PT1H'
  }
}

output text string = deploymentScript.properties.outputs.text

若要详细了解如何创建部署脚本资源,请参阅创建部署脚本。 若要创建用于部署脚本资源的脚本,我们建议建立专用脚本开发环境,例如 Azure 容器实例或 Docker 映像。 脚本开发完成并经过全面测试后,就可以从部署脚本资源集成或调用脚本文件。 有关详细信息,请参阅配置脚本开发环境

将脚本保存到 inlineScript.bicep 文件中,然后使用以下脚本部署资源:

$resourceGroupName = Read-Host -Prompt "Enter the name of the resource group to be created"
$location = Read-Host -Prompt "Enter the location (i.e. chinanorth2)"

New-AzResourceGroup -Name $resourceGroupName -Location $location

New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -TemplateFile "inlineScript.bicep"

Write-Host "Press [ENTER] to continue ..."

使用托管标识

以下示例演示如何使用托管标识从部署脚本内部与 Azure 交互。

@description('The location of the resources.')
param location string = resourceGroup().location

@description('The storage account to list blobs from.')
param storageAccountData {
  name: string
  container: string
}

@description('The role id of Storage Blob Data Reader.')
var storageBlobDataReaderRoleId = '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1'

@description('The storage account to read blobs from.')
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' existing = {
  name: storageAccountData.name
}

@description('The Storage Blob Data Reader Role definition from [Built In Roles](/role-based-access-control/built-in-roles).')
resource storageBlobDataReaderRoleDef 'Microsoft.Authorization/roleDefinitions@2022-05-01-preview' existing = {
  scope: subscription()
  name: storageBlobDataReaderRoleId
}

@description('The user identity for the deployment script.')
resource scriptIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' = {
  name: 'script-identity'
  location: location
}

@description('Assign permission for the deployment scripts user identity access to the read blobs from the storage account.')
resource dataReaderRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  scope: storageAccount
  name: guid(storageBlobDataReaderRoleDef.id, scriptIdentity.id, storageAccount.id)
  properties: {
    principalType: 'ServicePrincipal'
    principalId: scriptIdentity.properties.principalId
    roleDefinitionId: storageBlobDataReaderRoleDef.id
  }
}

@description('The deployment script.')
resource script 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
  name: 'script'
  location: location
  kind: 'AzureCLI'
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${scriptIdentity.id}': {}
    }
  }
  properties: {
    azCliVersion: '2.59.0'
    retentionInterval: 'PT1H'
    arguments: '${storageAccount.properties.primaryEndpoints.blob} ${storageAccountData.container}'
    scriptContent: '''
      #!/bin/bash
      set -e
      az storage blob list --auth-mode login --blob-endpoint $1 --container-name $2
    '''
  }
}

监视部署脚本并对其进行故障排除

对部署脚本资源进行部署时,需要一个存储帐户来存储用户脚本、执行结果和 stdout 文件。 可以指定自己的存储帐户。 有关详细信息,请参阅使用现有存储帐户

指定自己的存储帐户的替代方法包括将 cleanupPreference 设置为 OnExpiration。 然后,将 retentionInterval 配置为一段时间,以允许有充足的时间在移除存储帐户之前查看输出。 有关详细信息,请参阅清理部署脚本资源

cleanupPreference 属性添加到上述 Bicep 文件,并将值设置为 OnExpiration。 默认值为 Always。 此外,将 rentalInterval 设置为 PT1H(1 小时)或更短时间。

param name string = 'John Dole'
param location string = resourceGroup().location

resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
  name: 'inlineCLI'
  location: location
  kind: 'AzureCLI'
  properties: {
    azCliVersion: '2.52.0'
    arguments: name
    scriptContent: 'echo "The argument is ${name}."; jq -n -c --arg st "Hello ${name}" \'{"text": $st}\' > $AZ_SCRIPTS_OUTPUT_PATH'
    cleanupPreference: 'OnExpiration'
    retentionInterval: 'PT1H'
  }
}

output text string = deploymentScript.properties.outputs.text

成功部署 Bicep 文件后,请使用 Azure 门户、Azure CLI、Azure PowerShell 或 REST API 检查结果。

Azure 门户

部署好部署脚本资源后,该资源会在 Azure 门户中的资源组下列出。 除了部署脚本资源外,概述页面还列出了两个支持资源。 超出保留间隔后,将删除支持资源。

请注意,这两个支持资源的名称中都有 azscripts 后缀,因为这些资源会自动进行创建。 识别支持资源的另一种方法是使用 标记

屏幕截图显示了一个部署脚本资源组。

从列表中选择部署脚本资源。 部署脚本资源的概述页面显示了资源的重要信息,例如预配状态和两个支持资源(存储帐户和容器实例)。 “日志”区域显示了脚本中的打印文本。

屏幕截图显示了有关部署脚本资源的信息。

选择“输出”以显示脚本的输出

屏幕截图显示了部署脚本输出。

返回到资源组,选择存储帐户,选择“文件共享”,然后选择共享名称追加了 azscripts 的文件共享。 列表中会显示两个文件夹:azscriptinputazscriptoutput。 输出文件夹包含 executionresult.json 文件和脚本输出文件。 executionresult.json 文件包含脚本执行错误消息。 仅当成功运行脚本时,才会创建输出文件。

屏幕截图显示了一个部署脚本的输出文件夹的内容。

输入文件夹包含一个系统脚本文件和一些用户部署脚本文件。 可以使用修订后的文件替换用户部署脚本文件,然后从 Azure 容器实例重新运行部署脚本。

Azure CLI

可以使用 Azure CLI 在订阅或资源组范围内管理部署脚本:

list 命令的输出与以下实例类似:

{
  "arguments": "John Dole",
  "azCliVersion": "2.52.0",
  "cleanupPreference": "OnExpiration",
  "containerSettings": {
    "containerGroupName": null
  },
  "environmentVariables": null,
  "forceUpdateTag": null,
  "id": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/dsDemo/providers/Microsoft.Resources/deploymentScripts/inlineCLI",
  "identity": null,
  "kind": "AzureCLI",
  "location": "chinanorth2",
  "name": "inlineCLI",
  "outputs": {
    "text": "Hello John Dole"
  },
  "primaryScriptUri": null,
  "provisioningState": "Succeeded",
  "resourceGroup": "dsDemo",
  "retentionInterval": "1:00:00",
  "scriptContent": "echo \"The argument is John Dole.\"; jq -n -c --arg st \"Hello John Dole\" '{\"text\": $st}' > $AZ_SCRIPTS_OUTPUT_PATH",
  "status": {
    "containerInstanceId": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/dsDemo/providers/Microsoft.ContainerInstance/containerGroups/jgczqtxom5oreazscripts",
    "endTime": "2023-12-11T20:20:12.149468+00:00",
    "error": null,
    "expirationTime": "2023-12-11T21:20:12.149468+00:00",
    "startTime": "2023-12-11T20:18:26.674492+00:00",
    "storageAccountId": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/dsDemo/providers/Microsoft.Storage/storageAccounts/jgczqtxom5oreazscripts"
  },
  "storageAccountSettings": null,
  "supportingScriptUris": null,
  "systemData": {
    "createdAt": "2023-12-11T19:45:32.239063+00:00",
    "createdBy": "johndole@contoso.com",
    "createdByType": "User",
    "lastModifiedAt": "2023-12-11T20:18:26.183565+00:00",
    "lastModifiedBy": "johndole@contoso.com",
    "lastModifiedByType": "User"
  },
  "tags": null,
  "timeout": "1 day, 0:00:00",
  "type": "Microsoft.Resources/deploymentScripts"
}

Azure PowerShell

可以使用 Azure PowerShell 在订阅或资源组范围内管理部署脚本:

Get-AzDeploymentScript 输出与以下示例类似:

Name                : inlinePS
Id                  : /subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/dsDemo/providers/Microsoft.Resources/deploymentScripts/inlinePS
ResourceGroupName   : dsDemo
Location            : chinanorth2
SubscriptionId      : 01234567-89AB-CDEF-0123-456789ABCDEF
ProvisioningState   : Succeeded
Identity            :
ScriptKind          : AzurePowerShell
AzPowerShellVersion : 10.0
StartTime           : 12/11/2023 9:45:50 PM
EndTime             : 12/11/2023 9:46:59 PM
ExpirationDate      : 12/11/2023 10:46:59 PM
CleanupPreference   : OnExpiration
StorageAccountId    : /subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/dsDemo/providers/Microsoft.Storage/storageAccounts/ee5o4rmoo6ilmazscripts
ContainerInstanceId : /subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/dsDemo/providers/Microsoft.ContainerInstance/containerGroups/ee5o4rmoo6ilmazscripts
Outputs             :
                      Key                 Value
                      ==================  ==================
                      text                Hello John Dole.

RetentionInterval   : PT1H
Timeout             : P1D

REST API

可以使用 REST API 获取有关资源组级别和订阅级别的部署脚本资源的信息:

/subscriptions/<SubscriptionID>/resourcegroups/<ResourceGroupName>/providers/microsoft.resources/deploymentScripts/<DeploymentScriptResourceName>?api-version=2020-10-01
/subscriptions/<SubscriptionID>/providers/microsoft.resources/deploymentScripts?api-version=2020-10-01

下面的示例使用 ARMClient。 ARMClient 不是受支持的 Microsoft 工具。

armclient login
armclient get /subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourcegroups/myrg/providers/microsoft.resources/deploymentScripts/myDeployementScript?api-version=2020-10-01

输出类似于以下示例:

{
  "kind": "AzureCLI",
  "identity": null,
  "location": "chinanorth2",
  "systemData": {
    "createdAt": "2023-12-11T19:45:32.239063+00:00",
    "createdBy": "johndole@contoso.com",
    "createdByType": "User",
    "lastModifiedAt": "2023-12-11T20:18:26.183565+00:00",
    "lastModifiedBy": "johndole@contoso.com",
    "lastModifiedByType": "User"
  },
  "properties": {
    "provisioningState": "Succeeded",
    "azCliVersion": "2.52.0",
    "scriptContent": "echo \"The argument is John Dole.\"; jq -n -c --arg st \"Hello John Dole\" '{\"text\": $st}' > $AZ_SCRIPTS_OUTPUT_PATH",
    "arguments": "John Dole",
    "retentionInterval": "1:00:00",
    "timeout": "1 day, 0:00:00",
    "containerSettings": {
      "containerGroupName": null
    },
    "status": {
      "containerInstanceId": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/dsDemo/providers/Microsoft.ContainerInstance/containerGroups/jgczqtxom5oreazscripts",
      "endTime": "2023-12-11T20:20:12.149468+00:00",
      "error": null,
      "expirationTime": "2023-12-11T21:20:12.149468+00:00",
      "startTime": "2023-12-11T20:18:26.674492+00:00",
      "storageAccountId": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/dsDemo/providers/Microsoft.Storage/storageAccounts/jgczqtxom5oreazscripts"
    },
    "outputs": {
      "text": "Hello John Dole"
    },
    "cleanupPreference": "OnSuccess"
  },
  "id": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/dsDemo/providers/Microsoft.Resources/deploymentScripts/inlineCLI",
  "type": "Microsoft.Resources/deploymentScripts",
  "name": "inlineCLI",
}

以下 REST API 返回日志:

/subscriptions/<SubscriptionID>/resourcegroups/<ResourceGroupName>/providers/microsoft.resources/deploymentScripts/<DeploymentScriptResourceName>/logs?api-version=2020-10-01

它仅在删除部署脚本资源之前有效。


部署脚本错误代码

下表列出了部署脚本的错误代码:

错误代码 说明
DeploymentScriptInvalidOperation Bicep 文件中的部署脚本资源定义包含无效的属性名。
DeploymentScriptResourceConflict 如果部署脚本资源处于非终端状态且执行未超过 1 小时,则无法删除该部署脚本资源。 或者,无法同时重新运行资源标识符相同(订阅、资源组名称和资源名称相同)但脚本正文内容不同的同一部署脚本。
DeploymentScriptOperationFailed 部署脚本操作在内部失败。 请联系 Microsoft 支持部门。
DeploymentScriptStorageAccountAccessKeyNotSpecified 尚未为现有存储帐户指定访问密钥。
DeploymentScriptContainerGroupContainsInvalidContainers 部署脚本服务创建的容器组是在外部修改的,并且添加了无效的容器。
DeploymentScriptContainerGroupInNonterminalState 两个或更多部署脚本资源在同一资源组中使用相同的 Azure 容器实例名称,其中一个尚未完成其执行。
DeploymentScriptExistingStorageNotInSameSubscriptionAsDeploymentScript 在正部署脚本的订阅中找不到部署中提供的现有存储。
DeploymentScriptStorageAccountInvalidKind BlobBlobStorageBlobStorage 类型的现有存储帐户不支持文件共享,无法进行使用。
DeploymentScriptStorageAccountInvalidKindAndSku 现有存储帐户不支持文件共享。 有关支持的存储帐户类型的列表,请参阅“使用现有存储帐户”
DeploymentScriptStorageAccountNotFound 存储帐户不存在,或者已由外部进程或工具删除。
DeploymentScriptStorageAccountWithServiceEndpointEnabled 指定的存储帐户包含一个服务终结点。 不支持具有服务终结点的存储帐户。
DeploymentScriptStorageAccountInvalidAccessKey 为现有存储帐户指定的访问密钥无效。
DeploymentScriptStorageAccountInvalidAccessKeyFormat 存储帐户密钥的格式无效。 请参阅管理存储帐户访问密钥
DeploymentScriptExceededMaxAllowedTime 部署脚本执行时间超过了部署脚本资源定义中指定的超时值。
DeploymentScriptInvalidOutputs 部署脚本输出不是有效的 JSON 对象。
DeploymentScriptContainerInstancesServiceLoginFailure 用户分配的托管标识在 10 次尝试后,将以一分钟为间隔,无法进行登录。
DeploymentScriptContainerGroupNotFound 外部工具或进程删除了部署脚本服务创建的容器组。
DeploymentScriptDownloadFailure 支持脚本下载失败。 请参阅使用支持脚本
DeploymentScriptError 用户脚本引发错误。
DeploymentScriptBootstrapScriptExecutionFailed 启动脚本引发错误。 启动脚本是协调部署脚本执行的系统脚本。
DeploymentScriptExecutionFailed 执行部署脚本期间发生未知错误。
DeploymentScriptContainerInstancesServiceUnavailable 创建容器实例期间,Azure 容器实例服务会引发“服务不可用”错误。
DeploymentScriptContainerGroupInNonterminalState 在创建容器实例期间,另一个部署脚本在同一范围内使用相同的容器实例名称(同一订阅、资源组名称和资源名称)。
DeploymentScriptContainerGroupNameInvalid 指定的容器实例名称不符合 Azure 容器实例要求。 请参阅排查 Azure 容器实例中的常见问题

访问专用虚拟网络

可以在具有一些附加配置的专用网络中运行部署脚本。 有关详细信息,请参阅使用服务终结点访问专用虚拟网络通过专用终结点以专用方式运行 Bicep 部署脚本

后续步骤

本教程已介绍部署脚本的用法。 若要了解更多信息,请参阅以下文章: