Public/Invoke-LMAPIRequest.ps1
|
<# .SYNOPSIS Executes a custom LogicMonitor API request with full control over endpoint and payload. .DESCRIPTION The Invoke-LMAPIRequest function provides advanced users with direct access to the LogicMonitor API while leveraging the module's authentication, retry logic, debug utilities, and error handling. This is useful for accessing API endpoints that don't yet have dedicated cmdlets in the module. .PARAMETER ResourcePath The API resource path (e.g., "/device/devices", "/setting/integrations/123"). Do not include the base URL or query parameters here. .PARAMETER Method The HTTP method to use. Valid values: GET, POST, PATCH, PUT, DELETE. .PARAMETER QueryParams Optional hashtable of query parameters to append to the request URL. Example: @{ size = 100; offset = 0; filter = 'name:"test"' } .PARAMETER Data Optional hashtable containing the request body data. Will be automatically converted to JSON. Use this for POST, PATCH, and PUT requests. .PARAMETER RawBody Optional raw string body to send with the request. Use this instead of -Data when you need complete control over the request body format. Mutually exclusive with -Data. .PARAMETER Version The X-Version header value for the API request. Defaults to 3. Some newer API endpoints may require different version numbers. .PARAMETER ContentType The Content-Type header for the request. Defaults to "application/json". .PARAMETER MaxRetries Maximum number of retry attempts for transient errors. Defaults to 3. Set to 0 to disable retries. .PARAMETER NoRetry Switch to completely disable retry logic and fail immediately on any error. .PARAMETER OutFile Path to save the response content to a file. Useful for downloading reports or exports. .PARAMETER TypeName Optional type name to add to the returned objects (e.g., "LogicMonitor.CustomResource"). This enables proper formatting if you have custom format definitions. .PARAMETER AsHashtable Switch to return the response as a hashtable instead of a PSCustomObject. .EXAMPLE # Get a custom resource not yet supported by a dedicated cmdlet Invoke-LMAPIRequest -ResourcePath "/setting/integrations" -Method GET .EXAMPLE # Create a resource with custom payload $data = @{ name = "My Integration" type = "slack" url = "https://hooks.slack.com/services/..." } Invoke-LMAPIRequest -ResourcePath "/setting/integrations" -Method POST -Data $data .EXAMPLE # Create a device with custom properties (note the array format) $data = @{ name = "server1" displayName = "Production Server" preferredCollectorId = 5 customProperties = @( @{ name = "environment"; value = "production" } @{ name = "owner"; value = "ops-team" } ) } Invoke-LMAPIRequest -ResourcePath "/device/devices" -Method POST -Data $data .EXAMPLE # Update a resource with PATCH $updates = @{ description = "Updated description" } Invoke-LMAPIRequest -ResourcePath "/device/devices/123" -Method PATCH -Data $updates .EXAMPLE # Delete a resource Invoke-LMAPIRequest -ResourcePath "/setting/integrations/456" -Method DELETE .EXAMPLE # Get with query parameters and custom version $queryParams = @{ size = 500 filter = 'status:"active"' fields = "id,name,status" } Invoke-LMAPIRequest -ResourcePath "/device/devices" -Method GET -QueryParams $queryParams -Version 3 .EXAMPLE # Use raw body for special formatting requirements $rawJson = '{"name":"test","customField":null}' Invoke-LMAPIRequest -ResourcePath "/custom/endpoint" -Method POST -RawBody $rawJson .EXAMPLE # Download a report to file Invoke-LMAPIRequest -ResourcePath "/report/reports/123/download" -Method GET -OutFile "C:\Reports\report.pdf" .EXAMPLE # Format output as table Invoke-LMAPIRequest -ResourcePath "/device/devices" -Method GET | Format-Table id, name, displayName, status # Use existing format definition Invoke-LMAPIRequest -ResourcePath "/device/devices" -Method GET -TypeName "LogicMonitor.Device" .EXAMPLE # Get paginated results manually $offset = 0 $size = 1000 $allResults = @() do { $response = Invoke-LMAPIRequest -ResourcePath "/device/devices" -Method GET -QueryParams @{ size = $size; offset = $offset } $allResults += $response.items $offset += $size } while ($allResults.Count -lt $response.total) .NOTES You must run Connect-LMAccount before running this command. This cmdlet is designed for advanced users who need to: - Access API endpoints not yet covered by dedicated cmdlets - Test new API features or beta endpoints - Implement custom workflows requiring direct API access - Prototype new functionality before requesting cmdlet additions For standard operations, use the dedicated cmdlets (Get-LMDevice, New-LMDevice, etc.) as they provide better parameter validation, documentation, and user experience. .INPUTS None. You cannot pipe objects to this command. .OUTPUTS Returns the API response as a PSCustomObject by default, or as specified by -AsHashtable. #> function Invoke-LMAPIRequest { [CmdletBinding(DefaultParameterSetName = 'Data', SupportsShouldProcess, ConfirmImpact = 'Medium')] param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String]$ResourcePath, [Parameter(Mandatory)] [ValidateSet("GET", "POST", "PATCH", "PUT", "DELETE")] [String]$Method, [Hashtable]$QueryParams, [Parameter(ParameterSetName = 'Data')] [Hashtable]$Data, [Parameter(ParameterSetName = 'RawBody')] [String]$RawBody, [ValidateRange(1, 10)] [Int]$Version = 3, [String]$ContentType = "application/json", [ValidateRange(0, 10)] [Int]$MaxRetries = 3, [Switch]$NoRetry, [String]$OutFile, [String]$TypeName, [Switch]$AsHashtable ) #Check if we are logged in and have valid api creds if (-not $Script:LMAuth.Valid) { Write-Error "Please ensure you are logged in before running any commands, use Connect-LMAccount to login and try again." return } # Ensure ResourcePath starts with / if (-not $ResourcePath.StartsWith('/')) { $ResourcePath = '/' + $ResourcePath } # Build the request body $Body = $null if ($PSCmdlet.ParameterSetName -eq 'Data' -and $Data) { # Convert hashtable to JSON $Body = $Data | ConvertTo-Json -Depth 10 -Compress } elseif ($PSCmdlet.ParameterSetName -eq 'RawBody' -and $RawBody) { $Body = $RawBody } # Build query string from QueryParams hashtable $QueryString = "" if ($QueryParams -and $QueryParams.Count -gt 0) { $queryParts = @() foreach ($key in $QueryParams.Keys) { $value = $QueryParams[$key] if ($null -ne $value) { # URL encode the value $encodedValue = [System.Web.HttpUtility]::UrlEncode($value.ToString()) $queryParts += "$key=$encodedValue" } } if ($queryParts.Count -gt 0) { $QueryString = "?" + ($queryParts -join "&") } } # Build the full URI $Uri = "https://$($Script:LMAuth.Portal).$(Get-LMPortalURI)" + $ResourcePath + $QueryString # Create a message for ShouldProcess $operationDescription = switch ($Method) { "GET" { "Retrieve from" } "POST" { "Create resource at" } "PATCH" { "Update resource at" } "PUT" { "Replace resource at" } "DELETE" { "Delete resource at" } } # Adjust ConfirmImpact based on method $shouldProcessTarget = $ResourcePath $shouldProcessAction = $operationDescription # Determine if we should prompt based on method # GET requests should never prompt unless explicitly requested # DELETE requests should always prompt unless explicitly suppressed $shouldPrompt = $true if ($Method -eq "GET") { # For GET, only process if -Confirm was explicitly passed if ($PSBoundParameters.ContainsKey('Confirm')) { $shouldPrompt = $PSCmdlet.ShouldProcess($shouldProcessTarget, $shouldProcessAction) } else { $shouldPrompt = $true # Skip ShouldProcess for GET } } elseif ($Method -eq "DELETE") { # For DELETE, always use ShouldProcess with High impact $shouldPrompt = $PSCmdlet.ShouldProcess($shouldProcessTarget, $shouldProcessAction, "Are you sure you want to delete this resource?") } else { # For POST, PATCH, PUT use normal ShouldProcess $shouldPrompt = $PSCmdlet.ShouldProcess($shouldProcessTarget, $shouldProcessAction) } if ($shouldPrompt) { # Generate headers with custom version $Headers = New-LMHeader -Auth $Script:LMAuth -Method $Method -ResourcePath $ResourcePath -Data $Body -Version $Version -ContentType $ContentType # Output debug information Resolve-LMDebugInfo -Url $Uri -Headers $Headers[0] -Command $MyInvocation -Payload $Body # Build parameters for Invoke-LMRestMethod $restParams = @{ Uri = $Uri Method = $Method Headers = $Headers[0] WebSession = $Headers[1] CallerPSCmdlet = $PSCmdlet } if ($Body) { $restParams.Body = $Body } if ($OutFile) { $restParams.OutFile = $OutFile } if ($MaxRetries -eq 0 -or $NoRetry) { $restParams.NoRetry = $true } else { $restParams.MaxRetries = $MaxRetries } # Issue request $Response = Invoke-LMRestMethod @restParams # Handle the response if ($null -eq $Response) { return $null } # If OutFile was specified, the response is already saved if ($OutFile) { Write-Verbose "Response saved to: $OutFile" return [PSCustomObject]@{ Success = $true FilePath = $OutFile Message = "Response saved successfully" } } # Return the items array if it exists, otherwise return the response object if ($Response.items) { $Response = $Response.items } # Add type information if specified if ($TypeName) { $Response = Add-ObjectTypeInfo -InputObject $Response -TypeName $TypeName } # Convert to hashtable if requested if ($AsHashtable -and $Response) { if ($Response -is [Array]) { return $Response | ForEach-Object { $hashtable = @{} $_.PSObject.Properties | ForEach-Object { $hashtable[$_.Name] = $_.Value } $hashtable } } else { $hashtable = @{} $Response.PSObject.Properties | ForEach-Object { $hashtable[$_.Name] = $_.Value } return $hashtable } } return $Response } } |