5

I am currently trying to automate new user creation in our Zendesk ticketing system using Powershell and Curl. The problem I am running into is that the curl json body is enclosed by single quotes and I need to reference a variable inside that body. Here is what I have:

$Firstname = "Test"
$Lastname = "User"
$email= '[email protected]'   
curl.exe https://mydomain.zendesk.com/api/v2/users.json -H "Content-Type: application/json" -X POST -d '{\"user\": {\"name\": \"$Firstname $Lastname\", \"email\": \"$email\"}}' -v -u myuser:mypass

This works fine if I type in regular text values inside the json, but how can I get it to recognize the variables $Firstname, $Lastname and $email?

2 Answers 2

15

tl;dr

  • Your primary problem is the use of a verbatim string (single-quoted, i.e. '...'), inside of which variable references such as $Firstname are not interpolated.

  • You need an expandable (interpolating), string (double-quoted, i.e. "...") instead, in which any embedded " characters must then also be escaped for PowerShell's sake (either as `" or ""); however, this additional escaping need can be avoided with the use of the here-string expandable string variant (@"<newline>...<newline>"@), as shown below.

  • See the bottom section for a better, hashtable-based alternative solution.


Therefore, try the following:

$Firstname = "Test"
$Lastname = "User"
$email= '[email protected]'   
$json=@"
{\"user\": {\"name\": \"$Firstname $Lastname\", \"email\": \"$email\"}}
"@
curl.exe https://mydomain.zendesk.com/api/v2/users.json -d $json -H 'Content-Type: application/json' -X POST -v -u myuser:mypass

Using a double-quoted (expandable) here-string (@"<newline>...<newline>"@) makes specifying embedded " instances easy (no escaping for the sake of PowerShell's own syntax required), while still expanding variable references.

You're clearly aware of the - unfortunate - additional need to \-escape the " instances - update: no longer needed in PowerShell (Core) 7 v7.3+; see this answer - but just to explain why that is needed:

When passing arguments to an external program, PowerShell, after its own parsing and interpolation, wraps those resulting arguments that contain spaces in double quotes ("...") when concatenating them to form the process command line to launch the external utility with. Unfortunately, it does so without escaping any embedded " characters, which in effect results in their getting discarded; the only way to preserve them is to \-escape them - see this answer for more information.

If you wanted to do it inline with a regular double-quoted string, you'd have to escape the " instances for PowerShell too (as `"), resulting in the awkward combination \`":

"{\`"user\`": {\`"name\`": \`"$Firstname $Lastname\`", \`"email\`": \`"$email\`"}}"

Alternative:

Ryan himself points out in a comment that using a hashtable is the better alternative for constructing the data and then converting it to JSON with ConvertTo-Json and feeding it to curl via stdin is an alternative that avoids the quoting headaches:

# Create data as PS hashtable literal.
$data = @{ user = @{ name = "$Firstname $Lastname"; email = "[email protected]" } }

# Convert to JSON with ConvertTo-Json and pipe to `curl` via *stdin* (-d '@-')
$data | 
  ConvertTo-Json -Compress |
  curl.exe mydomain.zendesk.com/api/v2/users.json -d '@-' -H "Content-Type: application/json" -X POST -v -u myuser:mypass
Sign up to request clarification or add additional context in comments.

5 Comments

I have the inline version $body_j="{`"user`":`"ti`",`"pass`":`"passw`"}", then Invoke-WebRequest -Uri http://localhost:5000/login -Method Post -Body $body_j -ContentType 'application/json' and get Invoke-WebRequest : SyntaxError: Unexpected token \ in JSON at position 1
@Timo, I don't see any \ in your command, so I don't have an explanation, but note that this question is about curl.exe, not Invoke-WebRequest and that comments aren't the right place for troubleshooting, so I encourage you to ask a new question.
@mklement0, Very nice answer, it has helped me today, only thing somehow firstname and lastname was not working for me so I concatenated them into a single one and that worked. Didn't go too much deep but it worked, thanks for sharing cheers.
Glad to hear it, @RavinderSingh13. I've just updated the answer to improve the explanation for why \-escaping is needed, and to point out that it is no longer needed in PowerShell (Core) v7.3 and above.
I wasn't having this issue, but the \`" sequence was magic for getting curl and powershell to actually send the quotes in the payload. Thanks.
1

I think we can use here-string as json body for Invoke-RestMethod as below

$bufferTime = 5
$requestBody = @"
{
"size": 0,
    "aggs": {
    "last_x_min": {
        "filter": {
        "range": {
            "@timestamp": {
            "gte": "now-$($bufferTime)m",
            "lte": "now"
            }
        }
        },
        "aggs": {
        "value_agg": {
            "avg": {
            "field": "time-taken"
            }
        }
        }
    }
    }
}
"@

$esResponse = Invoke-RestMethod -URI http://locahost:9200 -TimeoutSec 15 -Method Post -ContentType 'application/json' -Body $requestBody

This is the script I use to query Elasticsearch. No need to escape double quotes.

1 Comment

This works without \ -escaping the embedded " instances, because you're calling a cmdlet (a PowerShell-native command, Invoke-RestMethod) rather than an external utility (such as curl in the OP's case). While I wish that these two invocation scenarios didn't require different quoting, they unfortunately do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.