Azure VM自定义脚本扩展传递JSON对象失败求助
问题背景
我有一个PowerShell脚本,计划在Azure虚拟机的CustomScriptExtension中执行,用于下载各类资源,该脚本需要接收一个配置对象作为参数。但在从JSON文件传递这个配置对象时出现了问题,以下是相关错误信息、配置文件、脚本及操作步骤,寻求可行的解决方法。
错误信息
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, at System.Management.Automation.PipelineOps.InvokePipeline(System.Object, Boolean, System.Management.Automation.CommandParameterInternal[][], System.Management.Automation.Language.CommandBaseAst[], System.Management.Automation.CommandRedirection[][], System.Management.Automation.Language.FunctionContext) at System.Management.Automation.Interpreter.ActionCallInstruction`6[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Run(System.Management.Automation.Interpreter.InterpretedFrame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(System.Management.Automation.Interpreter.InterpretedFrame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(System.Management.Automation.Interpreter.InterpretedFrame) at System.Management.Automation.Interpreter.Interpreter.Run(System.Management.Automation.Interpreter.InterpretedFrame) at System.Management.Automation.Interpreter.LightLambda.RunVoid1[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon) at System.Management.Automation.PSScriptCmdlet.RunClause(System.Action`1<System.Management.Automation.Language.FunctionContext>, System.Object, System.Object) at System.Management.Automation.CommandProcessorBase.Complete() at System.Management.Automation.CommandProcessorBase.DoComplete() at System.Management.Automation.Internal.PipelineProcessor.DoCompleteCore(System.Management.Automation.CommandProcessorBase) at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(System.Object) at System.Management.Automation.PipelineOps.InvokePipeline(System.Object, Boolean, System.Management.Automation.CommandParameterInternal[][], System.Management.Automation.Language.CommandBaseAst[], System.Management.Automation.CommandRedirection[][], System.Management.Automation.Language.FunctionContext) at System.Management.Automation.Interpreter.ActionCallInstruction`6[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Run(System.Management.Automation.Interpreter.InterpretedFrame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(System.Management.Automation.Interpreter.InterpretedFrame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(System.Management.Automation.Interpreter.InterpretedFrame) at System.Management.Automation.Interpreter.Interpreter.Run(System.Management.Automation.Interpreter.InterpretedFrame) at System.Management.Automation.Interpreter.LightLambda.RunVoid1[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon) at System.Management.Automation.DlrScriptCommandProcessor.RunClause(System.Action`1<System.Management.Automation.Language.FunctionContext>, System.Object, System.Object) at System.Management.Automation.DlrScriptCommandProcessor.Complete() at System.Management.Automation.CommandProcessorBase.DoComplete() at System.Management.Automation.Internal.PipelineProcessor.DoCompleteCore(System.Management.Automation.CommandProcessorBase) at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(System.Object) at System.Management.Automation.Runspaces.LocalPipeline.InvokeHelper() at System.Management.Automation.Runspaces.LocalPipeline.InvokeThreadProc() at System.Management.Automation.Runspaces.PipelineThread.WorkerProc() at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [process exited with code 3221225725 (0xc00000fd)] You can now close this terminal with Ctrl+D, or press Enter to restart.
JSON配置文件
{ "parameters": { "keyVault": { "value": { "name": "kv-name", "subscriptionId": "00000-0000-0000-0000" } }, "certificates": { "value": [ { "name": "application-certificate", "SaveTo": "C:\\Temp2\\" }, { "name": "client-certificate", "SaveTo": "C:\\Temp\\" } ] }, "secrets": { "value": [ { "name": "secret1", "secretName": "secret1" }, { "name": "secret2", "secretName": "mysecret2" }, { "name": "Save certificate password", "secretName": "certificatePassword" } ] }, "configCentralUri": { "value": "https://configcentral.adeo.local" } } }
创建VM的PowerShell脚本
[CmdletBinding()] param ( [Parameter()] [string]$VMRGName = 'rg-change-me', $Location = 'westeurope', $VMName = 'myvm', [Parameter(Mandatory = $false)] $Object ) $blobSasUrl = 'https://....' $blobName = 'myscript.ps1' $customScriptParam = @{ ResourceGroupName = $VMRGName Location = $Location VMName = $VMName Name = 'ConfigurationScript' Publisher = 'Microsoft.Compute' ExtensionType = 'CustomScriptExtension' TypeHandlerVersion = '1.1' Settings = @{ 'scriptBlobSasUri' = $blobSasUrl 'commandToExecute' = "powershell -ExecutionPolicy Unrestricted -File $blobName -config '$Object'" } } Set-AzVMExtension @customScriptParam
操作步骤
$config = gc .\parameters.json | ConvertFrom-Json -Depth 10 $configRAW = ($config | convertto-json -Depth 10 -Compress).replace('"','\"") .\createVM.ps1 -Object $configRAW
解决办法
问题根源
- 转义语法错误:操作步骤中
replace('"','\"")少了一个闭合单引号,导致生成的JSON字符串格式损坏,引发Newtonsoft.Json序列化失败。 - 命令行参数解析问题:直接传递压缩后的JSON字符串时,单引号包裹的参数容易被命令行解析器截断或误处理。
- 复杂参数传递方式不当:通过
commandToExecute传递嵌套JSON对象,转义逻辑复杂且容易出错。
修复方案
方案1:修正转义逻辑与参数传递
修改操作步骤中的转义代码,确保生成合法JSON字符串:
$config = Get-Content .\parameters.json -Raw | ConvertFrom-Json -Depth 10 # 正确转义双引号,避免语法错误 $configRAW = ($config | ConvertTo-Json -Depth 10 -Compress).Replace('"', '\"') .\createVM.ps1 -Object $configRAW
同时修改创建VM脚本的commandToExecute,使用双引号嵌套包裹参数:
"commandToExecute" = "powershell -ExecutionPolicy Unrestricted -File $blobName -config `"$Object`""
方案2:上传配置文件到Blob存储,让脚本直接读取
- 将
parameters.json上传到Azure Blob存储并获取SAS URL。 - 修改执行脚本
myscript.ps1,添加下载配置的逻辑:
param( [string]$configSasUrl ) # 创建临时目录 mkdir C:\temp -ErrorAction SilentlyContinue # 下载配置文件 Invoke-WebRequest -Uri $configSasUrl -OutFile "C:\temp\parameters.json" $config = Get-Content "C:\temp\parameters.json" -Raw | ConvertFrom-Json -Depth 10 # 后续业务逻辑使用$config对象
- 更新创建VM脚本的
commandToExecute:
$configSasUrl = "https://yourstorage.blob.core.windows.net/container/parameters.json?sas-token" "commandToExecute" = "powershell -ExecutionPolicy Unrestricted -File $blobName -configSasUrl '$configSasUrl'"
方案3:利用CustomScriptExtension的Settings传递参数
将配置直接放入扩展的Settings中,脚本读取扩展配置文件:
- 修改创建VM脚本:
$config = Get-Content .\parameters.json -Raw | ConvertFrom-Json -Depth 10 $customScriptParam = @{ ResourceGroupName = $VMRGName Location = $Location VMName = $VMName Name = 'ConfigurationScript' Publisher = 'Microsoft.Compute' ExtensionType = 'CustomScriptExtension' TypeHandlerVersion = '1.10' # 使用较新版本提升兼容性 Settings = @{ 'scriptBlobSasUri' = $blobSasUrl 'commandToExecute' = "powershell -ExecutionPolicy Unrestricted -File $blobName" 'config' = $config.parameters } } Set-AzVMExtension @customScriptParam
- 修改执行脚本
myscript.ps1读取扩展配置:
# 读取CustomScriptExtension的运行时配置 $settingsPath = "C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.10\RuntimeSettings\0.settings" $extensionSettings = Get-Content $settingsPath -Raw | ConvertFrom-Json $config = $extensionSettings.Settings.config # 后续业务逻辑使用$config对象
关键注意事项
- Depth参数:使用
ConvertFrom-Json和ConvertTo-Json时,确保-Depth值足够大,避免嵌套对象被截断。 - 扩展版本:优先使用CustomScriptExtension 1.10+版本,兼容性和稳定性更佳。
- 转义验证:传递字符串参数前,打印输出验证转义后的格式是否正确。
内容的提问来源于stack exchange,提问作者Bartolini
相关产品推荐
相关产品推荐

