Azure VM 规模集:是否可以通过 bootstrap script/settings 而无需从 URL 下载它们?
Azure VM Scale Set: is it possible to pass bootstrap script/settings without downloading them from URL?
我可以看到使用自定义脚本扩展可以 bootstrap 新 VM(在规模集中)。要访问脚本,它需要 azure 存储 URI 和凭据。这种方法对我不起作用,因为(内部政策)它不允许传递存储凭证。
我的 VMSS 已分配服务标识,后者已在 KeyVault 中注册。因此,直接在盒子上获取凭证非常简单。但是为此我至少需要小 bootstrap 脚本 =)
我发现了一种通过自定义脚本扩展实现此目的的 hacky 方法:
$bootstrapScriptPath = Join-Path -Path $PSScriptRoot -ChildPath "bootstrap.ps1"
$bootstrapScriptBlock = get-command $bootstrapScriptPath | Select -ExpandProperty ScriptBlock
$installScriptBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($bootstrapScriptBlock.ToString()))
"commandToExecute": "[concat('powershell -ExecutionPolicy Unrestricted -EncodedCommand ', parameters('installScriptBase64'))]"
但是不知道有没有更好的解决办法
本质上,我需要云服务提供的东西——上传负载和配置设置的能力。
解决方案
(注意,这是针对 Windows VM。对于 Linux VM,有一种更简单的方法 - 感谢@sendmarsh)
请参阅下面的实际实施(请注意,我将提出此想法的 @4c74356b41 标记为 post 的答案)。
首先,我真的没有看到任何 hacky,这是一个有效的方法。
另一种传入数据的方法——使用custom data 属性。它将作为 vm 中的一个文件提供,我不记得它是否是 base64 编码的,但你可以在配置后快速找到它。
另一种方法是对 VM 使用 Managed Service Identity。这样,您只需为 VM 分配适当的权限,它就可以从存储中下载脚本,而无需您明确传递它们。
无论哪种方式,您都需要将脚本传递给虚拟机并使用脚本扩展调用它。您可以在其中使用带有脚本的自定义图像。或者您可以在公开可用的 url 中获取脚本,以便 vm 始终可以提取并执行它(在这种情况下,您需要 MSI 为您处理身份验证)。
您可以做的另一件事是 pull certificates from KV 直接在 VM 中进行。配置。
您可以避免将脚本扩展与自定义数据和 cloud-init 一起使用 - 如果它是 Linux VM。不使用脚本扩展也会为您节省几分钟的部署时间。
此处有一个 VM 示例:https://msftstack.wordpress.com/2018/11/26/speeding-up-azure-resource-manager-templates-using-cloud-init/ - 您可以对规模集采用相同的方法。
这是我结束的地方:
- 将 Bootstrap.ps1 作为自定义数据传递
- 运行 通过自定义脚本扩展的复杂命令 - 它解码 CustomData.bin,复制为 Bootstrap.ps1 并调用它
参数
- 没有下载外部文件
- Bootstrap.ps1 注册任务计划程序以重新运行,从元数据服务获取用户分配的托管标识的令牌,转到
Key Vault 获取凭据以下载主要有效负载,下载它,
解压缩等
下面是有关如何使 #1 和 #2 正常工作的代码段。
示例 Bootstrap.ps1:
param(
[Parameter(Mandatory=$True)]
[string]
$param1,
[Parameter(Mandatory=$True)]
[string]
$param2
)
$ErrorActionPreference = "Stop"
Write-Output("Running Bootstrap.ps1 with the following parameters:");
Write-Output("`$param1 = `"$param1`";");
Write-Output("`$param2 = `"$param2`";");
将其作为自定义数据传递:
# Encoding bootstrap script
$bootstrapScriptPath = (Join-Path -Path "." -ChildPath "Node/Bootstrap.ps1");
$bootstrapScriptBlock = get-command $bootstrapScriptPath | Select -ExpandProperty ScriptBlock;
$encodedScript = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($bootstrapScriptBlock.ToString()));
...
| Set-AzVMOperatingSystem -CustomData $encodedScript
命令行:
$commandLine = "powershell -ExecutionPolicy Unrestricted -Command `"" ` + # Running powershell in cmd
"`$ErrorActionPreference = 'Stop';" ` + # Upon any error fail the provisioning of the extension
"`$content = [IO.File]::ReadAllText('C:\AzureData\CustomData.bin');" + ` # Read Base64-encoded file
"`$bytes = [System.Convert]::FromBase64String(`$content);" + ` # Convert to bytes
"`$script = [System.Text.Encoding]::Unicode.GetString(`$bytes);" + ` # Decode to string
"[IO.File]::WriteAllText('C:\AzureData\Bootstrap.ps1', `$script);" + ` # Save as Bootstrap.ps1
"C:\AzureData\Bootstrap.ps1 " + ` # Run a script
"-param1 'test' -param2 'test' " + ` # Pass needed parameters
" | Out-File -PSPath 'C:\AzureData\output.log';" ` +
"`"";
自定义脚本扩展:
$extensionSettings = @{ "fileUris" = ""; "commandToExecute" = ""};
$extensionProtectedSettings = @{ "commandToExecute" = "$commandLine" };
$result = Set-AzVMExtension -VMName "$($vm.Name)" -Location $resourceGroupLocation -Publisher "Microsoft.Compute" -Type "CustomScriptExtension" `
-TypeHandlerVersion "1.9" -Settings $extensionSettings -ProtectedSettings $extensionProtectedSettings `
-Name "Bootstrap" -ResourceGroupName $resourceGroup.ResourceGroupName;
我可以看到使用自定义脚本扩展可以 bootstrap 新 VM(在规模集中)。要访问脚本,它需要 azure 存储 URI 和凭据。这种方法对我不起作用,因为(内部政策)它不允许传递存储凭证。
我的 VMSS 已分配服务标识,后者已在 KeyVault 中注册。因此,直接在盒子上获取凭证非常简单。但是为此我至少需要小 bootstrap 脚本 =)
我发现了一种通过自定义脚本扩展实现此目的的 hacky 方法:
$bootstrapScriptPath = Join-Path -Path $PSScriptRoot -ChildPath "bootstrap.ps1"
$bootstrapScriptBlock = get-command $bootstrapScriptPath | Select -ExpandProperty ScriptBlock
$installScriptBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($bootstrapScriptBlock.ToString()))
"commandToExecute": "[concat('powershell -ExecutionPolicy Unrestricted -EncodedCommand ', parameters('installScriptBase64'))]"
但是不知道有没有更好的解决办法
本质上,我需要云服务提供的东西——上传负载和配置设置的能力。
解决方案
(注意,这是针对 Windows VM。对于 Linux VM,有一种更简单的方法 - 感谢@sendmarsh)
请参阅下面的实际实施(请注意,我将提出此想法的 @4c74356b41 标记为 post 的答案)。
首先,我真的没有看到任何 hacky,这是一个有效的方法。
另一种传入数据的方法——使用custom data 属性。它将作为 vm 中的一个文件提供,我不记得它是否是 base64 编码的,但你可以在配置后快速找到它。
另一种方法是对 VM 使用 Managed Service Identity。这样,您只需为 VM 分配适当的权限,它就可以从存储中下载脚本,而无需您明确传递它们。
无论哪种方式,您都需要将脚本传递给虚拟机并使用脚本扩展调用它。您可以在其中使用带有脚本的自定义图像。或者您可以在公开可用的 url 中获取脚本,以便 vm 始终可以提取并执行它(在这种情况下,您需要 MSI 为您处理身份验证)。
您可以做的另一件事是 pull certificates from KV 直接在 VM 中进行。配置。
您可以避免将脚本扩展与自定义数据和 cloud-init 一起使用 - 如果它是 Linux VM。不使用脚本扩展也会为您节省几分钟的部署时间。
此处有一个 VM 示例:https://msftstack.wordpress.com/2018/11/26/speeding-up-azure-resource-manager-templates-using-cloud-init/ - 您可以对规模集采用相同的方法。
这是我结束的地方:
- 将 Bootstrap.ps1 作为自定义数据传递
- 运行 通过自定义脚本扩展的复杂命令 - 它解码 CustomData.bin,复制为 Bootstrap.ps1 并调用它 参数
- 没有下载外部文件
- Bootstrap.ps1 注册任务计划程序以重新运行,从元数据服务获取用户分配的托管标识的令牌,转到 Key Vault 获取凭据以下载主要有效负载,下载它, 解压缩等
下面是有关如何使 #1 和 #2 正常工作的代码段。
示例 Bootstrap.ps1:
param(
[Parameter(Mandatory=$True)]
[string]
$param1,
[Parameter(Mandatory=$True)]
[string]
$param2
)
$ErrorActionPreference = "Stop"
Write-Output("Running Bootstrap.ps1 with the following parameters:");
Write-Output("`$param1 = `"$param1`";");
Write-Output("`$param2 = `"$param2`";");
将其作为自定义数据传递:
# Encoding bootstrap script
$bootstrapScriptPath = (Join-Path -Path "." -ChildPath "Node/Bootstrap.ps1");
$bootstrapScriptBlock = get-command $bootstrapScriptPath | Select -ExpandProperty ScriptBlock;
$encodedScript = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($bootstrapScriptBlock.ToString()));
...
| Set-AzVMOperatingSystem -CustomData $encodedScript
命令行:
$commandLine = "powershell -ExecutionPolicy Unrestricted -Command `"" ` + # Running powershell in cmd
"`$ErrorActionPreference = 'Stop';" ` + # Upon any error fail the provisioning of the extension
"`$content = [IO.File]::ReadAllText('C:\AzureData\CustomData.bin');" + ` # Read Base64-encoded file
"`$bytes = [System.Convert]::FromBase64String(`$content);" + ` # Convert to bytes
"`$script = [System.Text.Encoding]::Unicode.GetString(`$bytes);" + ` # Decode to string
"[IO.File]::WriteAllText('C:\AzureData\Bootstrap.ps1', `$script);" + ` # Save as Bootstrap.ps1
"C:\AzureData\Bootstrap.ps1 " + ` # Run a script
"-param1 'test' -param2 'test' " + ` # Pass needed parameters
" | Out-File -PSPath 'C:\AzureData\output.log';" ` +
"`"";
自定义脚本扩展:
$extensionSettings = @{ "fileUris" = ""; "commandToExecute" = ""};
$extensionProtectedSettings = @{ "commandToExecute" = "$commandLine" };
$result = Set-AzVMExtension -VMName "$($vm.Name)" -Location $resourceGroupLocation -Publisher "Microsoft.Compute" -Type "CustomScriptExtension" `
-TypeHandlerVersion "1.9" -Settings $extensionSettings -ProtectedSettings $extensionProtectedSettings `
-Name "Bootstrap" -ResourceGroupName $resourceGroup.ResourceGroupName;