Search code examples
powershellvariablesstring-interpolationpowershell-7.0

how do I use powershell variable in AWS CLI command


I am trying to run AWS CLI in Powershell 7 with a JSON string as parameter. AWS docs make it seem straightforward:

aws route53 ... --change-batch '{"Changes":[{"Action":"UPSERT"}]}'

however, I get an error:

Error parsing parameter '--change-batch': Invalid JSON: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) JSON received: {Changes:[{Action:UPSERT ...

So, it looks like double quotes are stripped at some point.

If I escape the quotes, command works:

aws route53 ... --change-batch '{\"Changes\":[{\"Action\":\"UPSERT\"}]}'

Now, I am trying to use a variable:

aws route53 ... --change-batch '{\"Changes\":[{\"Action\":\"UPSERT\", \"Value\":\"$MY_IP\"}]}'

but the variable is not resolved; that is, $MY_IP is passed to the server. I tried putting the whole string in double-quotes - but it looks like with double quotes internal quotes are removed even if I escape them. Also with backticks - it works as command continuation. I am looking at Microsoft docs - but the results I am getting are clearly different.

I don't think the problem has anything to do with AWS, but AWS CLI gave me a super twisted test case.


Solution

    • Fundamentally, PowerShell only performs string interpolation in double-quoted strings ("..."), also known as expandable strings: see this answer.

    • The surprising and unfortunate need to explicitly \-escape embedded " chars. in arguments for external programs applies irrespective of which quoting style you use, up to at least PowerShell 7.2.x: see this answer.

    Thus, since you need a "..." string for string interpolation (so that variable reference $MY_IP is expanded to its value), you need two layers of escaping of embedded ":

    • `" (or "") in order to satisfy PowerShell's syntax requirements...

    • ... and this escape sequence must additionally be escaped with \ in order for the external program to see the embedded " as such.

    Therefore:

    # Inside "...", pass-through " must be escaped as \`" (sic)
    aws route53 ... --change-batch "{\`"Changes\`":[{\`"Action\`":\"`UPSERT\`", \`"Value\`":\`"$MY_IP\`"}]}"
    

    You can ease the pain with the help of an (expandable) here-string, which obviates the need to escape the embedded " for PowerShell's sake:

    # A here-string obviates the need to escape " for PowerShell's sake.
    aws route53 ... --change-batch @"
    {\"Changes\":[{\"Action\":\"UPSERT\", \"Value\":\"$MY_IP\"}]}
    "@
    

    Another option is to perform the \-escaping after the fact, programmatically:

    aws route53 ... --change-batch (@"
    {"Changes":[{"Action":"UPSERT", "Value":"$MY_IP"}]}
    "@ -replace '"', '\"')