Working with Invoke-RestMethod

This topic contains 4 replies, has 2 voices, and was last updated by  stephenmbell 2 months, 2 weeks ago.

  • Author
    Posts
  • #95395

    stephenmbell
    Participant

    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

  • #95403

    postanote
    Participant

    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
    
  • #95410

    stephenmbell
    Participant

    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

  • #95412

    postanote
    Participant

    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.
    https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-6

    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
    
  • #95448

    stephenmbell
    Participant

    Excellent. This makes sense. Thanks again!

You must be logged in to reply to this topic.