1

My scenario: I have to pass in a JSON string as parameters to my Azure custom script extension and execute a script to update my VM configs. And I got error like ...ExecuteUpdateConfig.ps1 : Invalid array passed in, ']' expected...

What I have tried:

  1. I use $variable.GetType() to make sure everything is of type string.

  2. I tried to execute my script without using Azure Custom Script extension to test the script itself. And it actually works. I am passing in all strings, where does the "Array" coming from ?

    E.g.

    . ("D:\Utilities\ExecuteUpdateConfig.ps1") -DsnConfigsAsString $dsnconfigs -WebConfigsAsString $webconfigs
    
  3. I think the problem could be how I execute my script and pass in the parameters. I tried two ways in the local:

a. dot sourcing works:

. ("D:\Utilities\ExecuteUpdateConfig.ps1") -DsnConfigsAsString $dsnconfigs -WebConfigsAsString $webconfigs

b. this syntax does not work and will throw the error mentioned:

powershell.exe -ExecutionPolicy Unrestricted -Command "D:\Utilities\\ExecuteUpdateConfig.ps1 -DsnConfigsAsString $dsnconfigs -WebConfigsAsString $webconfigs"

How I generate my JSON string and pass my string to script in custom script extension :

$vm = @{ }
$vm | Add-Member WebConfigs @()
$vm | Add-Member WebConfigs @()
$vm | Add-Member WebConfigs @()

$vm.WebConfigs += ([PSCustomObject]@{
    Path           = "C:\WindowsAzure\Packages\CommonAgentConfig.config"
    Node           = "configSections"
    ChildNode      = "section"
    AttributeKey   = "name"
    AttributeValue = "microsoft.windowsAzure.guestAgent.configuration"
    UpdatedValue   = "SamLeong"
    EncryptedValue = $false
})

##convert to json string 
$webconfigs = ConvertTo-Json @($vm.WebConfigs )
$dsnconfigs = ConvertTo-Json @($vm.DsnConfigs)
$registryconfigs = ConvertTo-Json @()

##Generate a VM with custom script extension using ARM template
##dsnConfigs, webconfigs and registryConfigs are my JSON string
##password,fileuri, resourcegorupname not related to the problems are not shown here
New-AzResourceGroupDeployment `
-ResourceGroupName $resourceGroupName `
-TemplateFile ".\customscriptext.json" `
-adminUsername "tester" `
-adminPassword $adminPassword `
-vmName "test16" `
-dsnConfigs $dsnconfigs -webConfigs $webconfigs -FileUrl $fileUrl `  
-dnsLabelPrefix "samtest16"

In my ARM template, I then concat my params and PowerShell command into CommandToExecute, I defined them as "string" in my parameters section in ARM template:

    {
        "apiVersion": "2018-06-01",
        "type": "Microsoft.Compute/virtualMachines/extensions",
        "location": "[resourceGroup().location]",
        "name": "[concat(parameters('vmName'),'/installcustomscript')]",
        "dependsOn": [
            "[concat('Microsoft.Compute/virtualMachines/', parameters('vmName'))]"
        ],
        "tags": {
            "displayName": "config-app"
        },
        "properties": {
            "publisher": "Microsoft.Compute",
            "type": "CustomScriptExtension",
            "typeHandlerVersion": "1.10",
            "autoUpgradeMinorVersion": true,
            "settings": {
                "fileUris": "[parameters('FileUrl')]"
            },
            "protectedSettings": {
                "commandToExecute": "[concat('powershell -ExecutionPolicy Unrestricted -File ExecuteUpdateConfig.ps1', ' -DsnConfigsAsString ', parameters('dsnConfigs'), ' -WebConfigsAsString ', parameters('webConfigs'))]",
                "storageAccountName": "xxxxxxxxxxxxxxxxxxxxxxxxx",
                "storageAccountKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
            }
        }
    }

And in my ExecuteUpdateConfig.ps1 script:

param([string] $DsnConfigsAsString, [string] $WebConfigsAsString )


try {
    $DsnConfigs = $DsnConfigsAsString | Out-String | ConvertFrom-Json
    $WebConfigs = $WebConfigsAsString | Out-String | ConvertFrom-Json

    #other execution logics follow...
    foreach ($dsn in $DsnConfigs) { ##read the dsn object and update  } 
    foreach ($webConfig in $WebConfigs) { ##read the webconfig object and update }
}catch{
    Write-Error "$($_.Exception.Message)"
    throw "Error in updateAllSettings" 
}

Full error:

Microsoft.Compute/virtualMachines/extensions 'samtest17/installcustomscript' failed 
with message '{
  "status": "Failed",
  "error": {
    "code": "ResourceDeploymentFailure",
    "message": "The resource operation completed with terminal provisioning state      
'Failed'.",
    "details": [
      {
        "code": "VMExtensionProvisioningError",
        "message": "VM has reported a failure when processing extension
'installcustomscript'. Error message: \"Command execution finished, but failed
because it returned a non-zero exit code of: '1'. The command had an error output of:  
'C:\\Packages\\Plugins\\Microsoft.Compute.CustomScriptExtension\\1.10.5\\Downloads\\0\ 
\\r\nExecuteUpdateConfig.ps1 : Invalid array passed in, ']' expected. (3): [\r\n    + 
CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorExcep \r\n
tion\r\n    + FullyQ...' For more information, check the instance view by executing    
Get-AzVmssVm or Get-AzVm (https://aka.ms/GetAzVm). These commands can be executed      
using CloudShell (https://aka.ms/CloudShell)\"\r\n\r\nMore information on
troubleshooting is available at https://aka.ms/VMExtensionCSEWindowsTroubleshoot "     
      }
    ]
  }
}'
At D:\Scripts\Testing\TestCustomScript.ps1:58 char:1
+ New-AzResourceGroupDeployment `
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [New-AzResourceGroupDeployment], Exce  
   ption
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implem  
   entation.NewAzureResourceGroupDeploymentCmdlet

New-AzResourceGroupDeployment : 10:42:42 AM - VM has reported a failure when 
processing extension 'installcustomscript'. Error message: "Command execution 
finished, but failed because it returned a non-zero exit code of: '1'. The command     
had an error output of:
'C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.10.5\Downloads\0\       
ExecuteUpdateConfig.ps1 : Invalid array passed in, ']' expected. (3): [
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorExcep
   tion
    + FullyQ...' For more information, check the instance view by executing
Get-AzVmssVm or Get-AzVm (https://aka.ms/GetAzVm). These commands can be executed      
using CloudShell (https://aka.ms/CloudShell)"
More information on troubleshooting is available at
https://aka.ms/VMExtensionCSEWindowsTroubleshoot
At D:\Scripts\Testing\TestCustomScript.ps1:58 char:1
+ New-AzResourceGroupDeployment `
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [New-AzResourceGroupDeployment], Exce  
   ption
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implem  
   entation.NewAzureResourceGroupDeploymentCmdlet

New-AzResourceGroupDeployment : 10:42:42 AM - Template output evaluation skipped: at   
least one resource deployment operation failed. Please list deployment operations for  
details. Please see https://aka.ms/DeployOperations for usage details.
At D:\Scripts\Testing\TestCustomScript.ps1:58 char:1
+ New-AzResourceGroupDeployment `
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [New-AzResourceGroupDeployment], Exce  
   ption
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implem  
   entation.NewAzureResourceGroupDeploymentCmdlet

New-AzResourceGroupDeployment : 10:42:42 AM - Template output evaluation skipped: at 
least one resource deployment operation failed. Please list deployment operations for  
details. Please see https://aka.ms/DeployOperations for usage details.
At D:\Scripts\Testing\TestCustomScript.ps1:58 char:1
+ New-AzResourceGroupDeployment `
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [New-AzResourceGroupDeployment], Exce  
   ption
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implem  
   entation.NewAzureResourceGroupDeploymentCmdlet


Community
  • 1
  • 1
csamleong
  • 769
  • 1
  • 11
  • 24
  • Does this answer your question? [Unexpected ConvertTo-Json results? Answer: it has a default -Depth of 2](https://stackoverflow.com/questions/53583677/unexpected-convertto-json-results-answer-it-has-a-default-depth-of-2) – iRon Mar 25 '20 at 07:05
  • I think it is not be the cause of the problem. My use of ConvertTo-Json is the same. E.g. $webconfigs = ConvertTo-Json @($vm.WebConfigs ) However, how I run the command give two different result. 1. Works: . ("D:\Utilities\ExecuteUpdateConfig.ps1") -DsnConfigsAsString $dsnconfigs -WebConfigsAsString $webconfigs <<< works 2. Does not work: powershell.exe -ExecutionPolicy Unrestricted -Command "D:\Utilities\\ExecuteUpdateConfig.ps1 -DsnConfigsAsString $dsnconfigs -WebConfigsAsString $webconfigs" – csamleong Mar 25 '20 at 07:40
  • The reason dot-sourcing works but calling powershell.exe fails is possibly because your $dsnconfigs has line breaks in it. Dot sourcing will pass the string in verbatim, but calling powershell.exe will leave you at the mercy of the command line parser, which is pontentially just parsing everything up to the first line break in the value of $dsnconfig - in this case the string “[“. Try adding some logging to write out the values of your parameters and see what they look like when using dot sourcing vs powershell.exe. – mclayton Mar 25 '20 at 23:36

1 Answers1

2

(Expanded from my comment ^^^)

There's two reasons dot-sourcing works with your script, but calling powershell.exe fails:

  • dot-sourcing a script means it executes within the existing powershell session and will pass the value into your script as a variable reference, but calling powershell.exe will leave you at the mercy of the command line parser to interpret the value of the string variable

  • your $dsnConfigs variable has line breaks and other characters (e.g." and ') in it, which aren't being interpreted on the command line way you expect.

To demonstrate:

PS> Set-Content "myscript.ps1" "param([string] `$JsonString)`r`nwrite-host `$JsonString; `$myVar = ConvertFrom-Json `$JsonString"

PS> $myJson = ConvertTo-Json -InputObject @()
PS> . .\myScript.ps1 $myJson
[

]

PS> powershell .\myscript.ps1 $myJson
[
ConvertFrom-Json : Invalid array passed in, ']' expected. (1): [
At C:\src\scratch\myscript.ps1:2 char:34
+ write-host $JsonString; $myVar = ConvertFrom-Json $JsonString
+                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You could use the -Compress switch with ConvertTo-Json to eliminate line breaks:

PS> $myJson = ConvertTo-Json -InputObject @() -Compress
PS> powershell .\MyScript.ps1 $myJson
[]

but that still won't save you from other issues with the command line parser:

PS> $myJson = ConvertTo-Json -InputObject @("aaa") -Compress
PS> powershell .\MyScript.ps1 $myJson
[aaa]
ConvertFrom-Json : Invalid JSON primitive: aaa.
At C:\src\scratch\myscript.ps1:2 char:34
+ write-host $JsonString; $myVar = ConvertFrom-Json $JsonString
+                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Per this answer - passing JSON string via command line - you could base64 encode the json string, pass the base64 string as the parameter and then decode it inside your script:

PS> $myJson = ConvertTo-Json -InputObject @("aaa")
PS> $utf8   = [System.Text.Encoding]::UTF8.GetBytes($myJson)
PS> $base64 = [System.Convert]::ToBase64String($utf8)
PS> powershell .\MyScript.ps1 $base64
WyJhYWEiXQ==
["aaa"]

with myscript.ps1 now looking like this to decode the base64 string back into the original json:

myscript.ps1

param([string] $Base64Json)
write-host $Base64Json;

$utf8 = [System.Convert]::FromBase64String($Base64Json);
$json = [System.Text.Encoding]::UTF8.GetString($utf8);
write-host $json

$myVar = ConvertFrom-Json $json

Hope this helps...

mclayton
  • 8,025
  • 2
  • 21
  • 26
  • Thanks. It works as you have answered it. I tried different approach before I saw your post, I manually replace line feed, and space to empty string, and then added quote back in my script that I passed into, and that is not an elegant fix, just a workaround. Not to mention i still have problem converting back to json object. – csamleong Mar 26 '20 at 12:22