Chapter 13Lesson 03~160 minutes

REST APIs with Invoke-RestMethod

Use Invoke-RestMethod as a protocol-aware REST client: model resources and methods, serialize object bodies, inspect deserialized responses, classify HTTP status, and validate payload shape.

BeginnerRESTInvoke-RestMethodJSON

Learning objectives

  • Explain REST-style resources and common HTTP methods without hiding protocol semantics.
  • Build JSON request bodies from PowerShell objects.
  • Use Invoke-RestMethod for safe GET and echo-based write examples.
  • Inspect automatic JSON deserialization and returned object shape.
  • Choose an intentional HTTP error-handling policy.
  • Validate required response fields before downstream automation.

1. REST-style APIs expose resources through HTTP contracts

REST is an architectural style commonly expressed through HTTP resources. You do not need to memorize theory before using an API, but you should understand the contract: a URI identifies a resource or collection, an HTTP method expresses an operation, headers carry metadata, and the body often contains JSON.

MethodTypical intentIdempotency note
GETRead a resource/collectionNormally safe and idempotent.
POSTCreate or trigger an operationOften not idempotent.
PUTReplace/create a known resource representationDesigned to be idempotent.
PATCHPartially update a resourceDepends on API semantics.
DELETERemove a resourceOften designed to be idempotent, but verify the API.

2. Invoke-RestMethod deserializes structured responses

Invoke-RestMethod sends HTTP/HTTPS requests like Invoke-WebRequest, but when the response is JSON or XML it converts the body into PowerShell-friendly objects. That is why it is usually the better choice for APIs.

$result = Invoke-RestMethod -Uri 'https://httpbin.org/get?course=powershell' `
    -Method Get -ConnectionTimeoutSeconds 5 -OperationTimeoutSeconds 15

$result.GetType().FullName
$result.args.course
$result.headers | Get-Member

3. Build request bodies from objects, then serialize

Hand-concatenating JSON creates quoting, escaping, and type bugs. Model the payload as a PowerShell object first, then use ConvertTo-Json.

$payload = [pscustomobject]@{
    name    = 'web-01'
    enabled = $true
    ports   = @(80,443)
    owner   = [pscustomobject]@{
        team = 'platform'
        tier = 2
    }
}

$json = $payload | ConvertTo-Json -Depth 5
$json

4. POST to a test echo endpoint without changing production data

The public httpbin.org /anything endpoint echoes the request instead of creating a durable production resource. That makes it useful for learning request construction without a paid account.

$payload = [pscustomobject]@{
    name    = 'web-01'
    enabled = $true
    tags    = @('academy','lab')
}

$response = Invoke-RestMethod -Uri 'https://httpbin.org/anything/resources' `
    -Method Post `
    -ContentType 'application/json' `
    -Body ($payload | ConvertTo-Json -Depth 4) `
    -ConnectionTimeoutSeconds 5 -OperationTimeoutSeconds 15

$response.method
$response.json | Format-List

5. PUT, PATCH, and DELETE are still HTTP requests

The method changes the server-side contract, not PowerShell’s object model. On a real API, read the API documentation before deciding whether a method is safe or idempotent. The test endpoint below only echoes what you asked for.

$patch = @{ enabled = $false } | ConvertTo-Json
$echo = Invoke-RestMethod -Uri 'https://httpbin.org/anything/resources/web-01' `
    -Method Patch -ContentType 'application/json' -Body $patch
$echo.method
$echo.json.enabled

$deleteEcho = Invoke-RestMethod -Uri 'https://httpbin.org/anything/resources/web-01' `
    -Method Delete
$deleteEcho.method

6. Automatic conversion is convenient, but inspect the shape

A JSON object becomes a PowerShell object; arrays remain collections; JSON booleans become Booleans; null becomes $null. API schemas can still change, and a field can be missing even when your last response contained it.

$response = Invoke-RestMethod 'https://httpbin.org/json'
$response | Get-Member
$response.slideshow | Select-Object title,author
@($response.slideshow.slides).Count

7. Sometimes you need status and headers as well as deserialized data

Current web cmdlets support -StatusCodeVariable and -ResponseHeadersVariable. With -SkipHttpErrorCheck, HTTP 4xx/5xx responses can be inspected as data instead of automatically becoming a terminating HTTP error. Connection, DNS, and TLS failures can still throw and must still be caught.

$data = Invoke-RestMethod -Uri 'https://httpbin.org/status/404' `
    -SkipHttpErrorCheck `
    -StatusCodeVariable statusCode `
    -ResponseHeadersVariable responseHeaders `
    -ConnectionTimeoutSeconds 5 -OperationTimeoutSeconds 15

[pscustomobject]@{
    StatusCode  = $statusCode
    ContentType = ($responseHeaders['Content-Type'] -join ', ')
    BodyType    = if ($null -eq $data) { '<none>' } else { $data.GetType().FullName }
}

8. Choose one HTTP error policy deliberately

Two valid designs are common:

  1. Exception-first: let non-success HTTP status codes throw, catch the error, and extract response context.
  2. Status-first: use -SkipHttpErrorCheck plus status/header variables, then classify status codes yourself.

Mixing the two without a clear policy makes retry and CI behavior unpredictable.

9. Validate the shape you actually require

PowerShell does not automatically enforce a remote API schema. Perform small boundary checks before deeper logic. This is validation, not sanitization: it verifies assumptions about received data.

function Assert-ApiPayload {
    param([Parameter(Mandatory)]$InputObject)

    foreach ($name in 'name','enabled') {
        if ($null -eq $InputObject.PSObject.Properties[$name]) {
            throw "API payload is missing required property '$name'."
        }
    }
    if ($InputObject.name -isnot [string] -or [string]::IsNullOrWhiteSpace($InputObject.name)) {
        throw 'API property name must be a non-empty string.'
    }
    if ($InputObject.enabled -isnot [bool]) {
        throw 'API property enabled must be Boolean.'
    }
}

10. Lab: read, write to an echo endpoint, and validate the response

$read = Invoke-RestMethod 'https://httpbin.org/get?environment=lab'
if ($read.args.environment -ne 'lab') {
    throw 'GET response did not contain the expected query value.'
}

$requestObject = [pscustomobject]@{
    name    = 'api-lab'
    enabled = $true
}

$write = Invoke-RestMethod -Uri 'https://httpbin.org/anything/config' `
    -Method Post -ContentType 'application/json' `
    -Body ($requestObject | ConvertTo-Json)

Assert-ApiPayload -InputObject $write.json
[pscustomobject]@{
    Method       = $write.method
    Name         = $write.json.name
    Enabled      = $write.json.enabled
    EchoVerified = $true
}

If the public test service is unavailable in your environment, treat that as a network dependency failure—not as permission to remove timeouts/error checks. The same payload and validation functions can be reused against a local mock server.

11. Why REST clients matter in DevOps

CI systems, artifact registries, cloud control planes, Git hosting, monitoring products, ticket systems, and internal deployment platforms commonly expose HTTP APIs. PowerShell’s object model makes it natural to convert JSON into pipeline objects, but reliability still depends on protocol-aware status, timeout, authentication, pagination, and retry handling.

12. Verification checklist

  • You can describe GET/POST/PUT/PATCH/DELETE as HTTP method contracts.
  • You build JSON from PowerShell objects rather than hand-concatenated strings.
  • You can inspect deserialized REST response properties and types.
  • You understand exception-first versus status-first HTTP error policies.
  • You can validate required response properties before deeper logic.
  • Your write lab uses an echo/test endpoint rather than a production resource.

13. Common mistakes

  • Assuming “REST” means only GET plus JSON.
  • Concatenating JSON strings by hand.
  • Ignoring a 4xx/5xx status because the body contains useful text.
  • Assuming a property always exists because one sample response had it.
  • Retrying non-idempotent POST requests automatically without understanding duplicate effects.
  • Using real production credentials in a training request.

14. Knowledge check

Question 1. Why is Invoke-RestMethod convenient for JSON APIs?

Question 2. Why build a request body as a PowerShell object first?

Question 3. What does -SkipHttpErrorCheck change?

Question 4. Is POST always safe to retry?

Question 5. What is a schema-like boundary check for?

15. Summary and next bridge

Invoke-RestMethod turns API responses into objects, but it does not remove the need to understand HTTP. You now have a clean request-body pattern, status/error policy, and response validation. Lesson 4 adds the operational concerns that make real integrations harder: credentials, headers, pagination, rate limits, retries, and secret-safe diagnostics.

16. Authoritative references

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.