在 ARM 模板中使用部署脚本
了解如何使用 Azure 资源管理器 (ARM) 模板中的部署脚本。 借助 deploymentScripts
资源,用户可在 ARM 部署中执行脚本并查看执行结果。
这些脚本可用于执行自定义步骤,如:
- 将用户添加到目录。
- 执行数据平面操作,例如,复制 blob 或种子数据库。
- 查找并验证许可证密钥。
- 创建自签名证书。
- 在 Microsoft Entra ID 中创建对象。
- 从自定义系统中查找 IP 地址块。
部署脚本的优势:
- 易于编码、使用和调试。 你可以在最喜欢的开发环境中开发部署脚本。 脚本可以嵌入模板或外部脚本文件中。
- 可以指定脚本语言和平台。 当前,支持 Linux 环境上的 Azure PowerShell 和 Azure CLI 部署脚本。
- 允许将命令行参数传递给脚本。
- 可以指定脚本输出,并将其传递回部署。
部署脚本资源仅在 Azure 容器实例可用的区域中可用。 请参阅 Azure 容器实例在 Azure 区域的资源可用性。 目前,部署脚本仅使用公用网络。
重要
部署脚本服务需要将两个支持性资源用于脚本执行和故障排除:存储帐户和容器实例。 你可以指定现有的存储帐户,否则脚本服务将为你创建一个存储帐户。 当部署脚本执行进入某种最终状态时,脚本服务通常会删除这两个自动创建的支持性资源。 在删除支持性资源之前,你需要为这些资源付费。 有关定价信息,请参阅容器实例定价和 Azure 存储定价。 若要了解详细信息,请参阅清理部署脚本资源。
注意
Azure 登录的重试逻辑现在内置于包装脚本中。 如果你在部署脚本所在的模板中授予权限,部署脚本服务会在 10 分钟内尝试以 10 秒的间隔进行登录,直到托管标识角色分配复制完毕。
配置最低权限
对于部署脚本 API 2020-10-01 或更高版本,部署脚本的执行涉及两个主体:
部署主体(用于部署模板的主体):此主体用于创建执行部署脚本资源所需的基础资源 - 存储帐户和 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/action
和Microsoft.ContainerInstance/register/action
。部署脚本主体:只有在部署脚本需要向 Azure 进行身份验证并调用 Azure CLI/PowerShell 时,此主体才是必需的。 可通过两种方法指定部署脚本主体:
- 在
identity
属性中指定用户分配的托管标识(请参阅示例模板)。 指定后,脚本服务就会先调用Connect-AzAccount -Environment AzureChinaCloud -Identity
,再调用部署脚本。 托管标识必须具有完成脚本中操作所需的访问权限。 目前,identity
属性仅支持用户分配的托管标识。 若要使用另一标识登录,请使用此列表中的第二种方法。 - 将服务主体凭据作为安全的环境变量传递,然后即可在部署脚本中调用 Connect-AzAccount -Environment AzureChinaCloud 或 az login。
如果使用托管标识,则部署主体需要分配给托管标识资源的“托管的标识操作员”角色(内置角色)。
- 在
示例模板
以下 JSON 是一个示例。
{
"type": "Microsoft.Resources/deploymentScripts",
"apiVersion": "2020-10-01",
"name": "runPowerShellInline",
"location": "[resourceGroup().location]",
"tags": {
"tagName1": "tagValue1",
"tagName2": "tagValue2"
},
"kind": "AzurePowerShell", // or "AzureCLI"
"identity": {
"type": "userAssigned",
"userAssignedIdentities": {
"/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myID": {}
}
},
"properties": {
"forceUpdateTag": "1",
"containerSettings": {
"containerGroupName": "mycustomaci"
},
"storageAccountSettings": {
"storageAccountName": "myStorageAccount",
"storageAccountKey": "myKey"
},
"azPowerShellVersion": "9.7", // or "azCliVersion": "2.47.0",
"arguments": "-name \\\"John Dole\\\"",
"environmentVariables": [
{
"name": "UserName",
"value": "jdole"
},
{
"name": "Password",
"secureValue": "jDolePassword"
}
],
"scriptContent": "
param([string] $name)
$output = 'Hello {0}. The username is {1}, the password is {2}.' -f $name,${Env:UserName},${Env:Password}
Write-Output $output
$DeploymentScriptOutputs = @{}
$DeploymentScriptOutputs['text'] = $output
", // or "primaryScriptUri": "https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/deployment-script/deploymentscript-helloworld.ps1",
"supportingScriptUris":[],
"timeout": "PT30M",
"cleanupPreference": "OnSuccess",
"retentionInterval": "P1D"
}
}
注意
此示例用于演示目的。 scriptContent
和 primaryScriptUri
属性不能在模板中共存。
注意
scriptContent 显示具有多行的脚本。 Azure 门户和 Azure DevOps 管道无法分析具有多行的部署脚本。 可以将 PowerShell 命令(使用分号、“\r\n”或“\n”)链接成一行,或者在外部脚本文件中使用 primaryScriptUri
属性。 有许多免费的 JSON 字符串转义/反转义工具可用。 例如 https://www.freeformatter.com/json-escape.html。
属性值详细信息:
identity
:对于部署脚本 API 版本 2020-10-01 或更高版本,用户分配的托管标识是可选的,除非你需要在脚本中执行任何特定于 Azure 的操作。 对于 API 版本 2019-10-01 预览版,需要托管标识,因为部署脚本服务使用该托管标识来执行脚本。 指定标识属性后,脚本服务就会先调用Connect-AzAccount -Environment AzureChinaCloud -Environment AzureChinaCloud -Environment AzureChinaCloud -Environment AzureChinaCloud -Environment AzureChinaCloud -Identity
再调用用户脚本。 当前,仅支持用户分配的托管标识。 若要使用不同的标识登录,可以在脚本中调用 Connect-AzAccount -Environment AzureChinaCloud -Environment AzureChinaCloud -Environment AzureChinaCloud -Environment AzureChinaCloud -Environment AzureChinaCloud。tags
:部署脚本标记。 如果部署脚本服务生成了存储帐户和容器实例,则会将标记传递给这两个资源,用于标识它们。 标识这些资源的另一种方法是通过其后缀,其中包含“azscripts”。 有关详细信息,请参阅对部署脚本进行监视和故障排除。kind
:指定脚本类型。 当前,支持 Azure PowerShell 和 Azure CLI 脚本。 值为 AzurePowerShell 和 AzureCLI。forceUpdateTag
:在模板部署之间更改此值将强制重新执行部署脚本。 如果使用newGuid()
或utcNow()
函数,则这两个函数只能在参数的默认值中使用。 若要了解详细信息,请参阅多次运行脚本。containerSettings
:指定该设置,用于自定义 Azure 容器实例。 部署脚本需要新的 Azure 容器实例。 无法指定现有的 Azure 容器实例。 但是,可以使用containerGroupName
自定义容器组名称。 如果未指定,将自动生成组名。storageAccountSettings
:指定设置以使用现有的存储帐户。 如果未指定storageAccountName
,将自动创建存储帐户。 请参阅使用现有的存储帐户。azPowerShellVersion
/azCliVersion
:指定要使用的模块版本。 查看支持的 Azure PowerShell 版本的列表。 版本决定了要使用的容器映像:- 大于或等于 9 的 Az 版本使用 Ubuntu 22.04。
- 大于或等于 6 但小于 9 的 Az 版本使用 Ubuntu 20.04。
- 小于 6 的 Az 版本使用 Ubuntu 18.04。
重要
建议升级到最新版本的 Ubuntu,因为 Ubuntu 18.04 即将结束其生命周期,并且不会在 2023 年 5 月 31 日之后再收到安全更新。
查看支持的 Azure CLI 版本的列表。
重要
部署脚本使用 Microsoft Container Registry (MCR) 中可用的 CLI 映像。 认证部署脚本的 CLI 映像一般需要大约一个月的时间。 不要使用 30 天内发布的 CLI 版本。 若要查找映像的发行日期,请参阅 Azure CLI 发行说明。 如果使用了不受支持的版本,错误消息中会列出受支持的版本。
arguments
:指定参数值。 请以空格分隔这些值。部署脚本通过发出 CommandLineToArgvW 系统调用,将参数拆分为字符串数组。 此步骤是必要的,因为参数作为命令属性传递给 Azure 容器实例,而命令属性是字符串数组。
如果参数包含转义字符,请使用 JsonEscaper 对字符进行双重转义。 将最初已转义的字符串粘贴到工具中,然后选择“转义”。 该工具会输出一个经过双重转义处理的字符串。 例如,在前面的示例模板中,参数是
-name \"John Dole\"
。 转义字符串是-name \\\"John Dole\\\"
。若要将 object 类型的 ARM 模板参数作为参数传递,请使用 string() 函数将对象转换为字符串,然后使用 replace() 函数将任何
\"
替换为\\\"
。 例如:replace(string(parameters('tables')), '\"', '\\\"')
有关详细信息,请参阅示例模板。
environmentVariables
:指定环境变量以传递到脚本。 有关详细信息,请参阅开发部署脚本。scriptContent
:指定脚本内容。 若要运行外部脚本,请改用primaryScriptUri
。 有关示例,请参阅使用内联脚本和使用外部脚本。primaryScriptUri
:为具有受支持的文件扩展名的主部署脚本指定一个可公开访问的 URL。 有关详细信息,请参阅使用外部脚本。supportingScriptUris
:指定一个可公开访问的 URL 数组,它们指向在scriptContent
或primaryScriptUri
中调用的支持性文件。 有关详细信息,请参阅使用外部脚本。timeout
:指定 ISO 8601 格式中规定的脚本执行最大允许时间。 默认值为 P1D。cleanupPreference
。 指定当脚本执行进入某种最终状态时,清理两个支持性部署资源(存储帐户和容器实例)的相关首选项。 默认设置为 Always,即,不管最终状态如何(成功、失败、已取消),都会删除支持性资源。 若要了解详细信息,请参阅清理部署脚本资源。retentionInterval
:指定在部署脚本执行进入某种最终状态后,服务保留部署脚本资源的时间间隔。 在此持续时间到期时,将删除部署脚本资源。 持续时间基于 ISO 8601 模式。 保留时间间隔为 1 到 26 小时 (PT26H)。 当cleanupPreference
设置为 OnExpiration 时将使用此属性。 若要了解详细信息,请参阅清理部署脚本资源。
更多示例
- 示例 1:创建一个密钥保管库,并使用部署脚本将证书分配给该保管库。
- 示例 2:在订阅级别创建一个资源组,在该资源组中创建一个密钥保管库,然后使用部署脚本将证书分配给该保管库。
- 示例 3:创建一个用户分配的托管标识,在资源组级别将参与者角色分配给该标识,创建一个密钥保管库,然后使用部署脚本将证书分配给该保管库。
- 示例 4:这是与此列表中的示例 1 相同的方案。 将创建一个新资源组来运行部署脚本。 此模板是订阅级别模板。
- 示例 5:这是与示例 4 相同的方案。 此模板是资源组级别模板。
- 示例 6:手动创建用户分配的托管标识,并向其分配使用 Microsoft Graph API 创建 Microsoft Graph 应用程序的权限;在 ARM 模板中,使用部署脚本创建 Microsoft Entra 应用程序和服务主体,并输出对象 ID 和客户端 ID。
使用内联脚本
以下模板具有一个使用 Microsoft.Resources/deploymentScripts
类型定义的资源。 突出显示的部分是内联脚本。
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"name": {
"type": "string",
"defaultValue": "\\\"John Dole\\\""
},
"utcValue": {
"type": "string",
"defaultValue": "[utcNow()]"
}
},
"resources": [
{
"type": "Microsoft.Resources/deploymentScripts",
"apiVersion": "2020-10-01",
"name": "runPowerShellInlineWithOutput",
"location": "[resourceGroup().location]",
"kind": "AzurePowerShell",
"properties": {
"forceUpdateTag": "[parameters('utcValue')]",
"azPowerShellVersion": "8.3",
"scriptContent": "
param([string] $name)
$output = \"Hello {0}\" -f $name
Write-Output $output
$DeploymentScriptOutputs = @{}
$DeploymentScriptOutputs['text'] = $output
",
"arguments": "[concat('-name', ' ', parameters('name'))]",
"timeout": "PT1H",
"cleanupPreference": "OnSuccess",
"retentionInterval": "P1D"
}
}
],
"outputs": {
"result": {
"value": "[reference('runPowerShellInlineWithOutput').outputs.text]",
"type": "string"
}
}
}
注意
由于内联部署脚本是用双引号括起来的,因此部署脚本内的字符串需要使用反斜杠 (\) 进行转义或者使用单引号括起来。 还可以考虑使用字符串替换,如先前的 JSON 示例所示。
该脚本采用一个参数,并输出参数值。 DeploymentScriptOutputs 用于存储输出DeploymentScriptOutputs
。 在输出部分,值行显示了如何访问存储的值value
。 Write-Output
用于调试目的。 若要了解如何访问输出文件,请参阅对部署脚本进行监视和故障排除。 有关属性说明,请参阅示例模板。
若要运行脚本,请选择“尝试”以打开本地 Shell,然后将以下代码粘贴到 Shell 窗格中。
$resourceGroupName = Read-Host -Prompt "Enter the name of the resource group to be created"
$location = Read-Host -Prompt "Enter the location (i.e. chinaeast2)"
New-AzResourceGroup -Name $resourceGroupName -Location $location
New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -TemplateUri "https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/deployment-script/deploymentscript-helloworld.json"
Write-Host "Press [ENTER] to continue ..."
输出如下所示:
使用外部脚本
除了内联脚本以外,还可以使用外部脚本文件。 仅支持文件扩展名为 ps1 的主 PowerShell 脚本。 对于 CLI 脚本,主脚本可以具有任何扩展名(或没有扩展名),只要这些脚本是有效的 bash 脚本。 若要使用外部脚本文件,请将 scriptContent
替换为 primaryScriptUri
。 例如:
"primaryScriptUri": "https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/deployment-script/deploymentscript-helloworld.ps1",
有关详细信息,请参阅示例模板。
外部脚本文件必须是可访问的。 若要保护存储在 Azure 存储帐户中的脚本文件,请生成 SAS 令牌,并将其包含在模板的 URI 中。 设置到期时间以允许足够的时间来完成部署。 有关详细信息,请参阅使用 SAS 令牌部署专用 ARM 模板。
你负责确保部署脚本(primaryScriptUri
或 supportingScriptUris
)所引用的脚本的完整性。 仅引用你信任的脚本。
使用支持脚本
可以将复杂的逻辑分成一个或多个支持脚本文件。 使用 supportingScriptUris
属性可根据需要向支持脚本文件提供 URI 数组:
"scriptContent": "
...
./Create-Cert.ps1
...
"
"supportingScriptUris": [
"https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/deployment-script/create-cert.ps1"
],
可从内联脚本和主脚本文件中调用支持脚本文件。 支持脚本文件对文件扩展名没有限制。
支持文件在运行时复制到 azscripts/azscriptinput
。 使用相对路径从内联脚本和主脚本文件中引用支持文件。
处理 PowerShell 脚本的输出
以下模板显示了如何在两个 deploymentScripts
资源之间传递值:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"name": {
"type": "string",
"defaultValue": "John Dole"
},
"utcValue": {
"type": "string",
"defaultValue": "[utcNow()]"
}
},
"resources": [
{
"type": "Microsoft.Resources/deploymentScripts",
"apiVersion": "2020-10-01",
"name": "scriptInTemplate1",
"location": "[resourceGroup().location]",
"kind": "AzurePowerShell",
"properties": {
"forceUpdateTag": "[parameters('utcValue')]",
"azPowerShellVersion": "8.3",
"timeout": "PT1H",
"arguments": "[concat('-name', ' ', concat('\\\"', parameters('name'), '\\\"'))]",
"scriptContent": "
param([string] $name)
$output = 'Hello {0}' -f $name
Write-Output $output
$DeploymentScriptOutputs = @{}
$DeploymentScriptOutputs['text'] = $output
",
"cleanupPreference": "Always",
"retentionInterval": "P1D"
}
},
{
"type": "Microsoft.Resources/deploymentScripts",
"apiVersion": "2020-10-01",
"name": "scriptInTemplate2",
"location": "[resourceGroup().location]",
"kind": "AzurePowerShell",
"dependsOn": [
"scriptInTemplate1"
],
"properties": {
"forceUpdateTag": "[parameters('utcValue')]",
"azPowerShellVersion": "8.3",
"timeout": "PT1H",
"arguments": "[concat('-textToEcho', ' ', concat('\\\"', reference('scriptInTemplate1').outputs.text, '\\\"'))]",
"scriptContent": "
param([string] $textToEcho)
Write-Output $textToEcho
$DeploymentScriptOutputs = @{}
$DeploymentScriptOutputs['text'] = $textToEcho
",
"cleanupPreference": "Always",
"retentionInterval": "P1D"
}
}
],
"outputs": {
"result": {
"value": "[reference('scriptInTemplate2').outputs.text]",
"type": "string"
}
}
}
在第一个资源中,定义一个名为 $DeploymentScriptOutputs 的变量,并使用它来存储输出值$DeploymentScriptOutputs
。 若要访问模板内另一个资源的输出值,请使用:
reference('<ResourceName>').outputs.text
处理 CLI 脚本的输出
与 Azure PowerShell 部署脚本相比,CLI/bash 不会公开用于存储脚本输出的通用变量。 而是使用名为 AZ_SCRIPTS_OUTPUT_PATH
的环境变量来指示脚本输出文件的位置。 在 ARM 模板中执行部署脚本时,Bash shell 会自动为你配置此环境变量。 它的预定义值设置为 /mnt/azscripts/azscriptoutput/scriptoutputs.json。 输出必须遵循有效的 JSON 字符串对象结构。 文件的内容应格式化为键值对。 例如,字符串数组应保存为 { "MyResult": [ "foo", "bar"] }。 仅存储数组结果(例如 [ "foo", "bar" ])被视为无效。
部署脚本输出必须保存在 AZ_SCRIPTS_OUTPUT_PATH
位置,并且输出必须是有效的 JSON 字符串对象。 必须将该文件的内容保存为键值对。 例如,字符串数组存储为 { "MyResult": [ "foo", "bar"] }
。 仅存储数组结果是无效的,例如 [ "foo", "bar" ]
。
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"identity": {
"type": "string"
},
"utcValue": {
"type": "string",
"defaultValue": "[utcNow()]"
}
},
"resources": [
{
"type": "Microsoft.Resources/deploymentScripts",
"apiVersion": "2020-10-01",
"name": "runBashWithOutputs",
"location": "[resourceGroup().location]",
"kind": "AzureCLI",
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"[parameters('identity')]": {
}
}
},
"properties": {
"forceUpdateTag": "[parameters('utcValue')]",
"AzCliVersion": "2.40.0",
"timeout": "PT30M",
"arguments": "'foo' 'bar'",
"environmentVariables": [
{
"name": "UserName",
"value": "jdole"
},
{
"name": "Password",
"secureValue": "jDolePassword"
}
],
"scriptContent": "result=$(az keyvault list); echo \"arg1 is: $1\"; echo \"arg2 is: $2\"; echo \"Username is: $UserName\"; echo \"password is: $Password\"; echo $result | jq -c '{Result: map({id: .id})}' > $AZ_SCRIPTS_OUTPUT_PATH",
"cleanupPreference": "OnSuccess",
"retentionInterval": "P1D"
在先前的示例中使用了 jq。 它带有容器映像。 请参阅配置开发环境。
使用现有存储帐户
脚本执行和故障排除需要一个存储帐户和一个容器实例。 可以选择指定现有存储帐户,如果不指定,脚本服务会自动创建存储帐户和容器实例。 使用现有存储帐户的要求:
支持的存储帐户类型如下:
SKU 支持的类型 Premium_LRS FileStorage Premium_ZRS FileStorage Standard_GRS Storage、StorageV2 Standard_GZRS StorageV2 Standard_LRS Storage、StorageV2 Standard_RAGRS Storage、StorageV2 Standard_RAGZRS StorageV2 Standard_ZRS StorageV2 这些组合支持文件共享。 有关详细信息,请参阅创建 Azure 文件共享和存储帐户类型。
尚不支持存储帐户防火墙规则。 有关详细信息,请参阅配置 Azure 存储防火墙和虚拟网络。
部署主体必须具有管理存储帐户的权限,包括读取、创建和删除文件共享。
存储帐户的
allowSharedKeyAccess
属性必须设置为true
。 在 Azure 容器实例 (ACI) 中装载存储帐户的唯一方法是通过访问密钥。
若要指定现有存储帐户,请将以下 JSON 添加到 Microsoft.Resources/deploymentScripts
的属性元素中:
"storageAccountSettings": {
"storageAccountName": "myStorageAccount",
"storageAccountKey": "myKey"
},
storageAccountName
:指定存储帐户的名称。storageAccountKey
:指定存储帐户密钥之一。 可以使用 listKeys() 函数检索密钥。 例如:"storageAccountSettings": { "storageAccountName": "[variables('storageAccountName')]", "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value]" }
有关完整的 Microsoft.Resources/deploymentScripts
定义示例,请参阅示例模板。
使用现有存储帐户时,脚本服务将创建一个具有唯一名称的文件共享。 有关脚本服务清理文件共享的方式,请参阅清理部署脚本资源。
开发部署脚本
处理非终止错误
可通过在部署脚本中使用 $ErrorActionPreference
变量来控制 PowerShell 响应非终止错误的方式。 如果未在部署脚本中设置该变量,则脚本服务将使用默认值“Continue”。
不管为 $ErrorActionPreference
设置了什么值,当脚本遇到错误时,脚本服务会将资源预配状态设置为“失败”。
使用环境变量
部署脚本使用下列环境变量:
环境变量 | 默认值 | 系统预留 |
---|---|---|
AZ_SCRIPTS_AZURE_ENVIRONMENT | AzureCloud | N |
AZ_SCRIPTS_CLEANUP_PREFERENCE | OnExpiration | N |
AZ_SCRIPTS_OUTPUT_PATH | <AZ_SCRIPTS_PATH_OUTPUT_DIRECTORY>/<AZ_SCRIPTS_PATH_SCRIPT_OUTPUT_FILE_NAME> | Y |
AZ_SCRIPTS_PATH_INPUT_DIRECTORY | /mnt/azscripts/azscriptinput | Y |
AZ_SCRIPTS_PATH_OUTPUT_DIRECTORY | /mnt/azscripts/azscriptoutput | Y |
AZ_SCRIPTS_PATH_USER_SCRIPT_FILE_NAME | Azure PowerShell:userscript.ps1;Azure CLI:userscript.sh | Y |
AZ_SCRIPTS_PATH_PRIMARY_SCRIPT_URI_FILE_NAME | primaryscripturi.config | Y |
AZ_SCRIPTS_PATH_SUPPORTING_SCRIPT_URI_FILE_NAME | supportingscripturi.config | Y |
AZ_SCRIPTS_PATH_SCRIPT_OUTPUT_FILE_NAME | scriptoutputs.json | Y |
AZ_SCRIPTS_PATH_EXECUTION_RESULTS_FILE_NAME | executionresult.json | Y |
AZ_SCRIPTS_USER_ASSIGNED_IDENTITY | /subscriptions/ | N |
有关使用 AZ_SCRIPTS_OUTPUT_PATH
的详细信息,请参阅使用 CLI 脚本中的输出。
将安全字符串传递到部署脚本
通过在容器实例中设置环境变量 (EnvironmentVariable),可为容器运行的应用程序或脚本提供动态配置。 部署脚本以与 Azure 容器实例相同的方式处理非安全和安全环境变量。 有关详细信息,请参阅在容器实例中设置环境变量。 有关示例,请参阅示例模板。
环境变量允许的最大大小为 64 KB。
对部署脚本进行监视和故障排除
脚本服务将创建一个存储帐户(除非指定一个现有的存储帐户)和一个容器实例以执行脚本。 如果这些资源是由脚本服务自动创建的,则两个资源的资源名称后缀均为 azscriptsazscripts
。
用户脚本、执行结果和 stdout 文件存储在存储帐户的文件共享中。 有一个名为 azscripts
的文件夹。 在该文件夹中,还有两个用于输入文件和输出文件的文件夹:azscriptinput 和 azscriptoutputazscriptinput
azscriptoutput
。
输出文件夹包含 executionresult.json 和脚本输出文件。 可以在 executionresult.json 中看到脚本执行错误消息。 仅当成功执行脚本时,才会创建输出文件。 输入文件夹包含一个系统 PowerShell 脚本文件和一些用户部署脚本文件。 可以使用修订后的文件替换用户部署脚本文件,然后从 Azure 容器实例重新运行部署脚本。
使用 Azure 门户
部署好部署脚本资源后,该资源会在 Azure 门户中的资源组下列出。 以下屏幕截图显示了部署脚本资源的“概述”页面:
“概述”页面显示了资源的一些重要信息,例如预配状态、存储帐户、容器实例和日志 。
从左侧菜单中,可查看部署脚本内容、传递给脚本的参数以及输出。 还可导出部署脚本的模板,包括部署脚本本身。
使用 PowerShell
通过 Azure PowerShell,可在订阅或资源组范围管理部署脚本:
- Get-AzDeploymentScript:获取或列出部署脚本。
- Get-AzDeploymentScriptLog:获取部署脚本执行的日志。
- Remove-AzDeploymentScript:删除部署脚本及其关联资源。
- Save-AzDeploymentScriptLog:将部署脚本执行的日志保存到磁盘。
Get-AzDeploymentScript
输出类似于:
Name : runPowerShellInlineWithOutput
Id : /subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/myds0618rg/providers/Microsoft.Resources/deploymentScripts/runPowerShellInlineWithOutput
ResourceGroupName : myds0618rg
Location : chinaeast2
SubscriptionId : 01234567-89AB-CDEF-0123-456789ABCDEF
ProvisioningState : Succeeded
Identity : /subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/mydentity1008rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myuami
ScriptKind : AzurePowerShell
AzPowerShellVersion : 9.7
StartTime : 5/11/2023 7:46:45 PM
EndTime : 5/11/2023 7:49:45 PM
ExpirationDate : 5/12/2023 7:49:45 PM
CleanupPreference : OnSuccess
StorageAccountId : /subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/myds0618rg/providers/Microsoft.Storage/storageAccounts/ftnlvo6rlrvo2azscripts
ContainerInstanceId : /subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/myds0618rg/providers/Microsoft.ContainerInstance/containerGroups/ftnlvo6rlrvo2azscripts
Outputs :
Key Value
================== ==================
text Hello John Dole
RetentionInterval : P1D
Timeout : PT1H
使用 Azure CLI
通过 Azure CLI,可在订阅或资源组范围管理部署脚本:
- az deployment-scripts delete:删除部署脚本。
- az deployment-scripts list:列出所有部署脚本。
- az deployment-scripts show:检索部署脚本。
- az deployment-scripts show-log:显示部署脚本日志。
list 命令输出如下所示:
[
{
"arguments": "'foo' 'bar'",
"azCliVersion": "2.40.0",
"cleanupPreference": "OnExpiration",
"containerSettings": {
"containerGroupName": null
},
"environmentVariables": null,
"forceUpdateTag": "20231101T163748Z",
"id": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/myds0624rg/providers/Microsoft.Resources/deploymentScripts/runBashWithOutputs",
"identity": {
"tenantId": "01234567-89AB-CDEF-0123-456789ABCDEF",
"type": "userAssigned",
"userAssignedIdentities": {
"/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourcegroups/myidentity/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myuami": {
"clientId": "01234567-89AB-CDEF-0123-456789ABCDEF",
"principalId": "01234567-89AB-CDEF-0123-456789ABCDEF"
}
}
},
"kind": "AzureCLI",
"location": "chinaeast2",
"name": "runBashWithOutputs",
"outputs": {
"Result": [
{
"id": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/mytest/providers/Microsoft.KeyVault/vaults/mykv1027",
"resourceGroup": "mytest"
}
]
},
"primaryScriptUri": null,
"provisioningState": "Succeeded",
"resourceGroup": "mytest",
"retentionInterval": "1 day, 0:00:00",
"scriptContent": "result=$(az keyvault list); echo \"arg1 is: $1\"; echo $result | jq -c '{Result: map({id: .id})}' > $AZ_SCRIPTS_OUTPUT_PATH",
"status": {
"containerInstanceId": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/mytest/providers/Microsoft.ContainerInstance/containerGroups/eg6n7wvuyxn7iazscripts",
"endTime": "2023-11-01T16:39:12.080950+00:00",
"error": null,
"expirationTime": "2023-11-02T16:39:12.080950+00:00",
"startTime": "2023-11-01T16:37:53.139700+00:00",
"storageAccountId": null
},
"storageAccountSettings": {
"storageAccountKey": null,
"storageAccountName": "dsfruro267qwb4i"
},
"supportingScriptUris": null,
"systemData": {
"createdAt": "2023-10-31T19:06:57.060909+00:00",
"createdBy": "someone@contoso.com",
"createdByType": "User",
"lastModifiedAt": "2023-11-01T16:37:51.859570+00:00",
"lastModifiedBy": "someone@contoso.com",
"lastModifiedByType": "User"
},
"tags": null,
"timeout": "0:30:00",
"type": "Microsoft.Resources/deploymentScripts"
}
]
使用 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 login
armclient get /subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourcegroups/myrg/providers/microsoft.resources/deploymentScripts/myDeployementScript?api-version=2020-10-01
输出类似于:
{
"kind": "AzurePowerShell",
"identity": {
"type": "userAssigned",
"tenantId": "01234567-89AB-CDEF-0123-456789ABCDEF",
"userAssignedIdentities": {
"/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/myidentity1008rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myuami": {
"principalId": "01234567-89AB-CDEF-0123-456789ABCDEF",
"clientId": "01234567-89AB-CDEF-0123-456789ABCDEF"
}
}
},
"location": "chinaeast2",
"systemData": {
"createdBy": "someone@contoso.com",
"createdByType": "User",
"createdAt": "2023-05-11T02:59:04.7501955Z",
"lastModifiedBy": "someone@contoso.com",
"lastModifiedByType": "User",
"lastModifiedAt": "2023-05-11T02:59:04.7501955Z"
},
"properties": {
"provisioningState": "Succeeded",
"forceUpdateTag": "20220625T025902Z",
"azPowerShellVersion": "9.7",
"scriptContent": "\r\n param([string] $name)\r\n $output = \"Hello {0}\" -f $name\r\n Write-Output $output\r\n $DeploymentScriptOutputs = @{}\r\n $DeploymentScriptOutputs['text'] = $output\r\n ",
"arguments": "-name \\\"John Dole\\\"",
"retentionInterval": "P1D",
"timeout": "PT1H",
"containerSettings": {},
"status": {
"containerInstanceId": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/myds0624rg/providers/Microsoft.ContainerInstance/containerGroups/64lxews2qfa5uazscripts",
"storageAccountId": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/myds0624rg/providers/Microsoft.Storage/storageAccounts/64lxews2qfa5uazscripts",
"startTime": "2023-05-11T02:59:07.5951401Z",
"endTime": "2023-05-11T03:00:16.7969234Z",
"expirationTime": "2023-05-12T03:00:16.7969234Z"
},
"outputs": {
"text": "Hello John Dole"
},
"cleanupPreference": "OnSuccess"
},
"id": "/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/myds0624rg/providers/Microsoft.Resources/deploymentScripts/runPowerShellInlineWithOutput",
"type": "Microsoft.Resources/deploymentScripts",
"name": "runPowerShellInlineWithOutput"
}
以下 REST API 返回日志:
/subscriptions/<SubscriptionID>/resourcegroups/<ResourceGroupName>/providers/microsoft.resources/deploymentScripts/<DeploymentScriptResourceName>/logs?api-version=2020-10-01
它仅在删除部署脚本资源之前有效。
要在门户中查看 deploymentScripts 资源,请选择“显示隐藏的类型”:
清理部署脚本资源
这两个自动创建的支持性资源的生命周期永远不会超过 deploymentScript
资源,除非删除这些支持性资源失败。 支持性资源的生命周期由 cleanupPreference
属性控制,deploymentScript
资源的生命周期由 retentionInterval
属性控制:
cleanupPreference
:指定当脚本执行进入某种最终状态时,两个支持性资源的清理首选项。 支持的值包括:Always:一旦脚本执行进入某种最终状态,就立即删除这两个支持性资源。 如果使用现有的存储帐户,脚本服务将删除它创建的文件共享。 由于在清理支持性资源后,
deploymentScripts
资源可能仍然存在,因此在删除资源之前,脚本服务将保留脚本执行结果,例如 stdout、输出和返回值。OnSuccess:仅当脚本执行成功时才删除这两个支持性资源。 如果使用现有存储帐户,则脚本服务仅在脚本执行成功时才删除文件共享。
如果脚本执行不成功,脚本服务将等到
retentionInterval
过期,然后才依次清理支持资源和部署脚本资源。OnExpiration:仅当
retentionInterval
设置过期时才删除这两个支持性资源。 如果使用现有存储帐户,脚本服务将删除文件共享,但保留存储帐户。
容器实例和存储帐户根据
cleanupPreference
被删除。 但是,如果脚本失败,并且cleanupPreference
未设置为 Always,则部署过程会自动使容器保持运行一小时,或运行到清理了容器为止。 你可以利用这段时间对脚本进行故障排除。 如果要在成功部署后保持容器运行,请向脚本添加睡眠步骤。 例如,将 Start-Sleep 添加到脚本的末尾。 如果你未添加睡眠步骤,则容器将被设置为终端状态,即使其尚未被删除,你也无法访问该容器。retentionInterval
:指定将保留deploymentScript
资源的时间间隔,超过此时间间隔后,该资源将过期并被删除。
注意
不建议将脚本服务生成的存储帐户和容器实例用于其他目的。 根据脚本的生命周期,可能会删除这两个资源。
如果将部署脚本部署到带有 CanNotDelete 锁的资源组,则无法删除自动创建的存储帐户和容器实例。 若要解决此问题,可以将部署脚本部署到另一个不带锁的资源组。 请参阅示例模板中的示例 4 和示例 5。
多次运行脚本
部署脚本执行操作是一个幂等操作。 如果未更改 deploymentScripts
资源属性(包括内联脚本),则在重新部署模板时不会执行该脚本。 部署脚本服务将模板中的资源名称与同一资源组中的现有资源进行比较。 如果要多次执行相同的部署脚本,有两个选项可供选择:
更改
deploymentScripts
资源的名称。 例如,使用 utcNow 模板函数作为资源名称或资源名称的一部分。 更改资源名称将创建一个新的deploymentScripts
资源。 这对于保留脚本执行历史记录很有帮助。注意
utcNow
函数只能在参数的默认值中使用。在
forceUpdateTag
模板属性中指定其他值。 例如,使用utcNow
作为值。
注意
编写幂等的部署脚本。 这样可以确保即使它们再次意外运行,也不会引起系统更改。 例如,如果使用部署脚本创建 Azure 资源,请在创建资源之前验证该资源确实不存在,以便脚本成功执行,否则不必再次创建该资源。
配置开发环境
可使用预配置的容器映像作为部署脚本开发环境。 有关详细信息,请参阅为模板中的部署脚本配置开发环境。
成功测试脚本后,可以将其用作模板中的部署脚本。
部署脚本错误代码
错误代码 | 说明 |
---|---|
DeploymentScriptInvalidOperation | 模板中的部署脚本资源定义包含无效的属性名。 |
DeploymentScriptResourceConflict | 无法删除处于非终端状态且执行未超过 1 小时的部署脚本资源。 或者,无法同时重新运行资源标识符相同(订阅、资源组名称和资源名称相同)但脚本正文内容不同的同一部署脚本。 |
DeploymentScriptOperationFailed | 部署脚本操作在内部失败。 联系 Azure 支持部门。 |
DeploymentScriptStorageAccountAccessKeyNotSpecified | 尚未为现有存储帐户指定访问密钥。 |
DeploymentScriptContainerGroupContainsInvalidContainers | 由部署脚本服务创建的容器组已从外部修改,并且添加了无效的容器。 |
DeploymentScriptContainerGroupInNonterminalState | 两个或更多部署脚本资源在同一资源组中使用相同的 Azure 容器实例名称,其中一个尚未完成其执行。 |
DeploymentScriptStorageAccountInvalidKind | BlobBlobStorage 或 BlobStorage 类型的现有存储帐户不支持文件共享,因此无法使用。 |
DeploymentScriptStorageAccountInvalidKindAndSku | 现有存储帐户不支持文件共享。 有关支持的存储帐户类型的列表,请参阅使用现有存储帐户。 |
DeploymentScriptStorageAccountNotFound | 存储帐户不存在,或者已被外部进程或工具删除。 |
DeploymentScriptStorageAccountWithServiceEndpointEnabled | 指定的存储帐户包含一个服务终结点。 不支持具有服务终结点的存储帐户。 |
DeploymentScriptStorageAccountInvalidAccessKey | 为现有存储帐户指定的访问密钥无效。 |
DeploymentScriptStorageAccountInvalidAccessKeyFormat | 存储帐户密钥格式无效。 请参阅管理存储帐户访问密钥。 |
DeploymentScriptExceededMaxAllowedTime | 部署脚本执行时间超过了部署脚本资源定义中指定的超时值。 |
DeploymentScriptInvalidOutputs | 部署脚本输出不是有效的 JSON 对象。 |
DeploymentScriptContainerInstancesServiceLoginFailure | 用户分配的托管标识在间隔 1 分钟的 10 次尝试后无法登录。 |
DeploymentScriptContainerGroupNotFound | 部署脚本服务创建的容器组已被外部工具或进程删除。 |
DeploymentScriptDownloadFailure | 未能下载支持脚本。 请参阅使用支持脚本。 |
DeploymentScriptError | 用户脚本引发错误。 |
DeploymentScriptBootstrapScriptExecutionFailed | 启动脚本引发错误。 启动脚本是协调部署脚本执行的系统脚本。 |
DeploymentScriptExecutionFailed | 部署脚本执行期间出现未知错误。 |
DeploymentScriptContainerInstancesServiceUnavailable | 创建 Azure 容器实例 (ACI) 时,ACI 引发了“服务不可用”错误。 |
DeploymentScriptContainerGroupInNonterminalState | 创建 Azure 容器实例 (ACI) 时,另一个部署脚本正在同一范围中使用相同的 ACI 名称(相同的订阅、资源组名称和资源名称)。 |
DeploymentScriptContainerGroupNameInvalid | 指定的 Azure 容器实例名称 (ACI) 不符合 ACI 要求。 请参阅排查 Azure 容器实例中的常见问题。 |
在部署脚本中使用 Microsoft Graph
部署脚本可以使用 Microsoft Graph 在 Microsoft Entra ID 中创建和处理对象。
命令
使用 Azure CLI 部署脚本时,可以使用 az ad
命令组中的命令来处理应用程序、服务主体、组和用户。 还可以使用 az rest
命令直接调用 Microsoft Graph API。
使用 Azure PowerShell 部署脚本时,可以使用 Invoke-RestMethod
cmdlet 直接调用 Microsoft Graph API。
权限
部署脚本使用的标识需要获得授权才能使用 Microsoft Graph API,并为其执行的操作提供适当的权限。 必须在模板部署之外授权该标识,例如,预先创建用户分配的托管标识并为其分配 Microsoft Graph 应用角色。 有关详细信息,请参阅此快速入门示例。
访问专用虚拟网络
使用 Microsoft.Resources/deploymentScripts 版本 2023-08-01,可以在具有一些附加配置的专用网络中运行部署脚本。
创建用户分配的托管标识,并在
identity
属性中指定它。 若要分配标识,请参阅标识。创建一个将
allowSharedKeyAccess
设置为true
的存储帐户,并指定部署脚本以使用现有存储帐户。 若要指定现有存储帐户,请参阅使用现有存储帐户。 存储帐户需要一些额外的配置。在 Azure 门户中打开存储帐户。
在左侧菜单中,选择“访问控制 (IAM)”,然后选择“角色分配”选项卡。
将
Storage File Data Privileged Contributor
角色添加到用户分配托管标识。在“安全 + 网络”下,选择“网络”,然后选择“防火墙和虚拟网络”。
选择“已从所选虚拟网络和 IP 地址启用”。
在“虚拟网络”下,添加子网。 在屏幕截图中,子网称为 dspvnVnet。
在“异常”下面,选择“允许受信任的服务列表中的 Azure 服务访问此存储帐户”。
以下 ARM 模板演示如何配置用于运行部署脚本的环境:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"prefix": {
"type": "string",
"maxLength": 10
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"userAssignedIdentityName": {
"type": "string",
"defaultValue": "[format('{0}Identity', parameters('prefix'))]"
},
"storageAccountName": {
"type": "string",
"defaultValue": "[format('{0}stg{1}', parameters('prefix'), uniqueString(resourceGroup().id))]"
},
"vnetName": {
"type": "string",
"defaultValue": "[format('{0}Vnet', parameters('prefix'))]"
},
"subnetName": {
"type": "string",
"defaultValue": "[format('{0}Subnet', parameters('prefix'))]"
}
},
"resources": [
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2023-09-01",
"name": "[parameters('vnetName')]",
"location": "[parameters('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"10.0.0.0/16"
]
},
"enableDdosProtection": false,
"subnets": [
{
"name": "[parameters('subnetName')]",
"properties": {
"addressPrefix": "10.0.0.0/24",
"serviceEndpoints": [
{
"service": "Microsoft.Storage"
}
],
"delegations": [
{
"name": "Microsoft.ContainerInstance.containerGroups",
"properties": {
"serviceName": "Microsoft.ContainerInstance/containerGroups"
}
}
]
}
}
]
}
},
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [
{
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName'))]",
"action": "Allow",
"state": "Succeeded"
}
],
"defaultAction": "Deny"
},
"allowSharedKeyAccess": true
},
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
]
},
{
"type": "Microsoft.ManagedIdentity/userAssignedIdentities",
"apiVersion": "2023-07-31-preview",
"name": "[parameters('userAssignedIdentityName')]",
"location": "[parameters('location')]"
},
{
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2022-04-01",
"scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]",
"name": "[guid(tenantResourceId('Microsoft.Authorization/roleDefinitions', '69566ab7-960f-475b-8e7c-b3118f30c6bd'), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName')), resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')))]",
"properties": {
"principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName')), '2023-07-31-preview').principalId]",
"roleDefinitionId": "[tenantResourceId('Microsoft.Authorization/roleDefinitions', '69566ab7-960f-475b-8e7c-b3118f30c6bd')]",
"principalType": "ServicePrincipal"
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]"
]
}
]
}
可以使用以下 ARM 模板测试部署:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"prefix": {
"type": "string"
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"utcValue": {
"type": "string",
"defaultValue": "[utcNow()]"
},
"storageAccountName": {
"type": "string"
},
"vnetName": {
"type": "string"
},
"subnetName": {
"type": "string"
},
"userAssignedIdentityName": {
"type": "string"
}
},
"resources": [
{
"type": "Microsoft.Resources/deploymentScripts",
"apiVersion": "2023-08-01",
"name": "[format('{0}DS', parameters('prefix'))]",
"location": "[parameters('location')]",
"identity": {
"type": "userAssigned",
"userAssignedIdentities": {
"[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName')))]": {}
}
},
"kind": "AzureCLI",
"properties": {
"forceUpdateTag": "[parameters('utcValue')]",
"azCliVersion": "2.47.0",
"storageAccountSettings": {
"storageAccountName": "[parameters('storageAccountName')]"
},
"containerSettings": {
"subnetIds": [
{
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName'))]"
}
]
},
"scriptContent": "echo \"Hello world!\"",
"retentionInterval": "P1D",
"cleanupPreference": "OnExpiration"
}
}
]
}
后续步骤
本教程已介绍部署脚本的用法。 完成部署脚本教程: