Created
July 14, 2025 22:49
-
-
Save davelee212/ad16315a977165c065156c78041eb767 to your computer and use it in GitHub Desktop.
lm-get-service-now-info.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function invoke-lmapi { | |
| # Function to interact with the LogicMonitor API. | |
| # DL 14/01/23 - Does not handle paging at the moment so will only return the first 50 results in large resultsets. This hasn't been a problem so far as I've been using | |
| # queries for individual items but something that the function does need to be able to deal with. | |
| param ( | |
| [parameter(Mandatory)][string]$company, | |
| [parameter(Mandatory)][string]$accessId, | |
| [parameter(Mandatory)][string]$accessKey, | |
| [parameter(Mandatory)][string]$httpVerb, | |
| [parameter(Mandatory)][string]$resourcePath, | |
| [parameter()][string]$queryString, | |
| [parameter()][string]$body, | |
| [parameter()][string]$downloadTarget | |
| ) | |
| <# Use TLS 1.2 #> | |
| [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 | |
| $url = 'https://' + $company + '.logicmonitor.com/santaba/rest' + $resourcePath + $queryString | |
| # Get the current epoch time | |
| $epoch = [Math]::Round((New-TimeSpan -start (Get-Date -Date "1/1/1970") -end (Get-Date).ToUniversalTime()).TotalMilliseconds) | |
| # Encode the access key | |
| $hmac = New-Object System.Security.Cryptography.HMACSHA256 | |
| $hmac.Key = [Text.Encoding]::UTF8.GetBytes($accessKey) | |
| # Put everything together into $requestvars to be used as part of the authentication hash | |
| if ($body) { | |
| $requestVars = $httpVerb + $epoch + $body + $resourcePath | |
| } else { | |
| $requestVars = $httpVerb + $epoch + $resourcePath | |
| } | |
| # Create signature for this request using the access key to hash it | |
| $signatureBytes = $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($requestVars)) | |
| $signatureHex = [System.BitConverter]::ToString($signatureBytes) -replace '-' | |
| $signature = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($signatureHex.ToLower())) | |
| # Create dictionary to contain headers | |
| $auth = 'LMv1 ' + $accessId + ':' + $signature + ':' + $epoch | |
| $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" | |
| $headers.Add("Authorization",$auth) | |
| $headers.Add("X-Version",'3') | |
| $headers.Add("Content-Type",'application/json') | |
| # Make the request to the API (downloading a file or adding $body if it was passed) | |
| # If the request fails with a 429 "Too Many Requests" error code, then we're likely hammering the API too hard, so we wait 10 seconds and retry | |
| Do { | |
| $errorCode = 0 | |
| try { | |
| if ($downloadTarget) { | |
| $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Header $headers -OutFile $downloadTarget -TimeoutSec 300 -StatusCodeVariable statuscode | |
| } elseif ($body) { | |
| $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Header $headers -Body $body -TimeoutSec 30 -StatusCodeVariable statuscode | |
| } else { | |
| $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Header $headers -TimeoutSec 30 -StatusCodeVariable statuscode | |
| } | |
| } catch { | |
| Write-Host "Error: $_" | |
| [int]$errorCode = $_.exception.response.statusCode | |
| # Write-Host "Error code: $errorCode" | |
| } | |
| if ($errorCode -eq 429) { | |
| Write-Host "429 Too Many Requests - Waiting 10 seconds and retrying..." | |
| Start-Sleep -Seconds 10 | |
| } | |
| } while ($errorCode -eq 429) | |
| # Return the response from the API | |
| return $response | |
| } | |
| $company = "your-account-name" # Replace with your LogicMonitor company name | |
| $accessId = "your-access-id" # Replace with your LogicMonitor access ID | |
| $accessKey = "your-access-key" # Replace with your LogicMonitor access key | |
| $daysAgo = 2 # Number of days to look back for alerts | |
| # calculates the epoch time for the start of the query | |
| $startEpoch = (Get-Date (Get-Date).AddDays(-$daysAgo) -UFormat %s) | |
| # query string includes the ##EXTERNALTICKETID# custom column and filters for alerts created after the startEpoch. Will get the first 1000 results, you'll need to put a loop in to handle paging if you expect more than that. | |
| $queryString = ('?customColumns=%23%23EXTERNALTICKETID%23%23&size=1000&offset=0&filter=startEpoch>:' + $startEpoch) | |
| # call the LogicMonitor API to get the alerts | |
| $alerts = (invoke-lmapi ` | |
| -company $company ` | |
| -accessId $accessId ` | |
| -accessKey $accessKey ` | |
| -httpVerb "GET" ` | |
| -resourcePath "/alert/alerts" ` | |
| -queryString $queryString).items | |
| # Filter the alerts to only include those that went to a rule where the name includes "Service Now" and that were not suppressed (e.g. not SDT at the time) | |
| # Does a bit of manipulation to extract the ServiceNow Incident ID from the custom column ##EXTERNALTICKETID## | |
| $alerts | where-object {$_.rule -like "*Service Now*" -And !($_.suppressor)} | Select-Object ` | |
| @{Name="Alert ID";Expression={$_.id}}, ` | |
| @{Name="InternalId";Expression={$_.InternalId}}, ` | |
| @{Name="External_Ticket_ID_RAW";Expression={$_.customColumns.'##EXTERNALTICKETID##'}}, ` | |
| @{Name="SNOWIncidentId";Expression={$_.customColumns.'##EXTERNALTICKETID##'.split(':')[1].trim()}}, ` | |
| @{Name="SNOWLinks";Expression={$_.alertExternalTicketUrl.'servicenowIncidentLinks'}} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment