Invoke-WebRequest
The
Invoke-WebRequest
cmdlet sends HTTP, HTTPS, FTP, and FILE requests to a web page or web service. It parses the response and returns collections of forms, links, images, and other significant HTML elements.
Invoke-WebRequest is the multi-purpose cmdlet to use for web requests, it can talk to API’s, static pages, RSS feeds, etc. It doesn’t do any translation or conversion to the response it receives from the server.
$response = Invoke-WebRequest -Uri "https://www.binarymethod.com"
$response
StatusCode : 200 StatusDescription : OK Content : <!DOCTYPE html> ... RawContent : HTTP/1.1 200 OK Connection: keep-alive Strict-Transport-Security: max-age=31536000 Accept-Ranges: bytes Content-Length: 21148 Content-Type: text/html Date: Wed, 29 Jul 2020 14:28:22 GMT ETag: "... Forms : {} Headers : {[Connection, keep-alive], [Strict-Transport-Security, max-age=31536000], [Accept-Ranges, bytes], [Content-Length, 21148]...} Images : {@{innerHTML=; innerText=; outerHTML=<img title="Home" alt="avatar" src="/images/avatar.png">; outerText=; tagName=IMG; title=Home; alt=avatar; src=/images/avatar.png}} InputFields : {} Links : {@{innerHTML=...} ParsedHtml : mshtml.HTMLDocumentClass RawContentLength : 21148
This response contains the information you would expect during an HTTPs request/response exchange. You have the Status Code, Content, Header information and some other items that may prove useful. The content in this response is the raw HTML for the page, so you could potentially parse the html to pick out information.
Can I Access a ReST API With Invoke-WebRequest
Yes! I actually prefer to use Invoke-WebRequest as it gives you the actual Response object which contains all the details you need. For example, some ReST APIs return ETags, or Pages in the Response Header. The below example access a very quick ReST API I wrote in Python using FastAPI.
$response = Invoke-WebRequest -Uri "http://127.0.0.1:8000/users/"
$response
StatusCode : 200 StatusDescription : OK Content : [{"id":1,"name":"User Alpha","username":"usera"},{"id":2,"name":"User Omega","username":"usero"}] RawContent : HTTP/1.1 200 OK Content-Length: 97 Content-Type: application/json Date: Wed, 29 Jul 2020 14:35:28 GMT Server: uvicorn [{"id":1,"name":"User Alpha","username":"usera"},{"id":2,"name":"User Omega... Forms : {} Headers : {[Content-Length, 97], [Content-Type, application/json], [Date, Wed, 29 Jul 2020 14:35:28 GMT], [Server, uvicorn]} Images : {} InputFields : {} Links : {} ParsedHtml : mshtml.HTMLDocumentClass RawContentLength : 97
The information we want from the API is within the Content property
$response.Content
[{"id":1,"name":"User Alpha","username":"usera"},{"id":2,"name":"User Omega","username":"usero"}]
Great we have the response content but how do we use it. Most ReST API’s return data in JSON, luckily PowerShell supports JSON and we can get the actual data object by converting from JSON like so
$data = $response.Content | ConvertFrom-Json
$data
id name username
-- ---- --------
1 User Alpha usera
2 User Omega usero
Invoke-RestMethod
The
Invoke-RestMethod
cmdlet sends HTTP and HTTPS requests to Representational State Transfer (REST) web services that returns richly structured data.
Invoke-RestMethod is basically a wrapper cmdlet around Invoke-WebRequest. Invoke-RestMethod does some automatic conversion for you. If the API you are consuming returns JSON then Invoke-RestMethod will return a PowerShell Object which is a result of JSON conversion.
$response = Invoke-RestMethod -Uri "http://127.0.0.1:8000/users/"
$response
id name username
-- ---- --------
1 User Alpha usera
2 User Omega usero
As you can see the $response variable is a PSObject you can start using right away, no need for a manual conversion.
Unfortunately the Status Code and Headers are missing, most times this is ok. It’s a standard that 200 is Ok, 201 is Created, 400 causes an error etc. It’s almost safe to assume when your command works and returns an object that all is ok. I only say almost because not everyone adheres to standards and there may be some off the wall edge cases. Headers are important because some APIs provide ETags to help with caching, a Pages header to tell how many pages of objects there are, or a more common one is API versioning/obsolete flags.
Can I Access an HTML Page With Invoke-RestMethod?
Actually…you can! Invoke-RestMethod doesn’t do any conversion for the HTML content, just returns a string but this could still be useful depending on your use case.
$response = Invoke-RestMethod -Uri "https://www.binarymethod.com"
$response
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content="Hugo 0.70.0" />
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Binarymethod</title><meta name="viewport" content="width=device-width, initial-scale=1.0">
...
When to Use Invoke-RestMethod Over Invoke-WebRequest
My personal opinion is never, but I’m a control freak. I like having all the information Invoke-WebRequest provides, but telling you it’s the best way would not be honest. The best way is the way that fits the requirements of your script.
For example, my colleague Jordan Benzing recently pointed me at the API provided by ICanHazDadJoke. This is a perfect use case for Invoke-RestMethod!
Looking at the API Documentation you can see it’s a very straight forward API, no response headers to worry about, no odd ball status codes to take in to account. I would use Invoke-RestMethod for this like so
Invoke-RestMethod -Uri "https://icanhazdadjoke.com/" -Headers @{Accept = 'application/json'}
id : FdN7wcxAskb
joke : They're making a movie about clocks. It's about time
status : 200
Easy peasy, no fuss! This is the type of API Invoke-RestMethod is great for.
The Microsoft Graph API, however, is not something I would use Invoke-RestMethod for. I would use Invoke-WebRequest for any endpoint I consume.
I hope this helps clear up some of the confusion about when to use Invoke-WebRequest or Invoke-RestMethod. Invoke-RestMethod is perfect for quick APIs that have no special response information such as Headers or Status Codes, whereas Invoke-WebRequest gives you full access to the Response object and all the details it provides.