Forum Discussion
<#
------------------------------
LogicMonitor Subgroup NOC Item population
for NOC Widget Dashboards
------------------------------
Created by Cole McDonald (Netgain Technologies, LLC)
17 October 2023
------------------------------
Netgain and the Author are not responsible for any issues this may occur in your environment due
to the use of this script in whole or part. Please retain this comment block in your deployment.
------------------------------
This DS should have an appliesTo() that targets a single collector
The collector should have its script timeouts increased to account for
longer run times reaching out through the web to the API.
You can have this update multiple dashboards as it's grabbing all of the API data necessary,
minimizing the # of times you have to grab that data will reduce resource consumption on the collector.
------------------------------
#>
# Add your logicmonitor portal company name for the URL
$company = ""
$URLBase = "https://$company.logicmonitor.com/santaba/rest"
# These properties are defined somewhere that can be picked up by the device targeted by the appliesTo()
$accessID = "##ApiAccessID.key##"
$accessKey = "##ApiAccessKey.key##"
# This is the ID (integer) of the group containing the subgroups you want displayed.
# This needs to be changed to target based on name/path
$parentGroupID = 0
# The "name" property is the name of the Dashboard being targeted.
#!!! If you have more than one dashboard to update, duplicate this and
#!!! change the $masterdash and name. If you have questions on this,
#!!! reach out on the LM Community and I can show you what I've done.
$masterDash = @{ id=0; widgetid=0; name="NOC - Master" }
#region Initialization and Functions
#-------- The Functions ----------
function generateJSON {
param(
$dashInfo,
$clientnames,
$deviceDisplayName = "*",
$DSDisplayName = "*"
)
$itemArray = @()
foreach ($name in $clientnames) {
$itemArray += @{
"type" = "device"
"deviceGroupFullPath" = "Clients/$name"
"deviceDisplayName" = $deviceDisplayName
"dataSourceDisplayName" = $DSDisplayName
"instanceName" = "*"
"dataPointName" = "*"
"groupBy" = "deviceGroup"
"name" = "`#`#RESOURCEGROUP`#`#"
}
}
# Write JSON back to the API for that widget
$outputJSON = "`n`t{`n`t`t`"items`" : [`n"
foreach ($item in $itemArray) {
$elementJSON = @"
{
`"type`" : `"$($item.type)`",
`"dataPointName`" : `"$($item.dataPointName)`",
`"instanceName`" : `"$($item.instanceName)`",
`"name`" : `"$($item.name)`",
`"dataSourceDisplayName`" : `"$($item.dataSourceDisplayName)`",
`"groupBy`" : `"$($item.groupBy)`",
`"deviceGroupFullPath`" : `"$($item.deviceGroupFullPath)`",
`"deviceDisplayName`" : `"$($item.deviceDisplayName)`"
}
"@
if ($item -ne $itemArray[-1]) {
$outputJSON += "$elementJSON,`n"
} else {
# Last Item
$outputJSON += "$elementJSON`n`t`t]`n`t}"
}
}
write-output $outputJSON
}
function Send-Request {
param (
$cred ,
$URL ,
$accessid = $null,
$accesskey = $null,
$data = $null,
$version = '3' ,
$httpVerb = "GET"
)
if ( $accessId -eq $null) { exit 1 }
<# 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 #>
$response = Invoke-RestMethod `
-Uri $URL `
-Method $httpVerb `
-Body $data `
-Header $headers `
-erroraction SilentlyContinue `
-warningaction SilentlyContinue
Return $response
}
function Get-LMRestAPIObjectListing {
param (
$URLBase ,
$resourcePathRoot , # "/device/devices"
$size = 1000 ,
$accessKey ,
$accessId ,
$version = '2'
)
$output = @()
$looping = $true
$counter = 0
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
}
#endregion
# This could be more concise a grab with stronger filters,
# that's a future problem, this works and isn't terribly slow
#region Get Dashboards
$resourcePath = "/dashboard/dashboards"
$dashboards = Get-LMRestAPIObjectListing `
-resourcePathRoot $resourcePath `
-accessKey $accessKey `
-accessId $accessID `
-URLBase $URLBase
#endregion
#region Get Widgets
$resourcePath = "/dashboard/widgets"
$widgets = Get-LMRestAPIObjectListing `
-resourcePathRoot $resourcePath `
-accessKey $accessKey `
-accessId $accessID `
-URLBase $URLBase
#endregion
#region Get Groups
$resourcePath = "/device/groups"
$Groups = Get-LMRestAPIObjectListing `
-resourcePathRoot $resourcePath `
-accessKey $accessKey `
-accessId $accessID `
-URLBase $URLBase
#endregion
# Get Client Names from groups
$clientnames = (
$groups `
| where parentid -eq $parentGroupID `
| where name -notmatch "^\."
).name | sort
#!!! If you have more than one dashboard, duplicate this and change the $masterdash
#!!! to other variables defined at the start of the script.
# ID Master Dashboard
$master = $dashboards | ? name -eq $masterDash.name
if (($master.name).count -eq 1) {
$masterDash.id = $master.id
$masterDash.widgetid = $master.widgetsConfig[0].psobject.Properties.name
$outputJSON = generateJSON `
-dashInfo $masterDash `
-clientnames $clientnames
$resourcePath = "/dashboard/widgets/$($masterDash.widgetid)"
$url = $URLBase + $resourcePath
$widget = Send-Request `
-accessKey $accessKey `
-accessId $accessID `
-data $outputJSON `
-URL $URL `
-httpVerb "PATCH"
}
Here’s the code I’m using to populate these Dashboards. The new UI (v4) displays this better if you expand/maximize the widget to fill the page (although filtering out SDT’d alerts doesn’t work currently). Dashboard is just a single widget dashboard with a NOC widget that fills the page. The only two non-dynamic parts are the name of the dashboard, which goes in $masterDash.name and the ID of the group you want as your source for the subgroups… in our case it’s the “/Clients” group… so the ID number from that goes in $parentGroupID
This is built off of my old API exploration / report generation powershell functions, which is, in turn, built from the API + Powershell documentation from LM. This doesn't require any external modules to be added to use the API, so can be deployed against a collector with no prior software installs needed beyond what you’ll already have done.
I recommend targeting your dataSource to a single collector, then increasing the timeout for the scripts. At some point, I’m going to add pre-filtering to the initial API requests for groups and dashboards to speed those parts a little bit, but it’s not too bad currently while grabbing everything.
DS runs 1/day to populate the widget, feel free to have it hit more frequently if needed. Only two datapoints currently, exitCode and executionTime. I’m probably going to add groupCount at some point.
Enjoy.
Related Content
- 5 months ago
- 7 months ago
- 18 days ago