Powershell sub-expression syntax used by TFS/Azure Devops - powershell

TFS/Azure DevOps allows you to reference custom variables and built in build/release variables using the following syntax:
$(variable_name)
For example, if I wanted to grab the build definition name, I would grab the value from the following sub-expression:
$(Build.DefinitionName)
But, as far as I can tell, the above syntax is not a valid powershell sub-expression.
If "Build.DefinitionName" was a variable, it would need to look like:
$($Build.DefinitionName)
If it were a collection/hashtable, it would use curly braces:
${Build.DefinitionName}
If it were an object with a static property, it would be:
${Build::DefinitionName}
So what the heck is "Build" in the above example? Is TFS/VSTS/Azure DevOps doing some magic string replacement here or something?

It's not a Powershell language element, it's a simple TFS/VSTS placeholder syntax, just like SQLCMD uses: https://learn.microsoft.com/en-us/sql/relational-databases/scripting/sqlcmd-use-with-scripting-variables?view=sql-server-2017
TFS/VSTS inserts the values for these placeholders before invoking your Powershell tasks.

Build/release variables are made available to processes running in the scope of a build/release in the form of environment variables.
As an example, the build definition name is available the same way you'd access any other environment variable in PowerShell: $env:BUILD_DEFINITIONNAME. Note that periods are replaced with underscores.
The exception is secrets. Anything defined as a secret will have to be explicitly passed in to the consuming script, as they are not populated as environment variables.

Related

In what scenario was Invoke-Expression designed to be used?

9/10 times if you are trying to use the Invoke-Expression cmdlet, there is a better way. Building the arguments to a command dynamically? Use an array of arguments. Building the arguments to a cmdlet? Use splatting with an array or hashtable. Your command has a space in the path to it? Use the call operator (&).
This might seem open ended, but Invoke-Expression is an easily accessible cmdlet where the answer is almost always to never use it. But the cmdlet exists for a reason, is not deprecated, and most criticisms of its use state something similar to, "it's almost never the right answer", but never states when it is acceptable to use it. In what case is it acceptable to use Invoke-Expression? Or to word it a bit less openly, how was Invoke-Expression designed to be used?
To quote from a PowerShell team blog post titled Invoke-Expression considered harmful (emphasis added):
The bottom line: Invoke-Expression is a powerful and useful command for some scenarios such as creating new scripts at runtime, but in general, if you find yourself using Invoke-Expression, you should ask yourself, or maybe a respected colleague if there is a better way.
EBGreen notes:
Or to phrase it another way, It [Invoke-Expression] is ok to use as long as a user is never involved in any part of generating the string that will be invoked. But even then, not using it will enforce better habits than using it would.
In short:
As a matter of habit, always consider a different (usually more robust and secure) solution first.
If you do find that Invoke-Expression is your only choice, carefully consider the security implications: if a string from an (untrusted) outside source (e.g., user input) is passed directly to Invoke-Expression, arbitrary commands may be executed.
Therefore: Only use Invoke-Expression if you fully control or implicitly trust the input.
Note: As of Windows PowerShell v5.1 / PowerShell Core v6.1.0, the official Invoke-Expression help topic doesn't provide such guidance; this GitHub issue suggests rectifying that.
Rare examples of justified (safe) use of Invoke-Expression:
Creating PSv5+ custom classes dynamically:
so that the class can be used in a remote session.
so that the set of properties can be created based on conditions at runtime.
Using Invoke-Expression in combination with Write-Output:
to parse a string with embedded quoting, with extra precautions.
to parse command lines stored in a file, if trusted.
Using Invoke-Expression for nested property access:
via a property path stored in a string.

VSTS Nested Variables

I'm trying to accomplish a cascading variable, where when one design time variable is set, it cascades to other variables. Is this possible? I've tried with dot notation, I've tried without dot notation, and I've tried to do it in the task:
I also tried this task plugin, but my agents are on 2.0 and the task doesn't run.
Variable Toolbox Task
Any ideas?
Answer:
You can override these variables at the environment level. I will have to expand these variables in my script manually and then set them to the resolved values in this script.
You can set the variable value through Logging Command during the build:
Add PowerShell task
Script: (Need to use _ instead if the variable contains ., such as Server.Dev=> Server_Dev)
$v= [Environment]::GetEnvironmentVariable("Server_$env:Environment")
Write-Host ("##vso[task.setvariable variable=Server;]$v")
Then the following tasks could use Server variable with corresponding value
Instead of having different variables for each environment, use the same variable but override it at the environment level.

TFS variable update, variable split

I have a questions about Team Foundation Server variables:
Is there a way to update/define TFS variables from a PowerShell script (runs as a custom build step)?
When I define a variable value like "a,b,c" and I want to use it in a build step which wants it as a multiline value (separated by \n) is there a way to split it online, like Projects: $(myVariable).split(',') ?
Can I define multiline-type variable value?
Variables are name-value pairs defined by you or provided by Build or Release Management. You can use variables as inputs and in your scripts. Check: https://www.visualstudio.com/en-us/docs/build/define/variables
One variable usually has one value. But your requirement should can be achieved with Poswershell script, you need to refer to Powershell documentation to implement how to split values.
No. Never seen such definition.

Eclipse variable substitution syntax rules?

In many environments, variable substitution syntax provides for substituting or testing the final value of the variable.
From bash ${foo#t*is}, for instance, is delete regex t*is in the variable.
Is there anything like this available in eclipse?
Specifically, the variables that the framework uses to control and invoke the underlying tools - the ones you see when you list system variables, environment variable, build environment variables, etc.
Accessing these for use in command line invocations is ${VARIABLE_NAME}, and there is a syntax for modifying them - I have seen ${PROJECT_NAME:MyProject} for example.
The question concerns the documentation and full capability of this ability.

powershell templating

I've got an app config file that contains two types of templated variables. The first set will be populated at build time by some powershell scripts. The second remain within the config file but are used at runtime.
So, under source control, the file might look like:
<property name="$($settings.elementName)" value="${valueThatIsSubstitutedAtRuntime}" />
After a build I'd like it to be:
<property name="settingFromPosh" value="${valueThatIsSubstitutedAtRuntime}" />
But unfortunately it's coming out as
<property name="settingFromPosh" value="" />
I'm using the a slightly modified version of the templating method described in Powershell for Developers. Basically, I'm using populating a ScriptBlock with the variables to substitute, reading the template and calling Invoke-Expression with the template content. The script it successfully substituting values it can find but also wiping out any it can't.
I'm reluctant to use regex token replacements because I want the full power of ps within the template. Also, I prefer not to introduce different variable tokens for the values I don't want populated during the build.
Is there a way to leave variables in place that can't be substituted or perhaps escape them so they're ignore?
Put your string in single-quotes so that PowerShell doesn't try to expand any variables in it e.g.:
<property name="$($settings.elementName)" value='${valueThatIsSubstitutedAtRuntime}' />
Then you can use $ExecutionContext.InvokeCommand.ExpandString(...) to expand the string at runtime e.g.:
$foo = 'bar'
$str = '$foo'
$ExecutionContext.InvokeCommand.ExpandString($str)
Are you sure this:
"${valueThatIsSubstitutedAtRuntime}"
shouldn't be:
&{valueThatIsSubstitutedAtRuntime}
That first one is the syntax for a braced variable. I think it's looking for a variable named "whatever is between the braces" (and not finding it, so it returns a null string). If it's a script block, I'd think you'd want to invoke it.