Azure-pipelines-agent: [YAML] Cannot get PowerShell script to evaluate template expression

Created on 17 Aug 2018  路  17Comments  路  Source: microsoft/azure-pipelines-agent

I'm trying to get a powershell script inside a reusable step to recognize a parameter in a template expression and no combination of values seems to work.

parameters:
    someVar: 1

steps:
- powershell: |
      $someVar = ${{ parameters.someVar }}

The above results in an error in PowerShell saying variables containing { should be changed to `{.

Have you tried trouble shooting?

Yes

Agent Version and Platform

Version of your agent? 2.102.0/2.100.1/...

OS of the machine running the agent? Windows

VSTS Type and Version

VisualStudio.com or On-Prem TFS? VSTS

If VisualStudio.com, what is your account name? https://fsmb.visualstudio.com

What's not working?

Cannot get template expressions or parameters to be usable in PowerShell steps.

I have also tried:

$someVar = ${{ format('{0}', parameters.someVar) }}
$someVar = "${{ format('{0}', parameters.someVar) }}"
$someVar = `${{ format("{0}", parameters.someVar) }}`
$someVar = "${{ parameters.someVar }}"
$someVar = '${{ parameters.someVar }}'

In some cases it doesn't generate an error in PowerShell but it doesn't evaluate the expression either. I just get the original expression as a string.

Interestingly this properly replaces the template expression.

steps:
- powershell: "${{ format('.\\steps\\do-work.ps1 -major {0}', parameters.someVar) }}"

In this approach I'm calling a separate PowerShell script. However this does not work for my scenario because the file cannot be found. I got this sample from the vsts-tasks repo where they are doing a similar thing. But in my case I'm pulling the (reusable) step from a different repo than the actual yaml build file. So it seems like the pathing isn't correct such that the external script could be found. Hence I'm switching to a script block inside the reusable step for now.

Most helpful comment

Great to hear, this has been the most frustrating part of working with YAML so far.

All 17 comments

Also tried this.

- powershell: |
      ${{ format('$someVar= "{0}"', parameters.someVar) }}
      "${{ format('$someVar= "{0}"', parameters.someVar) }}"

Get same error about using `{

I'm running into similar problems (minus PowerShell).

Here is a complete repro:

build_impl.yml

parameters:
  value: 1

phases:
- phase: MyPhase
  steps:
  - script: echo ${{ parameters.value }}, ${value}, $(parameters.value) $(value)

build.yml

phases:

- template: build_impl.yml
  parameters: 
    value: 20

Output

${{ parameters.value }}, ${value}, $(parameters.value) $(value)

@davidbrownellMS, yes. As Eric has mentioned (in several posts ironically) template expressions only work if they start the string itself. So if you need to mix an expression with anything else then you need to use the format function. So in your case I believe this might work.

- script: ${{ format('echo {0}, {1}, {2}, {3}', parameters.value, parameters.value, parameters.value, parameters.value) }}

If the line doesn't start with ${{ then the parser doesn't look for template expressions at all.

@CoolDadTx Thank you very much! I hadn't seen that particular quirk referenced in the documentation yet - you have saved me a lot of time :).

I am struggling with the same but for executing a PowerShell script file and passing some arguments.

I got it also running with the 'format' command:

  • task: PowerShell@2
    inputs:
    targetType: 'filePath' # Optional. Options: filePath, inline
    filePath: '_Buildtest.ps1' # Required when targetType == FilePath
    arguments: "${{ format('-WIMFileName {0}-{1} -otherparam {2}', parameters.param1, parameters.param2,'parameters.param3') }}"

But I have still some issues for example when a parameter value has a space in it. Then the value should be in quotes ("). But I don't know how to do that. Simply but the yaml build parameter in quotes will break everything again. Any ideas? Is there an escape character for the format function?

@J0F3, I am not really familiar with .yml format, but I was able to give my powershell task a pipeline parameter like this :

- task: PowerShell@2
  inputs:
    targetType: 'filePath'
    filePath: ./Scripts/script.ps1 
    arguments: '-input1 ''$(parameters.param1)'''

Please notice that I use simple quote !
I also suggest you to create your complexe string (with your spaces) elsewhere so that you can simplify your yml script. Don't know if it's a good practice though ?

Does it helps ?

Thanks @SachaLhopital
Unfortunately that does seems not to work for me. The value of 'input1' is then simply '$(paramters.param1)'. So the parameter gets not resolved to the actual value.

Which actually make sense in the context of the answer from @CoolDadTx above. (If the line doesn't start with ${{ then the parser doesn't look for template expressions at all.)

I tought also to use a Task Group so to which I can simply pass the template paramters one by one. But the YAML build does not support Task Groups (yet).

@J0F3, firstly I notice you're using the PowerShell task and not the YAML PowerShell step. This probably doesn't matter and under the hood it likely uses the same task but the YAML step is a little cleaner.

As for your formatting, you put the parameter in single quotes but that should be in your format command I believe. Since this is a PS command you can wrap it in double quotes to avoid confusion. Here's what I might try. I have something similar in one of my YAML scripts but it fails for other reasons so I cannot tell whether this syntax is completely correct.

- powershell: ${{ format('.\myscript.ps1 -WIMFileName {0}-{1} -otherparam "{2}"', parameters.param1, parameters.param2,parameters.param3) }}"
   displayName: Run my script

Yes that works! Thank you @CoolDadTx :-)
I works even when using normal build variables in the first part of the format by just specify them as $Env:YourVariable

Why is it then failing your you? Would be happy to help you as well if I can. ;-)

@J0F3, mine fails because I'm using reusable steps in other repos. We have a lot of repos and we use a standardized build process. Outside YAML we use Task Groups to consistently build, test and deploy our apps. The equivalent in YAML is the reusable steps. But since we don't want to manage a copy in each repo we have a standalone repo that contains the steps and the per-repo build references them.

Referencing steps in other repos is supported and that works fine. But inside a reusable step I ran into a lot of issues with template expressions so I figured I'd just move that logic into .ps1 files and have the reusable step reference that. This is how Azure and some of the other built in YAML things work. But the problem is you have to specify a path to that file and I've yet to figure out the correct path to use to get to the .ps1 file sitting in a separate repo even though it is referenced from the (separate repo's) YAML file. It doesn't appear that the reusable steps are copied into the build directory anywhere. Or at least I've not found the correct path. So, my stuff isn't working because of a pathing issue. Once I get around that I'm going to be moving all my PS logic into separate .ps1 files and just reference them in YAML until the template expression stuff gets resolved.

We have created a new repository for all YAML related issues, please move the current issue to there.
https://github.com/Microsoft/azure-pipelines-yaml

@CoolDadTx currently the entire value must be an expression. So if you had a multi-line script you could do something like this:

- powershell: |
    ${{ format('
    $someVar1= "{0}"
    $someVar2= "{1}"
    ',
    parameters.someVar1,
    parameters.someVar2) }}

Next sprint we are rolling out support for expressions embedded within a string, to enable this:

- powershell: |
    $someVar1 = ${{ parameters.someVar1 }}
    $someVar2 = ${{ parameters.someVar2 }}

The sprint deployment will begin after next week, and takes roughly 3 weeks to roll across all accounts.

Great to hear, this has been the most frustrating part of working with YAML so far.

Is there any way to tell when our account is updated to the newest version so we know when we can switch over to this new syntax?

I assigned the template expression parameter as a PowerShell variable as suggested by CoolDadTx and ericsciple however I am having difficulty reading the variable from an inline script.

- task: PowerShell@2 inputs: arguments: "${{ format('$TestServiceName-{0}', parameters.service) }}" script: |

    write-host $($env:TestServiceName) 
    write-host $TestServiceName
    write-host $env:TestServiceName`

I have modified the syntax to match every example above and still am unable to write the value to the console. What am I missing?

@rjackowens lets troubleshoot it on a new issue. Also a full example will help.

@rjackowens lets troubleshoot it on a new issue. Also a full example will help.

Thanks for your response, I actually finally figured it out. After referencing https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/powershell?view=azure-devops I found that inline PowerShell scripts ignore passed arguments. I modified the task targetType to point to a file path and was then able to read the variable without issue.

Here is my code if any has a similar issue:

- task: PowerShell@2 displayName: 'PowerShell Script' inputs: targetType: 'filePath' filePath: projectVersion.ps1 arguments: ${{ parameters.service }}

Was this page helpful?
0 / 5 - 0 ratings