0

I currently have an Azure DevOps pipeline that makes a REST API call which returns an object of parameters:

@{location=eastus2; envName=sandbox; ...}

My PowerShell script looks like:

steps:
  - powershell: |
      $uri = "https://dev.azure.com/{org}/{proj}/_apis/build/latest/{buildId}?branchName={name}&api-version=6.0-preview.1"
      $token = [System.Text.Encoding]::UTF8.GetBytes("$(System.AccessToken)" + ":")
      $base64 = [System.Convert]::ToBase64String($token)

      $basicAuth = [string]::Format("Basic {0}", $base64)
      $headers = @{ Authorization = $basicAuth }
      
      $result = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -ContentType application/json
      $params = $result.templateParameters

      Write-Host "##vso[task.setvariable variable=paramObj]$params"
  - powershell: |
      Write-Host $(paramObj)

In this first PS task I can use $params.location which will return eastus2. However, in the second PS task I get an error saying:

Line |
   2 |  Write-Host @{location=eastus2; envName=Sandbox; sqlDBAutoPauseDatabas …
     |                        ~~~~~~~
     | The term 'eastus2' is not recognized as a name of a cmdlet,
     | function, script file, or executable program. Check the
     | spelling of the name, or if a path was included, verify that
     | the path is correct and try again.

Since my API call has an object of parameters, I'm trying to pass that parameter object to another task where I can then use those values. In the second task seen above I can't log the result, so of course trying to use dot notation, or Select-Object all results in the same error.

I was also attempting to iterate over the parameters object so I could do something like:

steps:
  - powershell: |
      ...
      $params = $result.templateParameters

      foreach ($param in $params) {
        Write-Host "##vso[task.setvariable variable=test]$param.Value"
      }
  - powershell: |
      Write-Host $(location)

But I haven't been able to figure this out either as I keep getting the same error listed above. How do I access the parameter values in a second script without hardcoding it?

agw2021
  • 266
  • 2
  • 22
  • Change `"##vso[task.setvariable variable=test]$param.Value"` to `"##vso[task.setvariable variable=test]$($param.Value)"` <- otherwise PowerShell tries to expand `$param` on it's own (without evaluating `.Value`) – Mathias R. Jessen Dec 29 '21 at 17:08
  • In short: In order to embed _expressions_ in an expandable string (`"..."`), you must enclose them in `$(...)`. Notably, this includes property and indexed access (e.g., `$($var.property)`, `$($var[0])`). Only variables _as a whole_ do not require this (e.g., `$var`, `$env:USERNAME`). See [this answer](https://stackoverflow.com/a/40445998/45375) to the linked duplicate. – mklement0 Dec 29 '21 at 17:23

1 Answers1

2

You've encountered the magic of expandable string parsing in PowerShell.

When you place a variable expression, like $param, inside an expandable string (a string literal bounded with " double quotes), PowerShell will attempt to resolve it's value for you - but nothing more than that - it's not going to attempt to evaluate member invocation, like $param.Value, for example.

So a string literal like this:

"$param.Value"

Will expand to:

"@{location=eastus2;...}.Value"

Notice that $params was evaluated and it's value substituted in the string, and the .Value-part is completely ignored.

To force PowerShell to evaluate expressions more complicated than simple variable references, use the $() subexpression operator:

Write-Host "##vso[task.setvariable variable=test]$($param.Value)"
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206