Private/Invoke-AeriesApiCall.ps1

function Invoke-AeriesApiCall
{
    <#
        .SYNOPSIS
        Invokes Aeries API calls

        .DESCRIPTION
        The Invoke-AeriesApiCall cmdlet is used to perform Aeries API calls

        .EXAMPLE
        Invoke-AeriesApiCall -Method "Get" -Endpoint "v5/systeminfo"
        This will perform a web request against the configured Aeries Instance and return the result
    #>


    [CmdletBinding()]
    param (
        # Method to perform the call with (Default is Get)
        [Parameter(Mandatory=$false)]
        [string]
        [ValidateSet("Get","Post","Put","Delete")]
        $Method = "Get",
        # Endpoint to call, such as "v5/systeminfo"
        [Parameter(Mandatory=$true)]
        [string]
        $Endpoint,
        # Success Status Code expected
        [Parameter(Mandatory=$false)]
        [int[]]
        $SuccessStatusCode = 200,
        # Body to send the request with. Typically used for Post or Put
        [Parameter(Mandatory=$false, ValueFromPipeline)]
        [string]
        $Body = "",
        # URL Query Parameters to utilize
        [Parameter(Mandatory=$false)]
        [hashtable]
        $QueryParameters,
        # When this is used, DatabaseYear will be ignored on the request
        [Parameter(Mandatory=$false)]
        [switch]
        $IgnoreDatabaseYear,
        # Set the ContentType for a request (only used for PUT/POST)
        [Parameter(Mandatory=$false)]
        [string]
        $ContentType = "application/json"
    )

    Begin {
        Write-Verbose -Message "Begin calling $Endpoint"

        # Required in order to communicate to Aeries
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
        # Add System.Web for parameter parsing (if necessary)
        Add-Type -AssemblyName System.Web

        # Retrieve config and ensure things are set properly
        $AeriesConfig = $Script:AeriesApiConfig
        if (
            [string]::IsNullOrWhiteSpace($AeriesConfig.URL) -or
            [string]::IsNullOrWhiteSpace($AeriesConfig.Certificate)
            ) { Throw "Please run Initialize-AeriesApi before making API calls" }

        # Generate the headers that will be sent
        $Headers = @{
            "Accept" = "application/json"
            "AERIES-CERT" = $AeriesConfig.Certificate
        }

        # Basic variables to be used when sending the request
        $BaseURL = $AeriesConfig.URL.Trim("/")
        $RequestURL = "$BaseURL/api/$Endpoint"

        # Begin Parameter processing
        # Check if a DatabaseYear was configured
        # If so, add to parameters except when asked to ignore
        if (![string]::IsNullOrWhiteSpace($AeriesConfig.DatabaseYear) -and !$IgnoreDatabaseYear) {
            $QueryParameters += @{
                "DatabaseYear" = $AeriesConfig.DatabaseYear.Trim()
            }
        }

        $QueryStringObject = [System.Web.HttpUtility]::ParseQueryString([string]::Empty)
        # Go through and process Parameters into TempParameters
        if ($QueryParameters.Count -ge 1) {
            foreach ($Key in $QueryParameters.Keys) {
                $QueryStringObject.Add($Key, $QueryParameters[$Key])
            }
        }
        # If there are items in the object, add them as a query string
        if ($QueryStringObject.Count -ge 1) {
            $RequestURL += ("?" + $QueryStringObject.ToString())
        }
    }

    Process {
        try {
            # Assemble the parameters for easy maintaining and clean code reading
            $FunctionParameters = @{
                Uri = $RequestURL;
                Method = $Method;
                Headers = $Headers;
                UserAgent = $AeriesConfig.UserAgent;
                UseBasicParsing = $true;
            }

            # Sending a body when it's not Put or Post doesn't work for Invoke-WebRequest
            if (@("Put","Post") -contains $Method) {
                # Set the Content-Type of the body
                $Headers.Add("Content-Type", $ContentType)

                # Assign the body
                $FunctionParameters.Body = $Body
            }

            # Run the Web Request
            $ApiResult = (Invoke-WebRequest @FunctionParameters)

            # Store the Status and Content
            $StatusCode = $ApiResult.StatusCode
            $ResponseBody = $ApiResult.Content
        }
        catch {
            # There was an error, retrieve the info from it
            $StatusCode = $_.Exception.Response.StatusCode.value__
            $ResponseBody = $_.ErrorDetails.Message
        }
    }

    End {
        Write-Verbose -Message "Finish calling $Endpoint"

        # Used to store codes that should be parsed for an error message
        # and returned slightly different
        $ParseErrorCodes  = @()
        $ParseErrorCodes += 400 # Bad Request
        $ParseErrorCodes += 401 # Unauthorized
        $ParseErrorCodes += 403 # Forbidden
        $ParseErrorCodes += 404 # Not Found

        if ($StatusCode -in $SuccessStatusCode) {
            # If the request was a success, return the Content as a JSON object
            return ($ResponseBody | ConvertFrom-Json)
        }
        elseif ($StatusCode -in $ParseErrorCodes) {
            # There is a message attached to the error from Aeries
            Throw "Error calling $Endpoint : $(($ResponseBody | ConvertFrom-Json).Message)"
        }
        else {
            Write-Verbose -Message "Error: $($ResponseBody)"
            Throw "Status code `"$($StatusCode)`" does not indicate success for $Endpoint`r`n$ResponseBody"
        }
    }
}