${...} is a variable reference, and whatever ... is is taken verbatim as a variable name.
- Enclosing a vairable name in
{...} is typically not necessary, but is required in two cases: (a) if a variable name contains special characters and/or (b) in the context of an expandable string ("..."), to disambiguate the variable name from subsequent characters - see this answer
In order to embed an expression or command as part of an argument, use $(...), the subexpression operator, and preferably enclose the entire argument in "..." - that way, the entire argument is unambiguously passed as a single argument, whereas an unquoted token that starts with a $(...) subexpression would be passed as (at least) two arguments (see this answer).
- If an expression or command by itself forms an argument,
(...), the grouping operator is sufficient and usually preferable - see this answer
Therefore:
[CmdletBinding()]
param(
[Parameter(Mandatory, HelpMessage="password?")]
[SecureString] $password
)
# Note the use of $(...) and the enclosure of the whole argument in "..."
do-something "password=$([Runtime.InteropServices.Marshal]::PtrToStringBSTR([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)))"
Also note:
On Windows it doesn't make a difference (and on Unix [securestring] instances offer virtually no protection and should be avoided altogether), but it should be [Runtime.InteropServices.Marshal]::PtrToStringBSTR(), not [Runtime.InteropServices.Marshal]::PtrToStringAuto()
As Santiago Squarzon points out, there is an easier way to convert a SecureString instance to its plain-text equivalent (which should generally be avoided[1], however, and, more fundamentally, use of [securestring] in new projects is discouraged[2]):
[pscredential]::new('unused', $password).GetNetworkCredential().Password
[1] A plain-text representation of a password stored in a .NET string lingers in memory for an unspecified time that you cannot control. More specifically, if it is part of a process command line, as in your case, it can be discovered that way. Of course, if the CLI you're targeting offers no way to authenticate other than with a plain-text password, you have no other choice.
[2] See this answer, which links to this .NET platform-compatibility recommendation.
dosomething password=${ }usedosomething password=( ). Also[System.Net.NetworkCredential]::new('', $pwd).Passwordis a lot easier than Marshal.password=...". Indeed, that was the problem. I copied the solution which had been written based upon my example, not on my original code. My mistake. "[T]his has turned into one big mess of a question." That it has. The original question was focused on value interpolation (which you answered with a great explanation—thank you), but then we got off into a discussion on Powershell's handling of secure strings, and that is a$(mess * 2). I'll tidy up the question.$($mess * 2). (Sigh.) It will take me a while to get the hang of this, I think.