Public/Invoke-HiddenApiRequest.ps1
|
function Invoke-HiddenApiRequest { <# .SYNOPSIS Call internal NMM web portal APIs. .DESCRIPTION Makes authenticated requests to the NMM web portal internal APIs. Authentication methods: 1. Connect-NMMHiddenApi - Opens browser, waits for extension to send cookies (recommended) 2. Set-NMMHiddenApiCookie - Manually set cookies from Cookie-Editor export Requires HiddenApiBaseUri to be configured in Private/Data/ConfigData.json. .PARAMETER Method HTTP method (GET, POST, PUT, DELETE, PATCH). .PARAMETER Endpoint API endpoint path (e.g., "accounts", "host-pool"). .PARAMETER Body Request body for POST/PUT/PATCH requests. .PARAMETER BaseUri Base URI for the API. If not provided, reads from ConfigData.json. .EXAMPLE Invoke-HiddenApiRequest -Method GET -Endpoint "accounts" .EXAMPLE Invoke-HiddenApiRequest -Method POST -Endpoint "some/endpoint" -Body @{ key = "value" } .EXAMPLE Invoke-HiddenApiRequest -Method GET -Uri "https://nmmdemo.nerdio.net/api/v1/msp/intune/global/policies/baselines" Calls a full URL directly. .EXAMPLE # Cookie-based auth workflow Set-NMMHiddenApiCookie -CookieString "AppServiceAuthSession=abc123" Invoke-HiddenApiRequest -Method GET -Endpoint "accounts" #> [CmdletBinding(DefaultParameterSetName = 'Endpoint')] param( [Parameter(Mandatory = $true)] [ValidateSet('GET', 'POST', 'PUT', 'DELETE', 'PATCH')] [string]$Method, [Parameter(Mandatory = $true, ParameterSetName = 'Endpoint')] [string]$Endpoint, [Parameter(Mandatory = $true, ParameterSetName = 'Uri')] [string]$Uri, [Parameter()] [hashtable]$Body, [Parameter(ParameterSetName = 'Endpoint')] [hashtable]$QueryParameters, [Parameter(ParameterSetName = 'Endpoint')] [string]$BaseUri ) process { # Determine authentication method $authMethod = $null if ($Script:HiddenApiAuthMethod -eq 'Cookie' -and $Script:HiddenApiCookies) { $authMethod = 'Cookie' Write-Verbose "Using cookie-based authentication" } elseif ($Script:HiddenApiToken -and $Script:HiddenApiToken.AccessToken) { # Check if token is expired if ($Script:HiddenApiToken.ExpiresOn -le (Get-Date)) { Write-Warning "Token has expired. Run Connect-NMMHiddenApi to re-authenticate." return } $authMethod = 'Bearer' Write-Verbose "Using bearer token authentication" } else { Write-Error "Not authenticated. Run Connect-NMMHiddenApi or Set-NMMHiddenApiCookie first." return } try { # Build URI based on parameter set if ($PSCmdlet.ParameterSetName -eq 'Uri') { # Use the full URI directly $requestUri = $Uri Write-Verbose "Using direct URI: $requestUri" } else { # Build URI from BaseUri + Endpoint if ([string]::IsNullOrEmpty($BaseUri)) { try { $config = Get-ConfigData -ErrorAction Stop if ($config.HiddenApiBaseUri) { $BaseUri = $config.HiddenApiBaseUri Write-Verbose "Using HiddenApiBaseUri from ConfigData.json: $BaseUri" } else { Write-Error "HiddenApiBaseUri not configured in ConfigData.json. Please add it to Private/Data/ConfigData.json" return } } catch { Write-Error "Could not read ConfigData.json. Please configure HiddenApiBaseUri in Private/Data/ConfigData.json" return } } $requestUri = "$BaseUri/$($Endpoint.TrimStart('/'))" # Add query parameters if provided if ($QueryParameters -and $QueryParameters.Count -gt 0) { $queryString = ($QueryParameters.GetEnumerator() | ForEach-Object { "$($_.Key)=$([System.Web.HttpUtility]::UrlEncode($_.Value))" }) -join "&" $requestUri = "$requestUri?$queryString" } } Write-Verbose "Request URI: $requestUri" Write-Verbose "Method: $Method" # Build headers based on auth method $headers = @{ 'Accept' = 'application/json' } # Build request parameters $requestParams = @{ Uri = $requestUri Method = $Method Headers = $headers ContentType = 'application/json' } # Add authentication based on method if ($authMethod -eq 'Bearer') { $headers['Authorization'] = "Bearer $($Script:HiddenApiToken.AccessToken)" Write-Verbose "Added Bearer token to Authorization header" } elseif ($authMethod -eq 'Cookie') { # Build cookie string $cookieString = ($Script:HiddenApiCookies.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join "; " $headers['Cookie'] = $cookieString Write-Verbose "Added cookies to request: $($Script:HiddenApiCookies.Keys -join ', ')" # Add XSRF token as header if present (required by ASP.NET Core) $xsrfToken = $Script:HiddenApiCookies['XSRF-TOKEN'] if ($xsrfToken) { $headers['X-XSRF-TOKEN'] = $xsrfToken $headers['RequestVerificationToken'] = $xsrfToken Write-Verbose "Added XSRF token to headers" } # Use WebSession for better cookie handling $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession foreach ($cookie in $Script:HiddenApiCookies.GetEnumerator()) { $cookieObj = New-Object System.Net.Cookie $cookieObj.Name = $cookie.Key $cookieObj.Value = $cookie.Value $cookieObj.Domain = ([System.Uri]$requestUri).Host $session.Cookies.Add($cookieObj) } $requestParams['WebSession'] = $session } # Add body for POST/PUT/PATCH if ($Body -and $Method -in @('POST', 'PUT', 'PATCH')) { $jsonBody = $Body | ConvertTo-Json -Depth 10 $requestParams.Body = $jsonBody Write-Verbose "Request body: $jsonBody" } # Execute request $response = Invoke-RestMethod @requestParams -ErrorAction Stop return $response } catch { $statusCode = $_.Exception.Response.StatusCode.value__ Write-Error "API request failed: $($_.Exception.Message)" Write-Verbose "Status code: $statusCode" # Try to get error details if ($_.ErrorDetails.Message) { Write-Verbose "Error details: $($_.ErrorDetails.Message)" } throw } } } |