powershellazure-powershellpowershell-5.1powershell-7.4

Powershell 5 vs 7 different output


I have a strange case running something in Powershell 7 returns the expected result, but in Powershell 5 it doesn't unless you break op the line in multiple assignments

Does anybody have an idea what goes wrong?

$wrong = az role assignment list --all --query "[? contains(scope,'/subscriptions/')]" | ConvertFrom-JSON | Select-Object *, @{n='Key';e={$_.scope+"|"+$_.roleDefinitionName+"|"+$_.principalName}}  | Select -expand Key
Write-Host Wrong
$wrong[0]

$right = az role assignment list --all --query "[? contains(scope,'/subscriptions/')]" | ConvertFrom-JSON 
$right = $right | Select-Object *, @{n='Key';e={$_.scope+"|"+$_.roleDefinitionName+"|"+$_.principalName}}  
$right = $right | Select -expand Key
Write-Host Right
$right[0]

$wrong and $right are both correct in PowerShell 7 (Azure Portal Cloud Shell), but $wrong is giving the incorrect result in PowerShell 5 (from a DevOps pipeline)...

Wrong
/subscriptions/***/resourceGroups/***/providers/Microsoft.ApiManagement/service/***
Right
/subscriptions/***/resourceGroups/***/providers/Microsoft.ApiManagement/service/***|Owner|

The whole string concatenation done in the Select-Object command for new property Key only works when splitting the commands to multiple lines.

I'm quite new to PowerShell and have a hard time figuring out all the quirks...


Solution

  • ConvertFrom-Json in PowerShell 5 (and, iirc earlier versions of 7.x), defaults to request that the pipeline processor not enumerate array output - meaning that if you pass a JSON document with a list/array as the root object to ConvertFrom-JSON, it'll output the whole array object as one thing:

    PS ~> '[1,2,3,4,5]' |ConvertFrom-JSON |ForEach-Object {"We received 1 item: '$_'"}
    We received 1 item: '1 2 3 4 5'
    

    You can counter this behavior by either breaking up the pipeline into two immediately downstream from ConvertFrom-JSON (as you've found yourself) - or you can pipe the output to a command that enumerates the array anyways (here using Write-Output):

    PS ~> '[1,2,3,4,5]' |ConvertFrom-JSON |Write-Output |ForEach-Object {"We received 1 item: '$_'"}
    We received 1 item: '1'
    We received 1 item: '2'
    We received 1 item: '3'
    We received 1 item: '4'
    We received 1 item: '5'
    

    I should note that the 7.4 version of ConvertFrom-JSON also has the ability to override this behavior via the -NoEnumerate switch parameter, so that you get the same behavior as in 5:

    PS ~> '[1,2,3,4,5]' |ConvertFrom-JSON -NoEnumerate |ForEach-Object { "We received 1 item: '$_'"}
    We received 1 item: '1 2 3 4 5'