Private/Resolve-LMDebugInfo.ps1

<#
.SYNOPSIS
Function to write debug information for LogicMonitor API requests.

.DESCRIPTION
The Resolve-LMDebugInfo function provides comprehensive debug information for LogicMonitor API requests including
command context, formatted parameters, URL details, secure header display, and formatted payload data.

.PARAMETER Url
The URL that was invoked for the API request.

.PARAMETER Headers
The headers hashtable used in the request.

.PARAMETER Command
The PowerShell command invocation context (typically $MyInvocation).

.PARAMETER Payload
The request payload (typically JSON string or null for GET/DELETE operations).

.EXAMPLE
Resolve-LMDebugInfo -Url "https://portal.logicmonitor.com/santaba/rest/device/devices" -Headers $Headers -Command $MyInvocation -Payload $JsonData

.NOTES
Sensitive headers (accessKey, bearerToken, JSESSIONID) are automatically redacted for security.
#>


function Resolve-LMDebugInfo {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [String]$Url,

        [Parameter(Mandatory)]
        [Hashtable]$Headers,

        [Parameter(Mandatory)]
        [System.Management.Automation.InvocationInfo]$Command,

        [String]$Payload
    )

    # Add timestamp for correlation
    $Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss.fff"

    # Extract HTTP method from headers or default to GET
    $HttpMethod = if ($Headers.ContainsKey('Content-Type') -and $Payload) {
        if ($Payload -match '".*":\s*null|".*":\s*""') { "PATCH" } else { "POST" }
    }
    elseif ($Url -match '/\d+$' -and !$Payload) { "GET" }
    elseif (!$Payload) { "DELETE" }
    else { "POST" }

    Write-Debug "============ LogicMonitor API Debug Info =============="
    Write-Debug "Command: $($Command.MyCommand) | Method: $HttpMethod | Timestamp: $Timestamp"

    # Format bound parameters
    if ($Command.BoundParameters.Count -gt 0) {
        $ParamList = $Command.BoundParameters.GetEnumerator() | ForEach-Object {
            $Value = if ($_.Value -is [Array]) { "[$($_.Value -join ', ')]" }
            elseif ($_.Value -is [Hashtable]) { "{$($_.Value.Keys -join ', ')}" }
            else { $_.Value }
            "$($_.Key): $Value"
        }
        Write-Debug "Parameters: $($ParamList -join ' | ')"
    }

    # Parse and display URL components
    $UriObj = [System.Uri]$Url
    Write-Debug "Endpoint: $($UriObj.PathAndQuery)"
    Write-Debug "Portal: $($UriObj.Host)"

    # Display headers with partial redaction for identification
    $SensitiveHeaders = @('accessKey', 'bearerToken', 'cookie', 'X-CSRF-Token')
    $HeaderInfo = $Headers.GetEnumerator() | ForEach-Object {
        $Value = if ($SensitiveHeaders -contains $_.Key) {
            if ($_.Value.Length -gt 25) {
                $_.Value.Substring(0, 25) + "..."
            }
            else {
                $_.Value
            }
        }
        else {
            $_.Value
        }
        "$($_.Key): $Value"
    }
    Write-Debug "Headers: $($HeaderInfo -join ' | ')"

    # Format and display payload with JSON pretty-printing
    if ($Payload) {
        try {
            # Parse JSON and redact sensitive fields
            $JsonObj = $Payload | ConvertFrom-Json
            $SensitiveFields = @('password', 'accessKey', 'token', 'secret', 'apiKey', 'bearerToken')

            # Recursively redact sensitive fields
            function RedactSensitiveData($Object) {
                if ($Object -is [PSCustomObject]) {
                    $Object.PSObject.Properties | ForEach-Object {
                        if ($SensitiveFields -contains $_.Name) {
                            $_.Value = "[REDACTED]"
                        }
                        elseif ($_.Value -is [PSCustomObject] -or $_.Value -is [Array]) {
                            $_.Value = RedactSensitiveData $_.Value
                        }
                    }
                }
                elseif ($Object -is [Array]) {
                    for ($i = 0; $i -lt $Object.Count; $i++) {
                        $Object[$i] = RedactSensitiveData $Object[$i]
                    }
                }
                return $Object
            }

            $RedactedObj = RedactSensitiveData $JsonObj
            $FormattedPayload = $RedactedObj | ConvertTo-Json -Depth 10 |
                ForEach-Object { $_ -split "`n" | ForEach-Object { " $_" } }
            Write-Debug "Request Payload:"
            $FormattedPayload | ForEach-Object { Write-Debug $_ }
        }
        catch {
            # Fallback to simple string display if JSON parsing fails
            Write-Debug "Request Payload (Raw): $Payload"
        }
    }
    else {
        Write-Debug "Request Payload: None ($HttpMethod request)"
    }

    Write-Debug "========================================================"
}