I am writing powershell script in VSCode.
I keep running into the stated issues, when trying to convert the form-data to x-www-form-urlencoded format.
I have a form data as below:
$formData = @{
'grant_type' = 'password'
'scope' = 'xyz'
'client_id' = 'xyzAPIClient'
'client_secret' = 'xyzabcdef'
'username' = 'user1@xyz.com'
'password' = 'password1'
}
I tried to convert it to x-www-form-urlencoded format, in order to use it as below (to post a request to the server):
$formUrlEncoded = $formData | ForEach-Object {"$($_.Key)=$($_.Value)"} -join '&'
$response = Invoke-RestMethod -Uri $apiEndPoint -Method Post -Body $formUrlEncoded -Headers $headers
I run into the below issue
ForEach-Object : Cannot bind parameter 'RemainingScripts'. Cannot convert the "-join" value of type "System.String" to type
"System.Management.Automation.ScriptBlock".
At C:\Automation\automation-scripts\automation-getToken.ps1:15 char:31
+ ... oded = $formData | ForEach-Object {"$($_.Key)=$($_.Value)"} -join '&'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [ForEach-Object], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.ForEachObjectCommand
I am new to powershell and I couldn't understand what is causing this issue. Could somebody help
To explain the error you're getting, this error happens because PowerShell is assuming that -join
is being passed as argument of ForEach-Object
this is why you need to wrap your expression with the grouping operator ( )
, basically to, as explained in the documentation, "let output from a command participate in an expression". In addition, hashtables are not enumerable by default in PowerShell, you must use the .GetEnumerator()
method:
$formData = @{
'grant_type' = 'password'
'scope' = 'xyz'
'client_id' = 'xyzAPIClient'
'client_secret' = 'xyzabcdef'
'username' = 'user1@xyz.com'
'password' = 'password1'
}
$formUrlEncoded = ($formData.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join '&'
$formUrlEncoded
# Outputs:
# username=user1@xyz.com&client_id=xyzAPIClient&password=password1&scope=xyz&grant_type=password&client_secret=xyzabcdef
Taking a step back, the -Body
parameter from Invoke-RestMethod
takes IDIctionary
as input and already knows how to deal with instances implementing the interface, so it is very likely that you don't need to join the key / value pairs to form the url-encoded-string.
In summary, it is likely that this should work (as it becomes evident you're trying to get a token from the Graph API, see the examples from this answer and this answer):
$formData = @{
'grant_type' = 'password'
'scope' = 'xyz'
'client_id' = 'xyzAPIClient'
'client_secret' = 'xyzabcdef'
'username' = 'user1@xyz.com'
'password' = 'password1'
}
$response = Invoke-RestMethod -Uri $apiEndPoint -Method Post -Body $formData -Headers $headers
Side note, in case you do need to form the url-encoded-string, using the HttpUtility
APIs can be useful here:
Add-Type -AssemblyName System.Web
$query = [System.Web.HttpUtility]::ParseQueryString('')
$formData.GetEnumerator() | ForEach-Object { $query[$_.Key] = $_.Value }
# if you need URL encoded:
$query.ToString()
# username=user1%40xyz.com&client_id=xyzAPIClient&password=password1&scope=xyz&grant_type=password&client_secret=xyzabcdef
# if you need URL decoded:
[System.Web.HttpUtility]::UrlDecode($query.ToString())
# username=user1@xyz.com&client_id=xyzAPIClient&password=password1&scope=xyz&grant_type=password&client_secret=xyzabcdef