Skip to the main content.

Unlocking the Power of PowerShell: Tips for Success

How To Use Web Services in PowerShell - Invoke-RestMethod Deep Dive

In part 2 we looked at Invoke-WebRequest. Today we focus on Invoke-RestMethod: this cmdlet performs the same tasks but focuses on the data: it recognizes the format and automatically returns appropriate objects. 

For example, let’s assume you want to find out where an obscure IP address is registered that you found in your logs. When you enter the URL below into your browser, you get back the registration information in a human-readable format:

https://ipinfo.io/8.8.8.8

Accessing Web Service

The same web server can also act as a “web service”; it simply changes the data type from HTML to a machine-readable format. Try this in your browser:

https://ipinfo.io/8.8.8.8/json

This time, you receive the information in JSON format:


{
  "ip": "8.8.8.8",
  "hostname": "dns.google",
  "city": "Mountain View",
  "region": "California",
  "country": "US",
  "loc": "37.4056,-122.0775",
  "org": "AS15169 Google LLC",
  "postal": "94043",
  "timezone": "America/Los_Angeles",
  "readme": "https://ipinfo.io/missingauth",
  "anycast": true
}

 

Invoke-WebRequest

Invoke-WebRequest can retrieve any web data for you, so it can work with web services as well. However, you always receive the raw data:


$ip = '8.8.8.8'
$url = "https://ipinfo.io/$ip/json"

$result = Invoke-WebRequest -UseBasicParsing -Uri $url

 

The result looks exactly like in the browser before:


$ip = '8.8.8.8'
$url = "https://ipinfo.io/$ip/json"

$result = Invoke-RestMethod -UseBasicParsing -Uri $url 

 

You would need to determine the content type yourself and then apply the appropriate conversion:


PS C:\> $result.Headers['Content-Type']
application/json; charset=utf-8

PS C:\> $formatted = $result.Content | ConvertFrom-Json

PS C:\> $formatted ip : 8.8.8.8 hostname : dns.google city : Mountain View region : California country : US loc : 37.4056,-122.0775 org : AS15169 Google LLC postal : 94043 timezone : America/Los_Angeles readme : https://ipinfo.io/missingauth anycast : True PS C:\> $formatted.hostname dns.google PS C:\> $formatted.org AS15169 Google LLC PS C:\> $formatted | Select-Object -Property HostName, Org, Country hostname org country -------- --- ------- dns.google AS15169 Google LLC US

 

Invoke-RestMethod

Invoke-RestMethod detects the content type and then applies the appropriate conversion automatically. If you are after the data returned by a web service, it is the much more convenient option. You simply exchange the cmdlet name:


$ip = '8.8.8.8'
$url = "https://ipinfo.io/$ip/json"

$result = Invoke-RestMethod -UseBasicParsing -Uri $url 

 

Now, you can directly work with the data and don’t have to worry about content types or conversions:


PS C:\> $result

ip       : 8.8.8.8
hostname : dns.google
city     : Mountain View
region   : California
country  : US
loc      : 37.4056,-122.0775
org      : AS15169 Google LLC
postal   : 94043
timezone : America/Los_Angeles
readme   : https://ipinfo.io/missingauth
anycast  : True

PS C:\> $result.hostname
dns.google

PS C:\> $result | Select-Object -Property HostName, Org, Country

hostname   org                country
--------   ---                -------
dns.google AS15169 Google LLC US     
 

Caveats and Tricks

The insight you just gained can be very helpful because the magic does not always work. For example, an administrator responsible for Lenovo servers wanted to make her life easier by automatically downloading the BIOS updates for her servers from Lenovo. They are publicly available right here:

https://download.lenovo.com/cdrt/td/catalogv2.xml

When you enter this URL into your browser, you see a huge XML data set. This is no different from the previous example: it is a machine-readable web service.

The admin selected Invoke-RestMethod to retrieve the information:

 



$Uri = 'https://download.lenovo.com/cdrt/td/catalogv2.xml'
$data = Invoke-RestMethod -Uri $Uri -UseBasicParsing 


 

While this worked, unfortunately, the magic auto-conversion did not occur: the result was the same huge XML data set. Why?

Invoke-RestMethod tried to auto-convert the data but failed, and so would you. The conversion to XML would yield nothing.


PS C:\> $data -as [xml]

Invoke-RestMethod is only as good and capable as the data you receive. Upon closer examination, it turns out that Lenovo returns text with a few garbled bytes at the beginning:


PS C:\> $data.Substring(0,40)

 

Issues like this may occasionally occur and are caused by different types of text encoding. While Invoke-RestMethod cannot fix this, you can: once you remove the first five bytes, the XML conversion works as expected:


PS C:\> $xml = $data.Substring(5) -as [xml]

PS C:\> $xml.ModelList.Model

name                                              Types BIOS SCCM                       
----                                              ----- ---- ----                       
ThinkCentre M715Q                                 Types BIOS SCCM                       
ThinkCentre M715Q 2nd Gen                         Types BIOS {SCCM, SCCM, SCCM}         
ThinkCentre M810Z                                 Types BIOS {SCCM, SCCM}               
ThinkCentre M625Q                                 Types BIOS {SCCM, SCCM, SCCM}         
ThinkCentre M630E                                 Types BIOS {SCCM, SCCM}               
ThinkCentre M710Q                                 Types BIOS {SCCM, SCCM, SCCM}  
...
 

In the end, the admin was able to automate the task with a remarkably short PowerShell script, selecting a Lenovo server, retrieving the download URL for the latest BIOS version, and downloading the binaries:


# web service url
$Uri = 'https://download.lenovo.com/cdrt/td/catalogv2.xml'

# get data and convert into correct format
$data = Invoke-RestMethod -Uri $Uri -UseBasicParsing
# fix the text encoding issue
[xml]$realData = $data.Substring(5)

# let the user select the server model
$selection = $realData.ModelList.Model |
  # we care only about these two properties 
  Select-Object -Property Name, BIOS |
  # retrieve the download URL from the nested property "#text" and move it up
  ForEach-Object {
    $_.BIOS = $_.BIOS.'#text'
    $_
  } |
  Sort-Object -Property Name |
  # let the user select one server type
  Out-GridView -Title 'Select Server Type' -OutputMode Single

# this is what we got back
$url = $selection.BIOS
$modelname = $selection.Model -replace '\s','_'

# construct the local file name
$filename = Split-Path -Path $url -Leaf
$timestamp = Get-Date -Format 'yyyy-MM-dd'
$destinationPath = Join-Path -Path $env:temp -ChildPath "$timestamp-$modelname-$filename"

# use Invoke-WebRequest to perform the actual download
Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $destinationPath 
# show the selected downloaded file in File Explorer
explorer /select,$destinationPath 

 

Good2know

Your ultimate PowerShell Cheat Sheet

Unleash the full potential of PowerShell with our handy poster. Whether you're a beginner or a seasoned pro, this cheat sheet is designed to be your go-to resource for the most important and commonly used cmdlets.

The poster is available for download and in paper form.

PowerShell Poster 2023

Get your poster here!

 

 

Related links 

 

Related posts

5 min read

Using .NET Libraries in PowerShell - Best Practice and Quick Start

Most PowerShell cmdlets are simply wrappers around underlying .NET libraries. In this series, we’ll take a closer look...

5 min read

How To Use Web Services in PowerShell - Advanced Options

In the previous parts we looked at the basic tasks Invoke-WebRequest and Invoke-RestMethod perform, and how both...

5 min read

How To Use Web Services in PowerShell - Invoke-RestMethod Deep Dive

In part 2 we looked at Invoke-WebRequest. Today we focus on Invoke-RestMethod: this cmdlet performs the same tasks but...

About the author: