在 Bicep 中开发部署脚本
本文提供了演示如何在 Bicep 中开发部署脚本的示例。
部署脚本资源可能具有部署持续时间。 若要高效开发和测试这些脚本,请考虑建立专用开发环境,例如 Azure 容器实例 (ACI) 或 Docker 实例。 有关详细信息,请参阅创建开发环境。
语法
以下 Bicep 文件是一个部署脚本资源示例。 有关详细信息,请参阅最新的部署脚本架构。
resource <symbolic-name> 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
name: '<resource-name>'
location: resourceGroup().location
tags: {}
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'<user-assigned-identity-id>': {}
}
}
kind: 'AzureCLI'
properties: {
storageAccountSettings: {
storageAccountName: '<storage-account-name>'
storageAccountKey: '<storage-account-key>'
}
containerSettings: {
containerGroupName: '<container-group-name>'
subnetIds: [
{
id: '<subnet-id>'
}
]
}
environmentVariables: []
azCliVersion: '2.52.0'
arguments: '<script-arguments>'
scriptContent: '''<azure-cli-or-azure-powershell-script>''' // or primaryScriptUri: 'https://raw.githubusercontent.com/Azure/azure-docs-bicep-samples/main/samples/deployment-script/inlineScript.ps1'
supportingScriptUris: []
timeout: 'P1D'
cleanupPreference: 'OnSuccess'
retentionInterval: 'P1D'
forceUpdateTag: '1'
}
}
在部署脚本中,请指定以下属性值:
tags
:指定部署脚本标记。 如果部署脚本服务创建两个支持性资源(存储帐户和容器实例),则相应标记将传递到这两个资源。 你可以使用这些标记来标识这些资源。 标识这些支持性资源的另一种方法是通过其后缀,其中包含“azscripts”。 有关详细信息,请参阅对部署脚本进行监视和故障排除。identity
:对于部署脚本 API2020-10-01
或更高版本,除非需要在脚本中执行任何特定于 Azure 的操作或者你正在专用网络中运行部署脚本,否则用户分配的托管标识是可选的。 API 版本2019-10-01-preview
需要托管标识,因为部署脚本服务将使用该标识来运行脚本。指定
identity
属性后,脚本服务会在调用用户脚本之前调用Connect-AzAccount -Environment AzureChinaCloud -Environment AzureChinaCloud -Identity
。 目前,仅支持用户分配的托管标识。 若要在部署脚本中使用其他标识登录,可调用 Connect-AzAccount -Environment AzureChinaCloud -Environment AzureChinaCloud。 有关详细信息,请参阅配置最低权限。kind
:指定脚本的类型,即AzurePowerShell
或AzureCLI
。 除了kind
,还需要指定azPowerShellVersion
或azCliVersion
属性。storageAccountSettings
:指定设置以使用现有的存储帐户。 如果未指定storageAccountName
,将自动创建存储帐户。 有关详细信息,请参阅使用现有存储帐户。containerSettings
:自定义 Azure 容器实例的名称。 如需详细了解如何配置容器的组名称,请参阅本文后面的配置容器实例部分。 如需了解如何配置subnetIds
以在专用网络中运行部署脚本,请参阅访问专用虚拟网络。environmentVariables
:指定要传递给脚本的环境变量。azPowerShellVersion
/azCliVersion
:指定要使用的模块版本。查看支持的 Azure CLI 版本的列表。
重要
该部署脚本使用 Microsoft 工件注册表中的可用 CLI 映像。 认证部署脚本的 CLI 映像通常需要大约一个月的时间。 请勿使用过去 30 天内发布的 CLI 版本。 若要查找映像的发行日期,请参阅 Azure CLI 发行说明。 如果使用了不受支持的版本,错误消息中会列出受支持的版本。
arguments
:指定参数值。 请以空格分隔这些值。部署脚本通过发出 CommandLineToArgvW 系统调用,将参数拆分为字符串数组。 此步骤是必要的,因为参数作为命令属性传递给 Azure 容器实例,而命令属性是一个字符串数组。
如果参数包含转义字符,请对这些字符进行双重转义。 例如,在前面的示例 Bicep 语法中,参数为
-name \"John Dole\"
。 转义字符串是-name \\"John Dole\\"
。若要将
object
类型的 Bicep 参数作为参数传递,请使用 string() 函数将对象转换为字符串,然后使用 replace() 函数将任何英文引号 ("
) 替换为双重转义的英文引号 (\\"
)。 例如:replace(string(parameters('tables')), '"', '\\"')
有关详细信息,请参阅 Bicep 文件示例。
scriptContent
:指定脚本内容。 它可以是内联脚本,也可以是使用 loadTextContent 函数导入的外部脚本文件。 有关详细信息,请参阅本文后面的内联文件与外部文件部分。 若要运行外部脚本,请改用primaryScriptUri
。primaryScriptUri
:为具有受支持的文件扩展名的主部署脚本指定一个可公开访问的 URL。 有关详细信息,请参阅本文后面的使用外部脚本部分。supportingScriptUris
:指定一个可公开访问的 URL 数组,它们指向在scriptContent
或primaryScriptUri
中调用的支持性文件。 有关详细信息,请参阅本文后面的内联文件与外部文件部分。timeout
:以 ISO 8601 格式指定脚本执行的最大允许时间。 默认值为P1D
。forceUpdateTag
:在 Bicep 文件部署之间更改此值将强制重新运行部署脚本。 如果使用newGuid()
或utcNow()
函数,则只能在参数的默认值中使用。 若要了解详细信息,请参阅本文后面的多次运行脚本部分。cleanupPreference
。 指定在脚本执行进入最终状态时清理两个支持性部署资源(存储帐户和容器实例)的首选项。 默认设置为Always
,该值要求无论最终状态如何(Succeeded
、Failed
或Canceled
)都删除支持性资源。 若要了解详细信息,请参阅本文后面的清理部署脚本资源部分。retentionInterval
:指定在部署脚本执行进入某种最终状态后,服务保留部署脚本资源的时间间隔。 在此持续时间到期时,将删除部署脚本资源。 持续时间基于 ISO 8601 模式。 保留时间间隔为 1 小时 (PT1H
) 到 26 小时 (PT26H
)。 当cleanupPreference
设置为OnExpiration
时,请使用此属性。 若要了解详细信息,请参阅本文后面的清理部署脚本资源部分。
更多示例
- 示例 1:创建一个密钥保管库,并使用部署脚本将证书分配给该保管库。
- 示例 2:在订阅级别创建一个资源组,在该资源组中创建一个密钥保管库,然后使用部署脚本将证书分配给该保管库。
- 示例 3:创建一个用户分配的托管标识,在资源组级别将参与者角色分配给该标识,创建一个密钥保管库,然后使用部署脚本将证书分配给该保管库。
- 示例 4:手动创建一个用户分配的托管标识,并为其分配使用 Microsoft Graph API 创建 Microsoft Entra 应用程序的权限。 在 Bicep 文件中,使用部署脚本创建 Microsoft Entra 应用程序和服务主体,并输出对象 ID 和客户端 ID。
内联文件与外部文件
部署脚本可以驻留在 Bicep 文件中,也可以作为单独的文件存储在外部。
使用内联脚本
以下 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: 'set -e; output="Hello $1"; echo $output'
retentionInterval: 'P1D'
}
}
在脚本中包含 set -e
,以便在命令返回非零状态时立即退出。 这种做法简化了错误调试过程。
加载脚本文件
使用 loadTextContent 函数可以以字符串形式检索脚本文件。 通过此函数,可在外部文件中维护脚本并将其作为部署脚本进行访问。 为脚本文件指定的路径相对于 Bicep 文件。
你可以将内联脚本从前面的 Bicep 文件提取到 hello.sh 文件中,然后将后者放入名为“scripts”的子文件夹中。
output="Hello $1"
echo $output
然后,可以像以下示例一样修改前面的 Bicep 文件:
param name string = 'John Dole'
param location string = resourceGroup().location
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
name: 'loadTextContentCLI'
location: location
kind: 'AzureCLI'
properties: {
azCliVersion: '2.52.0'
arguments: name
scriptContent: loadTextContent('./scripts/hello.sh')
retentionInterval: 'P1D'
}
}
使用外部脚本
你可以使用外部脚本文件,而不是内联脚本。 仅支持扩展名为 .ps1 的主 PowerShell 脚本。 对于 CLI 脚本,主脚本可以携带任何有效的 Bash 脚本扩展名,或者根本没有扩展名。 若要使用外部脚本文件,请将 scriptContent
替换为 primaryScriptUri
。
param name string = 'John Dole'
param location string = resourceGroup().location
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
name: 'externalScriptCLI'
location: location
kind: 'AzureCLI'
properties: {
azCliVersion: '2.52.0'
primaryScriptUri: 'https://raw.githubusercontent.com/Azure/azure-docs-bicep-samples/main/samples/deployment-script/hello.sh'
arguments: '-name ${name}'
retentionInterval: 'P1D'
}
}
外部脚本文件必须是可访问的。 为了帮助保护存储在 Azure 存储帐户中的脚本文件,请生成共享访问签名 (SAS) 令牌并将其包含在模板的 URI 中。 设置过期时间以留出足够的时间来完成部署。 有关详细信息,请参阅使用 SAS 令牌部署专用 ARM 模板。
你负责确保部署脚本所引用的脚本(primaryScriptUri
或 supportingScriptUris
)的完整性。 仅引用你信任的脚本。
使用支持脚本
可以将复杂的逻辑分成一个或多个支持脚本文件。 如有必要,请使用 supportingScriptUris
属性向支持性脚本文件提供一个 URI 数组。
param name string = 'John Dole'
param location string = resourceGroup().location
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
name: 'supportingScriptCLI'
location: location
kind: 'AzureCLI'
properties: {
azCliVersion: '2.52.0'
arguments: name
scriptContent: 'output="Hello $1"; echo $output; ./hello.sh "$1"'
supportingScriptUris: [
'https://raw.githubusercontent.com/Azure/azure-docs-bicep-samples/master/samples/deployment-script/hello.sh'
]
retentionInterval: 'P1D'
}
}
你可以在内联脚本和主脚本文件中调用支持性脚本文件。 支持脚本文件对文件扩展名没有限制。
支持性文件将在运行时复制到 azscripts/azscriptinput。 在内联脚本和主脚本文件中,请使用相对路径引用支持性文件。
访问 Azure 资源
若要访问 Azure 资源,必须配置 identity
元素。 以下 Bicep 文件演示如何检索 Azure 密钥保管库列表。 此外,还需要向用户分配管理标识授予访问密钥保管库的权限。
param identity string
param location string = resourceGroup().location
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
name: 'listKvCLI'
location: location
kind: 'AzureCLI'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${identity}': {}
}
}
properties: {
azCliVersion: '2.52.0'
scriptContent: 'result=$(az keyvault list); echo $result | jq -c \'{Result: map({id: .id})}\' > $AZ_SCRIPTS_OUTPUT_PATH'
retentionInterval: 'P1D'
}
}
output result object = deploymentScript.properties.outputs
注意
Azure 登录的重试逻辑现在内置于包装器脚本中。 如果在部署脚本所在的 Bicep 文件中授予权限,部署脚本服务会进行 10 分钟的登录重试(每次的间隔为 10 秒),直到托管标识的角色分配复制完毕。
使用输出
处理输出的方法因使用的脚本类型(Azure CLI 或 Azure PowerShell)而异。
Azure CLI 部署脚本使用名为 AZ_SCRIPTS_OUTPUT_PATH
的环境变量来指示脚本输出文件的位置。 运行 Bicep 文件中的部署脚本时,Bash shell 会自动为你配置此环境变量。 其预定义值被设置为 /mnt/azscripts/azscriptoutput/scriptoutputs.json
。
输出必须符合有效的 JSON 字符串对象结构。 文件的内容应采用键值对格式。 例如,请以 { "MyResult": [ "foo", "bar"] }
的形式保存字符串数组。 仅存储数组结果(如 [ "foo", "bar" ]
)是无效的。
param name string = 'John Dole'
param location string = resourceGroup().location
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
name: 'outputCLI'
location: location
kind: 'AzureCLI'
properties: {
azCliVersion: '2.52.0'
arguments: name
scriptContent: 'jq -n -c --arg st "Hello ${name}" \'{"text": $st}\' > $AZ_SCRIPTS_OUTPUT_PATH'
retentionInterval: 'P1D'
}
}
output text string = deploymentScript.properties.outputs.text
使用环境变量
将安全字符串传递到部署脚本
你可以在容器实例中设置环境变量 (EnvironmentVariable
),以提供容器运行的应用程序或脚本的动态配置。 部署脚本以与 Azure 容器实例相同的方式处理非安全和安全环境变量。 有关详细信息,请参阅在容器实例中设置环境变量。
环境变量允许的最大大小为 64 KB。
param location string = resourceGroup().location
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
name: 'passEnvVariablesCLI'
location: location
kind: 'AzureCLI'
properties: {
azCliVersion: '2.52.0'
environmentVariables: [
{
name: 'UserName'
value: 'jdole'
}
{
name: 'Password'
secureValue: 'jDolePassword'
}
]
scriptContent: 'echo "Username is :$Username"; echo "Password is: $Password"'
retentionInterval: 'P1D'
}
}
系统定义的环境变量
下表列出了系统定义的环境变量:
环境变量 | 默认值 (CLI) | 默认值 (PowerShell) | 系统预留 |
---|---|---|---|
AZ_SCRIPTS_CLEANUP_PREFERENCE |
Always |
Always |
否 |
AZ_SCRIPTS_OUTPUT_PATH |
/mnt/azscripts/azscriptoutput/scriptoutputs.json |
不适用 | 是 |
AZ_SCRIPTS_PATH_INPUT_DIRECTORY |
/mnt/azscripts/azscriptinput|/mnt/azscripts/azscriptinput |
不适用 | 是 |
AZ_SCRIPTS_PATH_OUTPUT_DIRECTORY |
/mnt/azscripts/azscriptoutput|/mnt/azscripts/azscriptoutput |
不适用 | 是 |
AZ_SCRIPTS_PATH_USER_SCRIPT_FILE_NAME |
userscript.sh |
userscript.ps1 |
是 |
AZ_SCRIPTS_PATH_PRIMARY_SCRIPT_URI_FILE_NAME |
primaryscripturi.config |
primaryscripturi.config |
是 |
AZ_SCRIPTS_PATH_SUPPORTING_SCRIPT_URI_FILE_NAME |
supportingscripturi.config |
supportingscripturi.config |
是 |
AZ_SCRIPTS_PATH_SCRIPT_OUTPUT_FILE_NAME |
scriptoutputs.json |
scriptoutputs.json |
是 |
AZ_SCRIPTS_PATH_EXECUTION_RESULTS_FILE_NAME |
executionresult.json |
executionresult.json |
是 |
AZ_SCRIPTS_USER_ASSIGNED_IDENTITY |
不适用 | 不适用 | 否 |
有关使用 AZ_SCRIPTS_OUTPUT_PATH
的示例,请参阅本文前面的处理输出部分。
若要访问环境变量,请使用以下代码。
param location string = resourceGroup().location
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
name: 'listEnvVariablesCLI'
location: location
kind: 'AzureCLI'
properties: {
azCliVersion: '2.52.0'
scriptContent: 'echo "AZ_SCRIPTS_AZURE_ENVIRONMENT is : $AZ_SCRIPTS_AZURE_ENVIRONMENT",echo "AZ_SCRIPTS_CLEANUP_PREFERENCE is : $AZ_SCRIPTS_CLEANUP_PREFERENCE",echo "AZ_SCRIPTS_OUTPUT_PATH is : $AZ_SCRIPTS_OUTPUT_PATH",echo "AZ_SCRIPTS_PATH_INPUT_DIRECTORY is : $AZ_SCRIPTS_PATH_INPUT_DIRECTORY",echo "AZ_SCRIPTS_PATH_OUTPUT_DIRECTORY is : $AZ_SCRIPTS_PATH_OUTPUT_DIRECTORY",echo "AZ_SCRIPTS_PATH_USER_SCRIPT_FILE_NAME is : $AZ_SCRIPTS_PATH_USER_SCRIPT_FILE_NAME",echo "AZ_SCRIPTS_PATH_PRIMARY_SCRIPT_URI_FILE_NAME is : $AZ_SCRIPTS_PATH_PRIMARY_SCRIPT_URI_FILE_NAME",echo "AZ_SCRIPTS_PATH_SUPPORTING_SCRIPT_URI_FILE_NAME is : $AZ_SCRIPTS_PATH_SUPPORTING_SCRIPT_URI_FILE_NAME",echo "AZ_SCRIPTS_PATH_SCRIPT_OUTPUT_FILE_NAME is : $AZ_SCRIPTS_PATH_SCRIPT_OUTPUT_FILE_NAME",echo "AZ_SCRIPTS_PATH_EXECUTION_RESULTS_FILE_NAME is : $AZ_SCRIPTS_PATH_EXECUTION_RESULTS_FILE_NAME",echo "AZ_SCRIPTS_USER_ASSIGNED_IDENTITY is : $AZ_SCRIPTS_USER_ASSIGNED_IDENTITY"'
retentionInterval: 'P1D'
}
}
使用现有的存储帐户
若要运行脚本并允许进行故障排除,你需要具有一个存储帐户和一个容器实例。 你可以指定现有存储帐户,也可以让脚本服务同时自动创建存储帐户和容器实例。
下面是使用现有存储帐户的要求:
下表列出了支持的帐户类型。 包含各层的列指示的是
-SkuName
或--sku
参数的值。 包含各种支持类型的列指示的是-Kind
或--kind
参数。层 支持的类型 Premium_LRS
FileStorage
Standard_GRS
Storage
,StorageV2
Standard_LRS
Storage
,StorageV2
Standard_RAGRS
Storage
,StorageV2
这些组合支持文件共享。 有关详细信息,请参阅创建 Azure 文件共享和存储帐户类型。
尚不支持存储帐户的防火墙规则。 有关详细信息,请参阅配置 Azure 存储防火墙和虚拟网络。
部署主体必须有权管理存储帐户,包括读取、创建和删除文件共享。 有关详细信息,请参阅配置最低权限。
存储帐户的
allowSharedKeyAccess
属性必须设置为true
。 在 Azure 容器实例 (ACI) 中装载存储帐户的唯一方法是通过访问密钥。
若要指定现有存储帐户,请将以下 Bicep 代码添加到 Microsoft.Resources/deploymentScripts
的属性元素:
param storageAccountName string = 'myStorageAccount'
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
...
properties: {
...
storageAccountSettings: {
storageAccountName: storageAccountName
storageAccountKey: listKeys(resourceId('Microsoft.Storage/storageAccounts', storageAccountName), '2023-01-01').keys[0].value
}
}
}
有关完整的 Microsoft.Resources/deploymentScripts
定义示例,请参阅本文前面的语法部分。
使用现有存储帐户时,脚本服务将创建具有唯一名称的文件共享。 若要了解脚本服务如何清理文件共享,请参阅本文后面的清理部署脚本资源部分。
配置容器实例
部署脚本需要新的 Azure 容器实例。 你无法指定现有的容器实例, 但可以使用 containerGroupName
自定义容器的组名称。 如果你未指定组名称,系统会自动生成。 创建此容器实例需要其他配置。 有关详细信息,请参阅配置最低权限。
你还可以指定用于在专用网络中运行部署脚本的 subnetId
值。 有关详细信息,请参阅访问专用虚拟网络。
param containerGroupName string = 'mycustomaci'
param subnetId string = '/subscriptions/01234567-89AB-CDEF-0123-456789ABCDEF/resourceGroups/myResourceGroup/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet'
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
...
properties: {
...
containerSettings: {
containerGroupName: containerGroupName
subnetIds: [
{
id: subnetId
}
]
}
}
}
多次运行脚本
部署脚本执行操作是一个幂等操作。 如果未更改任何 deploymentScripts
资源属性(包括内联脚本),则重新部署 Bicep 文件时不会运行相应脚本。
部署脚本服务将 Bicep 文件中的资源名称与同一资源组中的现有资源进行比较。 如果要多次运行同一部署脚本,有两个选项可供选择:
更改
deploymentScripts
资源的名称。 例如,使用“utcNow 函数”作为资源名称或资源名称的一部分。utcNow
函数只能在参数的默认值中使用。更改资源名称将创建一个新的
deploymentScripts
资源。 这对于保留脚本执行历史记录很有帮助。在
forceUpdateTag
属性中指定其他值。 例如,使用utcNow
作为值。
请编写部署脚本以确保幂等性,这样意外的重新运行便不会导致系统更改。 例如,通过部署脚本创建 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,并具有其执行的操作的适当权限。 你必须在 Bicep 文件之外授权该标识,例如,预先创建用户分配的托管标识并为其分配 Microsoft Graph 应用角色。 有关详细信息,请参阅此快速入门示例。
清理部署脚本资源
这两个自动创建的支持性资源的生命周期永远不会超过 deploymentScript
资源(除非删除这些支持性资源失败)。 cleanupPreference
属性控制支持性资源的生命周期。 retentionInterval
属性控制 deploymentScript
资源的生命周期。 下面介绍如何使用这些属性:
cleanupPreference
:指定在脚本执行进入最终状态时两个支持性资源的清理首选项。 支持的值包括:Always
:脚本执行进入最终状态后,删除这两个支持性资源。 如果使用现有的存储帐户,脚本服务将删除该服务创建的文件共享。 由于清理支持性资源后deploymentScripts
资源可能仍然存在,因此在删除这些资源之前,脚本服务将保留脚本执行结果(例如stdout
)、输出和返回值。OnSuccess
:只有在脚本执行成功时才会删除这两个支持醒资源。 如果使用现有的存储帐户,脚本服务将仅在脚本执行成功时移除文件共享。如果脚本执行不成功,脚本服务将等到
retentionInterval
值过期,然后再依次清理支持性资源和部署脚本资源。OnExpiration
:只有在retentionInterval
设置过期时,才会删除这两个支持性资源。 如果使用现有的存储帐户,脚本服务会移除文件共享,但保留存储帐户。
容器实例和存储帐户将根据
cleanupPreference
值进行删除。 但是,如果脚本失败且cleanupPreference
未设置为Always
,则部署过程会自动使容器运行一小时或直到容器被清理。 你可以利用这段时间对脚本进行故障排除。如果要在成功部署后保持容器运行,请向脚本添加睡眠步骤。 例如,将 Start-Sleep 添加到脚本的末尾。 如果未添加睡眠步骤,那么容器将设置为最终状态,因此即使尚未删除,也无法再进行访问。
retentionInterval
:指定将保留deploymentScript
资源的时间间隔,超过此时间间隔后,该资源将过期并被删除。
注意
不建议将脚本服务生成的存储帐户和容器实例用于其他目的。 根据脚本的生命周期,可能会移除这两个资源。
后续步骤
本文介绍了如何创建部署脚本资源。 若要了解详细信息,请访问以下链接: