通过Terraform部署Azure CustomScriptExtension执行磁盘脚本失败
尝试通过Terraform部署Azure CustomScriptExtension,完成Windows虚拟机的数据磁盘格式化挂载,以及按需扩容C盘,但始终无法将本地脚本传递至虚拟机执行。自定义PowerShell脚本位于Terraform配置同目录的./CustomScripts/CustomScriptDataDisks.ps1,脚本内容如下:
#Custom script to perform format on data disks, assign letter and name based on disk lun Set-StrictMode -Version 3 try { Write-Output "Change CD drive letter to W" Write-Verbose -Message "[Start]::Changing CD drive letter to W..." $CDDrive = Get-WmiObject -Class Win32_volume -Filter "DriveType = '5'" if ($CDDrive) {$CDDrive | Set-WmiInstance -Arguments @{DriveLetter = 'W:'}} Write-Verbose -Message "[Finish]::Changed CD drive letter to W" #Extend C drive if necessary $CPartition = Get-Partition -DriveLetter C $CSupportedSize = Get-PartitionSupportedSize -DriveLetter C if ($CSupportedSize.SizeMax -gt $CPartition.Size) { Resize-Partition -DriveLetter C -Size $CSupportedSize.SizeMax } Write-Output "Initializing Disks" $disks = Get-Disk | Where-Object partitionstyle -eq 'raw' | Sort-Object number if ($disks) { #Format disks $i = 70 $DiskCount = 1 foreach ($disk in $disks) { $DiskLUN = [int]([regex]::Match($disk.Path, "[#]{1}\d{1,}[#]{1}").Value.Replace("#", "")) if ($DiskLUN -lt 19 -or $DiskLUN -gt 39) { $diskID = [string]$DiskCount $diskLabel = $env:COMPUTERNAME + "-DataDisk-" + $DiskID $diskLetter = [char]$i #Mount Disks $disk | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -UseMaximumSize -DriveLetter $diskLetter | Format-Volume -FileSystem NTFS -NewFileSystemLabel $diskLabel -Confirm:$false -Force $i++; $DiskCount++ } if ($DiskLUN -gt 19 -and $DiskLUN -lt 30) { $i = 76 $DiskCount = 1 $diskID = [string]$DiskCount $diskLabel = $env:COMPUTERNAME + "-LogDisk-" + $DiskID $diskLetter = [char]$i #Mount Disks $disk | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -UseMaximumSize -DriveLetter $diskLetter | Format-Volume -FileSystem NTFS -NewFileSystemLabel $diskLabel -Confirm:$false -Force $i++; $DiskCount++ } if ($DiskLUN -gt 29 -and $DiskLUN -lt 40) { $i = 83 $DiskCount = 1 $diskID = [string]$DiskCount $diskLabel = $env:COMPUTERNAME + "-BackupDisk-" + $DiskID $diskLetter = [char]$i #Mount Disks $disk | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -UseMaximumSize -DriveLetter $diskLetter | Format-Volume -FileSystem NTFS -NewFileSystemLabel $diskLabel -Confirm:$false -Force $i++; $DiskCount++ } } } } #end try catch { Write-Error -Message "Caught an exception:" Write-Error -Message "Exception Type: $($_.Exception.GetType().FullName)" Write-Error -Message "Exception Message: $($_.Exception.Message)" }
已尝试的方案及错误
方案1:使用相对路径指定脚本
配置如下:
resource "azurerm_virtual_machine_extension" "CustomScriptDisks" { name = "${azurerm_windows_virtual_machine.win-vm.name}-CustomScriptDisks" virtual_machine_id = azurerm_windows_virtual_machine.win-vm.id publisher = "Microsoft.Compute" type = "CustomScriptExtension" type_handler_version = "1.10" settings = <<SETTINGS { "commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -File ./CustomScripts/CustomScriptDataDisks.ps1" } SETTINGS }
错误信息:
Command execution finished, but failed because it returned a non-zero exit code of: '-196608'. The command had an error output of: 'The argument './CustomScripts/CustomScriptDataDisks.ps1' to the -File parameter does not exist. Provide the path to an existing '.ps1' file as an argument to the -File parameter. '
方案2:Base64编码脚本嵌入配置
配置如下:
resource "azurerm_virtual_machine_extension" "CustomScriptDisks" { name = "${azurerm_windows_virtual_machine.win-vm.name}-CustomScriptDisks" virtual_machine_id = azurerm_windows_virtual_machine.win-vm.id publisher = "Microsoft.Compute" type = "CustomScriptExtension" type_handler_version = "1.10" protected_settings = <<SETTINGS { "commandToExecute": "powershell -encodedCommand ${textencodebase64(file("./CustomScripts/CustomScriptDataDisks.ps1"), "UTF-16LE")}" } SETTINGS }
错误信息:
Command execution finished, but failed because it returned a non-zero exit code of: '1'. The command had an error output of: 'The command line is too long. '
解决思路
1. 使用fileUris从存储账户下载脚本
将PowerShell脚本上传至Azure存储账户的blob容器(可设置公开访问或生成SAS令牌),在Terraform配置中通过settings指定脚本的存储URL,commandToExecute直接引用下载后的脚本文件名(默认下载路径为C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\<version>\Downloads\0\,可直接用文件名)。
示例配置:
resource "azurerm_virtual_machine_extension" "CustomScriptDisks" { name = "${azurerm_windows_virtual_machine.win-vm.name}-CustomScriptDisks" virtual_machine_id = azurerm_windows_virtual_machine.win-vm.id publisher = "Microsoft.Compute" type = "CustomScriptExtension" type_handler_version = "1.10" settings = <<SETTINGS { "fileUris": ["https://yourstorageaccount.blob.core.windows.net/yourcontainer/CustomScriptDataDisks.ps1"], "commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -File CustomScriptDataDisks.ps1" } SETTINGS # 若使用私有容器,添加存储账户密钥或SAS令牌 protected_settings = <<PROTECTED_SETTINGS { "storageAccountName": "yourstorageaccount", "storageAccountKey": "yourstoragekey" } PROTECTED_SETTINGS }
2. 精简脚本并优化编码
- 移除脚本中不必要的注释、冗余空格,合并重复逻辑,缩小脚本体积
- 使用
trimspace()处理脚本内容后再编码,减少冗余字符
示例:
"commandToExecute": "powershell -encodedCommand ${textencodebase64(trimspace(file("./CustomScripts/CustomScriptDataDisks.ps1")), "UTF-16LE")}"
3. 更新扩展版本至最新稳定版
将type_handler_version更新为最新稳定版(如1.14),新版本通常优化了命令行长度限制和脚本传递逻辑。
4. 本地预生成编码内容
提前在本地用PowerShell将脚本编码为Base64:
[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes((Get-Content -Path ./CustomScripts/CustomScriptDataDisks.ps1 -Raw)))
将编码结果保存为文本文件,在Terraform中直接读取该文件内容,避免配置中生成过长字符串。
示例:
resource "azurerm_virtual_machine_extension" "CustomScriptDisks" { # 其他配置保持不变 protected_settings = <<SETTINGS { "commandToExecute": "powershell -encodedCommand ${file("./CustomScripts/encoded_script.txt")}" } SETTINGS }
内容的提问来源于stack exchange,提问作者NickP

