You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
解决办法

问题根源

  1. 转义语法错误:操作步骤中replace('"','\"")少了一个闭合单引号,导致生成的JSON字符串格式损坏,引发Newtonsoft.Json序列化失败。
  2. 命令行参数解析问题:直接传递压缩后的JSON字符串时,单引号包裹的参数容易被命令行解析器截断或误处理。
  3. 复杂参数传递方式不当:通过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存储,让脚本直接读取

  1. 将parameters.json上传到Azure Blob存储并获取SAS URL。
  2. 修改执行脚本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对象
  1. 更新创建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中,脚本读取扩展配置文件:

  1. 修改创建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
  1. 修改执行脚本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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 04:54:49