powershell templating - powershell

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.

Related

Translate PL/SQL define to TSQL

I am given a task to transition PL/SQL code to T-SQL.
Can anybody explain what SET DEFINE ON does in sqlplus and most impooirtantly how to translate it to T-SQL (I suppose using sqlcmd as a launcher?)
SET DEFINE is an SQL*Plus command setting the use and prefix for substitution variables. & is the default prefix and SET DEFINE ON resets it to this default and turns on the use of substitution variables.
So this is not a PL/SQL thing but an SQL*Plus thing.
As far as I know there's no such thing as substitution variables for sqlcmd, i.e. there's no equivalent for sqlcmd let alone T-SQL. But I might be wrong there.
'&' appears to be a token of the metalanguage used by SQL*Plus (the client, that is) to activate its "variable substitution" feature. So string literals containing '&' ("Marks & Spencer") tokens may not behave entirely as expected. SET DEFINE apparently serves to control that activation.

Powershell sub-expression syntax used by TFS/Azure Devops

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.

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.

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.

Mutt macros: How to set and then use a variable in one macro?

I'm trying to write a small mutt macro that will set a variable, and then re-use that variable in the same macro.
What I'm really trying to do is compose a message and sub in the To: address (which later I will populate dynamically).
This is in my .muttrc
set my_to = "bobby#test.com"
macro index E "<enter-command> set my_to = barry#test.com<enter>mn\$my_to"
My problem is the variable my_to never changes from bobby#... to barry#...
I believe it has to do with the $ being interpreted during the config parsing, and the manual recommends using an escape . But if I do that, only the literal "$my_to" shows up.
What am I doing wrong?
update:
I still haven't been able to set/get variables as above, but I'd missed an obvious Mutt solution to my problem ESC-e will create a new email based on the existing. Similar to the edit-as-new in Thunderbird.