Working with Invoke-RestMethod

Hello all – Hoping someone can explain this to me.

I had our dev team stand up a web api which houses some information about our network devices.

If I run:

Invoke-RestMethod -uri $apiUrl

Here is (sample) data that I am returned:

[
  {
    "Active": true,
    "AlternateName": "sample string 2",
    "EnvironmentInfoId": 3,
    "InstallWeek": "2018-03-07T16:06:25.0430535-05:00",
    "Notes": "sample string 4",
    "RackSize": "sample string 5",
    "Subnet": {
      "SubnetId": 1,
      "Network": "sample string 2",
      "RangeStart": 3,
      "RangeEnd": 4,
      "Tunnel1": "sample string 5",
      "Tunnel2": "sample string 6",
      "Tunnel3": "sample string 7",
      "Tunnel4": "sample string 8",
      "Tunnel5": "sample string 9",
      "Tunnel6": "sample string 10",
      "GTechSRX": "sample string 11",
      "CreatedBy": "sample string 12",
      "LastModifiedBy": "sample string 13",
      "CreatedDate": "2018-03-07T16:06:25.0430535-05:00",
      "LastModifiedDate": "2018-03-07T16:06:25.0430535-05:00"
    },
    "SubnetId": 1,
    "ThirdRegister": true,
    "CreatedBy": "sample string 7",
    "LastModifiedBy": "sample string 8",
    "CreatedDate": "2018-03-07T16:06:25.0430535-05:00",
    "LastModifiedDate": "2018-03-07T16:06:25.0430535-05:00"
  },
  {
    "Active": true,
    "AlternateName": "sample string 2",
    "EnvironmentInfoId": 3,
    "InstallWeek": "2018-03-07T16:06:25.0430535-05:00",
    "Notes": "sample string 4",
    "RackSize": "sample string 5",
    "Subnet": {
      "SubnetId": 1,
      "Network": "sample string 2",
      "RangeStart": 3,
      "RangeEnd": 4,
      "Tunnel1": "sample string 5",
      "Tunnel2": "sample string 6",
      "Tunnel3": "sample string 7",
      "Tunnel4": "sample string 8",
      "Tunnel5": "sample string 9",
      "Tunnel6": "sample string 10",
      "GTechSRX": "sample string 11",
      "CreatedBy": "sample string 12",
      "LastModifiedBy": "sample string 13",
      "CreatedDate": "2018-03-07T16:06:25.0430535-05:00",
      "LastModifiedDate": "2018-03-07T16:06:25.0430535-05:00"
    },
    "SubnetId": 1,
    "ThirdRegister": true,
    "CreatedBy": "sample string 7",
    "LastModifiedBy": "sample string 8",
    "CreatedDate": "2018-03-07T16:06:25.0430535-05:00",
    "LastModifiedDate": "2018-03-07T16:06:25.0430535-05:00"
  }
]

Why - If I run this:

$network = Invoke-RestMethod -uri $apiUrl 
$network= $network | Select-Object -ExpandProperty Subnet

I get the data from the Subnet property. BUT - if I run this:

$network = Invoke-RestMethod -uri $apiUrl | Select-Object -ExpandProperty Subnet

My $network variable is empty??

This is the first time I am really working with Invoke-RestMethod and JSON data - so I may very well be the problem:)

Thanks
Steve

Yep, first times are, well, you know. 8^}

One: Why are you using the same variable on each assignment?
Two: if the first one is working for you, why do you feel the need to try the other one?
Three: If you want this as that approach, then try it this way. Though this is not optimal, as you’ll see later.

# Define a variable to assign the output to.  Get the object details first.  Then get the subnet from the previous object
$network = (Invoke-RestMethod -uri $apiUrl) | Select-Object -ExpandProperty Subnet

Those parens, like math means do this first before doing anything else.
Which is what you did here:

$network = Invoke-RestMethod -uri $apiUrl 
$SubNetwork= $network | Select-Object -ExpandProperty Subnet

The real question is why do it this way at all. You already have all you need from the first

Invoke-RestMethod -uri $apiUrl

I can’t do the above for obious reasons, but If I just take your JSON results. Just use the ConvertFrom-Json cmdlet.

# Get parameters, examples, full and Online help for a cmdlet or function

(Get-Command -Name ConvertFrom-Json).Parameters
Get-help -Name ConvertFrom-Json -Examples
Get-help -Name ConvertFrom-Json -Full
Get-help -Name ConvertFrom-Json -Online

Get-Help about_*
Get-Help about_Functions

# Find all cmdlets / functions with a target parameter
Get-Help * -Parameter Append

# All Help topics locations
explorer "$pshome\$($Host.CurrentCulture.Name)"
$ApiJson = @'
[
  {
    "Active": true,
    "AlternateName": "sample string 2",
    "EnvironmentInfoId": 3,
    "InstallWeek": "2018-03-07T16:06:25.0430535-05:00",
    "Notes": "sample string 4",
    "RackSize": "sample string 5",
    "Subnet": {
      "SubnetId": 1,
      "Network": "sample string 2",
      "RangeStart": 3,
      "RangeEnd": 4,
      "Tunnel1": "sample string 5",
      "Tunnel2": "sample string 6",
      "Tunnel3": "sample string 7",
      "Tunnel4": "sample string 8",
      "Tunnel5": "sample string 9",
      "Tunnel6": "sample string 10",
      "GTechSRX": "sample string 11",
      "CreatedBy": "sample string 12",
      "LastModifiedBy": "sample string 13",
      "CreatedDate": "2018-03-07T16:06:25.0430535-05:00",
      "LastModifiedDate": "2018-03-07T16:06:25.0430535-05:00"
    },
    "SubnetId": 1,
    "ThirdRegister": true,
    "CreatedBy": "sample string 7",
    "LastModifiedBy": "sample string 8",
    "CreatedDate": "2018-03-07T16:06:25.0430535-05:00",
    "LastModifiedDate": "2018-03-07T16:06:25.0430535-05:00"
  },
  {
    "Active": true,
    "AlternateName": "sample string 2",
    "EnvironmentInfoId": 3,
    "InstallWeek": "2018-03-07T16:06:25.0430535-05:00",
    "Notes": "sample string 4",
    "RackSize": "sample string 5",
    "Subnet": {
      "SubnetId": 1,
      "Network": "sample string 2",
      "RangeStart": 3,
      "RangeEnd": 4,
      "Tunnel1": "sample string 5",
      "Tunnel2": "sample string 6",
      "Tunnel3": "sample string 7",
      "Tunnel4": "sample string 8",
      "Tunnel5": "sample string 9",
      "Tunnel6": "sample string 10",
      "GTechSRX": "sample string 11",
      "CreatedBy": "sample string 12",
      "LastModifiedBy": "sample string 13",
      "CreatedDate": "2018-03-07T16:06:25.0430535-05:00",
      "LastModifiedDate": "2018-03-07T16:06:25.0430535-05:00"
    },
    "SubnetId": 1,
    "ThirdRegister": true,
    "CreatedBy": "sample string 7",
    "LastModifiedBy": "sample string 8",
    "CreatedDate": "2018-03-07T16:06:25.0430535-05:00",
    "LastModifiedDate": "2018-03-07T16:06:25.0430535-05:00"
  }
]
'@

$ApiJson | ConvertFrom-Json

Which gives you this…

Active            : True
AlternateName     : sample string 2
EnvironmentInfoId : 3
InstallWeek       : 2018-03-07T16:06:25.0430535-05:00
Notes             : sample string 4
RackSize          : sample string 5
Subnet            : @{SubnetId=1; Network=sample string 2; RangeStart=3; RangeEnd=4; Tunnel1=sample string 5; Tunnel2=sample string 6; Tunnel3=sample string 7; Tunnel4=sample string 8; 
                    Tunnel5=sample string 9; Tunnel6=sample string 10; GTechSRX=sample string 11; CreatedBy=sample string 12; LastModifiedBy=sample string 13; 
                    CreatedDate=2018-03-07T16:06:25.0430535-05:00; LastModifiedDate=2018-03-07T16:06:25.0430535-05:00}
SubnetId          : 1
ThirdRegister     : True
CreatedBy         : sample string 7
LastModifiedBy    : sample string 8
CreatedDate       : 2018-03-07T16:06:25.0430535-05:00
LastModifiedDate  : 2018-03-07T16:06:25.0430535-05:00

Active            : True
AlternateName     : sample string 2
EnvironmentInfoId : 3
InstallWeek       : 2018-03-07T16:06:25.0430535-05:00
Notes             : sample string 4
RackSize          : sample string 5
Subnet            : @{SubnetId=1; Network=sample string 2; RangeStart=3; RangeEnd=4; Tunnel1=sample string 5; Tunnel2=sample string 6; Tunnel3=sample string 7; Tunnel4=sample string 8; 
                    Tunnel5=sample string 9; Tunnel6=sample string 10; GTechSRX=sample string 11; CreatedBy=sample string 12; LastModifiedBy=sample string 13; 
                    CreatedDate=2018-03-07T16:06:25.0430535-05:00; LastModifiedDate=2018-03-07T16:06:25.0430535-05:00}
SubnetId          : 1
ThirdRegister     : True
CreatedBy         : sample string 7
LastModifiedBy    : sample string 8
CreatedDate       : 2018-03-07T16:06:25.0430535-05:00
LastModifiedDate  : 2018-03-07T16:06:25.0430535-05:00

… which I then can just do this

($ApiJson | ConvertFrom-Json).Subnet

Which gives this.

SubnetId         : 1
Network          : sample string 2
RangeStart       : 3
RangeEnd         : 4
Tunnel1          : sample string 5
Tunnel2          : sample string 6
Tunnel3          : sample string 7
Tunnel4          : sample string 8
Tunnel5          : sample string 9
Tunnel6          : sample string 10
GTechSRX         : sample string 11
CreatedBy        : sample string 12
LastModifiedBy   : sample string 13
CreatedDate      : 2018-03-07T16:06:25.0430535-05:00
LastModifiedDate : 2018-03-07T16:06:25.0430535-05:00

SubnetId         : 1
Network          : sample string 2
RangeStart       : 3
RangeEnd         : 4
Tunnel1          : sample string 5
Tunnel2          : sample string 6
Tunnel3          : sample string 7
Tunnel4          : sample string 8
Tunnel5          : sample string 9
Tunnel6          : sample string 10
GTechSRX         : sample string 11
CreatedBy        : sample string 12
LastModifiedBy   : sample string 13
CreatedDate      : 2018-03-07T16:06:25.0430535-05:00
LastModifiedDate : 2018-03-07T16:06:25.0430535-05:00

So, just, do

(Invoke-RestMethod -uri $apiUrl | ConvertFrom-Json).Subnet

First - thank you for taking the time to reply.

I was using the same variable in the assignment because, what I really want to get to is the subnet information. Ideally, without having to do $network.subnet.property at each point of reference in my coding. Not a huge deal, but I thought it would save some keystrokes.

I just tested both methods:

(Invoke-RestMethod -uri $apiUrl) | Select -exp Subnet
# and also
(Invoke-RestMethod -uri $apiUrl).Subnet

and they produced desired results. I understand that the () mean that it will run first, but I guess I am still confused as to WHY I would need the ().

I don’t need it if I run:

Get-Service | Select -exp DisplayName

Why is invoke-restmethod different?

Thanks again
steve

An object needs to exist before you can act on it.
IVR in how you are using it has to create.

$apiUrl - Means nothing to the system until something is used to populate it, which is what you are doing with the IVR.
This is not specific to IVR. This would be the same for any cmdlet you use that is not acting on a existing object.

Windows services and processes already are created by other sources. So, these service / process objects exist before you ever ask for them. No create required. So, you are asking for the instance of a running service / process object.

Invoke is the request the create a new instance of a new object, not an existing one.

Invoke-Rest​Method Module:Microsoft.PowerShell.Utility

Description

The Invoke-RestMethod cmdlet sends HTTP and HTTPS requests to Representational State Transfer (REST) web services that returns richly structured data.

PowerShell formats the response based to the data type. For an RSS or ATOM feed, PowerShell returns the Item or Entry XML nodes. For JavaScript Object Notation (JSON) or XML, PowerShell converts (or deserializes) the content into objects.

This cmdlet is introduced in Windows PowerShell 3.0.

The ‘()’ is just doing what you did here: ‘$network = Invoke-RestMethod -uri $apiUrl’. Creating the needed object, so that you can do what you did here: ‘$network= $network | Select-Object -ExpandProperty Subnet’

So, you have to work on an existing object, or create it before you can, either inline or separately.

As for you other reasoning for the variable. You can still use that variable approach, still in one line.

$Network = (Invoke-RestMethod -uri $apiUrl).Subnet

Excellent. This makes sense. Thanks again!