Working with a client recently and needed to check connectivity to a web service. I wanted to make a few HTTP requests. Normally I’d fire up curl and paste a URL with basic auth credentials:
curl -u $USERNAME:$PASSWORD -k https://api.example.com/some/endpoint
But… they were on Windows. No problem, fire up chocolately and install curl
choco install curl
Ah… this server is locked down and no way they’re going to fill out the paperwork (in triplicate) to install . Someone said that PowerShell has curl installed. But wait a minute…
Someone has played a nasty trick on me.
I can’t use curl, but PowerShell has something not quite, but almost entirely unlike curl. Let’s see what we can do with Invoke-WebRequest.
Ok, so you can call a URL with:
Invoke-WebRequest -Uri https://api.example.com/some/endpoint
It turns out that passing Basic Authentication credentials is trickier. But wait! stackoverflow comes to the rescue(-ish).
You have a -Credential flag, but all that does is open up a Windows dialog to enter them. Not very automatable. (automatible? odd-tomato-bull?)
Basic authentication is really just an HTTP header:
GET /some/endpoint HTTP/1.1 Authorization: Basic SSBsb3ZlIEtlbHNleSBGb3g=
And the credentials are just a base64 encoded string containing text in the format
username:password
One last thing though, you need to escape the colon in your credentials. Apparently backtick is the escape character in Powershell:
$credentials = "$username`:$password"
And you can pass an array of headers with Invoke-WebRequest. (backtick also allows continuation on the next line)
Invoke-WebRequest ` -Uri https://api.example.com/some/endpoint ` -Headers @{ Authorization = "Basic $encodedCredentials" }
So now I’m getting somewhere.
The only step left is to figure out how to Base64Encode with PowerShell. I’m not sure where I found it, but this will do the trick:
$encodedCredentials = ` [System.Convert]::ToBase64String( ` [System.Text.Encoding]::ASCII.GetBytes($credentials))
Remember folks, only you can prevent github from sharing your credentials. Use an environment variable, and don’t put your password in powershell your script:
Here’s how you get an environment variable in Powershell
Get-ChildItem Env:USERNAME
Finally, here is my full script:
$username = $(Get-ChildItem Env:USERNAME).value $password = $(Get-ChildItem Env:PASSWORD).value $credentials = "$username`:$password" $encodedCredentials = ` [System.Convert]::ToBase64String( ` [System.Text.Encoding]::ASCII.GetBytes($credentials)) Invoke-WebRequest ` -Uri https://api.example.com/some/endpoint ` -Headers @{ Authorization = "Basic $encodedCredentials" }
But wait, there’s more!
Now that I’ve got my response I can actually use Powershell to parse the JSON response and access it using ConvertFrom-Json.:
Given the following JSON
[{ "contact": { "name": "Aaron Evans", "website": { "url": "http://one-shore.com" }, "blog": { "url": "https://fijiaaron.wordpress.com" } }}]
I could parse it like this:
$response = $(Invoke-WebRequest -Uri "$endpoint" -Headers $headers) $contacts = $(ConvertFrom-Json -InputObject $response.Content) Invoke-WebRequest -Uri $contacts[0].contact.blog.url
Please don’t flood my blog with hits. I haven’t monetized it yet.
See the whole thing with this gist:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$username = $(Get-ChildItem Env:USERNAME).value | |
$password = $(Get-ChildItem Env:PASSWORD).value | |
$credentials = "$username`:$password" | |
$encodedCredentials = ` | |
[System.Convert]::ToBase64String( ` | |
[System.Text.Encoding]::ASCII.GetBytes($credentials)) | |
Invoke-WebRequest ` | |
–Uri https://api.example.com/some/endpoint ` | |
–Headers @{ Authorization = "Basic $encodedCredentials" } | |
$response = $(Invoke-WebRequest –Uri "$endpoint" –Headers $headers) | |
$data = $(ConvertFrom-Json –InputObject $response.Content) |