Cole McDonald

  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by Cole McDonald

  1. Yeah, I'm using the ID of each bit exclusively. I figured we need the instance IDs as they apply to the specific resource rather than the id of the DS itself... which would be the same for every instance of that DS as applied to any resource. I've built quite a few Relational DB driven apps I've spent my time chasing IDs... and then banging my head against my desk once the "obvious" problem was unconvered. The instance shows the deviceID, the deviceDataSourceID, and the instanceIDs as their own properties in the returned JSON (here parsed into a PSObject). that dds ID should be the specific ID of the DS as applied to that device. Everything seems to be correct on the walk up to the "/data" I'm a little surprised we can't just get "/instance/#####/data" from it as a direct access to the endpoint of the walk since it's a unique ID for the instance itself. Overly complicated data access method that is prone to error and misinterpretation.
  2. I'll be getting postman running tomorrow... my time was up and had to move to another effort. Thank you for pointing the tool out to me. It looks really useful. and thanks for poking at my code. I'll verify I have the right IDs for each of those pieces as well. (Funny Note... I typed this up yesterday and didn't hit submit). I've verified that I have the right pieces and parts. You're examining the JSON in your examples... I use Powershell ISE and explore the return data using the variables. I have verified that I'm using the deviceDataSource rather than the dataSource: PS C:\Users\cole.mcdonald> $inst_CPU id : 8976881 dataSourceId : 5725412 deviceDataSourceId : 427972 groupId : 538636 groupName : @default name : Perfmon_CPU-_Total displayName : _Total description : lockDescription : False deviceId : 5378 and my call was: /device/devices/5378/devicedatasources/427972/instances/8976881/data (I switched devices to verify that it wasn't just empty) I've tried grabbing data from both the instance directly as well as the graph data. I'm going to dig into postman... but looking at it, I don't know that it will necessarily show me anything different than I'm getting using straight powershell... and I generally dislike GUI'd applications for this sort of thing as I find them horribly inefficient (I'm old and grew up on CLI). Here's my Postman return: { "dataSourceName": "Perfmon_CPU", "dataPoints": [ "PercentProcessorTimeCounter", "PercentProcessorTime" ], "values": [], "time": [], "nextPageParams": "" } So it looks like it's succeeding in the call, just no data returned. Odd, because the graph shows that there should be historical data there to grab.
  3. No idea... I've never used it... I've only ever used the PS functions I'm currently using... including to get other data previously. Although I'm not sure if I was using V1 or V2 at that time.
  4. i'm trying to get to this data using Powershell... no errors, it successfully grabs the device, DS, and instances using the same functions... but the data returns an empty array for data that is present in the LM resources interface... thoughts? $resourcePath = "/device/devices/$($$($$($" my output shows me this: Gathering /device/devices/6966/devicedatasources/503122/instances/9311756/data No errors are thrown during the run. It's confounding me a bit.
  5. Here's a bit of PS that goes at the head of my scripts for RESTAPI work... uses Credential Manager 2.0 with the restAPI user/key stored in the windows cred store: Import-Module CredentialManager function Send-Request { param ( $cred , $URL , $accessid = $null, $accesskey = $null, $data = $null, $version = '2' , $httpVerb = "GET" ) if ( $accessId -eq $null) { $accessId = $cred.UserName $accessKey = $cred.GetNetworkCredential().Password } # Use TLS 1.2 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Get current time in milliseconds $epoch = [Math]::Round( ( New-TimeSpan ` -start (Get-Date -Date "1/1/1970") ` -end (Get-Date).ToUniversalTime()).TotalMilliseconds ) # Concatenate Request Details $requestVars = $httpVerb + $epoch + $data + $resourcePath # Construct Signature $hmac = New-Object System.Security.Cryptography.HMACSHA256 $hmac.Key = [Text.Encoding]::UTF8.GetBytes( $accessKey ) $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() ) ) # Construct Headers $auth = 'LMv1 ' + $accessId + ':' + $signature + ':' + $epoch $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.Add( "Authorization", $auth ) $headers.Add( "Content-Type" , 'application/json' ) # uses version 2 of the API $headers.Add( "X-version" , $version ) # Make Request try { $response = Invoke-RestMethod ` -Uri $URL ` -Method $httpVerb ` -Body $data ` -Header $headers ` -erroraction stop ` -warningaction SilentlyContinue } catch { $response = -1 } Return $response } function Get-LMRestAPIObjectListing { param ( $URLBase , $resourcePathRoot , # "/device/devices" $size = 1000 , $accessKey , $accessId , $version = '2' ) write-host "Gathering $resourcePathRoot" $output = @() $looping = $true $counter = 0 # write-host "Gathering data from $resourcePathRoot" while ($looping) { # Re-calc offset based on iteration $offset = $counter * $size $resourcePath = $resourcePathRoot $queryParam = "?size=$size&offset=$offset" $url = $URLBase + $resourcePath + $queryParam # Make Request $response = Send-Request ` -accesskey $accessKey ` -accessid $accessId ` -URL $url ` -version $version if ( $response.items.count -eq $size ) { # Return set is full, more items to retrieve $output += $response.items $counter++ } elseif ( $response.items.count -gt 0 ) { # Return set is not full, store date, end loop $output += $response.items $looping = $false } else { # Return set is empty, no data to store, end loop $looping = $false } } write-output $output } $company = "<CompanyName>" $URLBase = "https://$" # This will resolve to proper values if it's being run from inside LM $accessID = "##Logicmonitor.AccessID.key##" $accessKey = "##Logicmonitor.AccessKey.key##" if ( $accessID -like "##*" ) { # Not being run from inside LM - populate manually for testing Import-Module CredentialManager $Cred = Get-StoredCredential -Target LogicMonitor $accessID = $cred.UserName $accessKey = $Cred.GetNetworkCredential().Password } # Get Devices $resourcePath = "/device/devices" $Devices = Get-LMRestAPIObjectListing ` -resourcePathRoot $resourcePath ` -accessKey $accessKey ` -accessId $accessID ` -URLBase $URLBase I wrote most of it using the standard "Send-Request" piece from LM's docs and the posts here... but added a bit to work around the request limit to allow me to fill a variable with all of the specific piece I'm looking for. It also allows for storing creds for dev that are also in LM properties for deployment. They're stored in a generic windows credential named "LogicMonitor" locally. To expand what it does, change or duplicate the "Get Devices" section to target a different $resourcePath from the swagger doc for the restAPI. DISCLAIMER: this code comes with no guarantees from myself or my employers past and present that it is safe in your environment. Use it with caution as you always should.
  6. From the Device perspective, what do the three(?) elements of the sdtstatus mean? Looking at a device, I see "none-none-none" or something like "none-SDT-none" where one of the elements is replaced with SDT. @Sarah Terry?
  7. I've implemented this in the past as a dataSource for tracking number of failed connection attempts against a server over a 5 minute period. Powershell that grabs the last 5 minutes of 4625 from the windows security log where the message contains the status for bad username or bad password. It just returns a count rather than individual events. This let me drive a NOC widget of devices to show brute force intrusion attempts. This could potentially be added like a cluster alert using the existing eventSource though and help to combine individual events into a single actionable alert to reduce noise. This is a super old thread, but I just came across it.... so I'll add my $.02
  8. I'm not sure where it came from. I'm no longer at Beyond Impact, so don't have access to that deployment any longer. I didn't actually perform the deployment of LM at that location, so I'm not sure how it got in the DS list there. It was originally written by an LM developer, but the code above should allow you to create your own implementation of it from scratch.
  9. Thanks guys. Will this allow me to add customProperties or am I locked into accessing existing customProperties?
  10. Has anyone had any success using the v2 of the REST API to push a PATCH to add a customer property to an object (in this case /device/groups/##### without wiping out any other custom properties that have already been set? We've run into a curious behavior while trying to add a property to feed out integration to our ticketing system. It seems to replace the whole of the custom properties object, not just the child object within the customproperties level of the json: { `"customProperties`" : [{ `"name`" : `"connectwisev2.companyid`", `"value`" : `"$($integrationID.TrimEnd())`" }] } We had 2-3 other properties in there that disappeared after patching.
  11. Here's the link: and yes? I don't quite recall how to have it return a NaN, but I have done it in the past, so I know it's possible. unkn() is a constant for it, that may have been how I did it... so: if(maxrtt>100,1,unkn()) ?
  12. If you have a website built that will take a URL structure that can be married to device/instance property values, you can have the alert generate the URL form the inciting instances properties to direct you to the appropriate page. You may need to build out a redirection page within your site that receives and interprets those URLs for you. ##DATASOURCE## might be the right token to use for building that decision/redirection tree.
  13. ACK should be removable if determined it was checked incorrectly by a user.
  14. That's what I've been doing currently. Any change to it though means you have to change, then distribute that change manually still. So it's pretty much set in stone once you've produced it at scale. The templating would be a way to make changes without having to take this process to that extreme.
  15. You would bring in the ping as a "do not display" value, then make a virtual datapoint that uses an if() to evaluate the >100 and return a NAN for the false condition.
  16. The alerts take you to a specific instance, so it should be possible. URL structure is this: https://<companyName><deviceInstanceNumber> You should be able to derive the instance # from the REST API... but you state that you're a bit of a novice. Might be ##system.instanceid## or just ##instanceID##
  17. My ability to edit the previous post timed out (annoying)... here's the final thing I was going for: if (get-date -format HH -eq 10) { if ( test-path "\\servername\C$\Path\To\File.txt" ) { write-output "1" } else { write-output "0" } } Just checks the 24hr Hour to see if it's 10. Have it fire once an hour.
  18. I don't specifically have a ready made one, but if you make a DS that "AppliesTo" a system.hostname=="oneOfYourCollectors" (make sure it's in the same domain as the resource with the UNC path you want to check). Then something like: if ( test-path "\\servername\C$\Path\To\File.txt" ) { write-output "1" } else { write-output "0" } as a powershell script should do the trick.
  19. With the issues we've been having with collector resource exhaustion, I've been thinking about ways to reduce the amount of dataSources that run on the collectors at any given time. It occurs to me that if there is a host down, all of the datasources are still trying to run against it and having to await timeout before releasing their resources on the collector. I'd like to submit that a Host down status should issue a partial SDT for the device that would prevent all but the host status datasources from running against that device. The host status change could then remove the SDT once it has cleared. This would prevent devices in environments that spin them up / down based on load rather than a schedule from taking up too many collector system resources during their down time. It would also help alleviate strain during larger outages while still providing just the actionable information necessary to address the situation.
  20. or for sites that place dashboards up on big NOC displays at the front of a call center.
  21. No such luck... could you verify that this is the correct URL structure for this @Forrest Evans - LM? Even being really pedantic about it still isn't taking it:,preferredCollectorId same error: Invoke-RestMethod : {"errorMessage":"custom property name cannot be predef.externalResourceID\ncustom property name cannot be predef.externalResourceType\n","errorCode":1404,"errorDetail":null} At C:\Scripts\Balance-LMABCG.ps1:51 char:24 + $response = Invoke-RestMethod ` + ~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
  22. Thank you Forrest. As always, a font of great information. I assume from the reading that the default is "refresh" From the link above (to save folks some clicking): "opType=replace indicates that the properties included in the request payload will be added if they don't already exist, or updated if they do already exist, but all other existing properties will remain the same" I'll be putting this in place to day and I'll report back... hopefully with a new $data block for the script and calling it done!