Skip to content

API Examples

List Agents

# Query the TRMM API with PowerShell
. .\dotenv.ps1

$Agents_Endpoint = GetURL("agents")
$Agents = Invoke-RestMethod $Agents_Endpoint -Headers $Headers -ContentType "application/json" -Method "Get"
# $Agents | ConvertTo-Json
$Agents.foreach({ $_ }) | Select-Object Agent_ID, Hostname, Site_Name, Client_Name, Monitoring_Type | Format-Table

List Tasks

# Query the TRMM API with PowerShell
. .\dotenv.ps1

$Tasks_Endpoint = GetURL("tasks")
$Response = Invoke-RestMethod $Tasks_Endpoint -Headers $Headers -ContentType "application/json" -Method "Get"
# $Response | ConvertTo-Json
$Response.foreach({ $_ }) | Select-Object ID, Policy, Name, Custom_Field | Format-Table

Get Custom Fields

$TRMM_API_Base_Endpoint = Read-Host "Please provide your API domain i.e https://api.domain.com"
$TRMM_API_Key = Read-Host "Please provide your API Key"

$TRMM_API_CustomFields_Endpoint = $TRMM_API_Base_Endpoint + "/core/customfields"

$TRMM_Request_Headers = @{

    "X-API-KEY" = $TRMM_API_Key

}

$Request = Invoke-RestMethod $TRMM_API_CustomFields_Endpoint -Headers $TRMM_Request_Headers -Method Get

$TRMM_Agent_ExistingCustomFields = ($Request | ? {$_.model -eq 'Agent'})

# Gets all of the Windows Features and places it in an array to parse later
# This is to ensure that TRMM has all Windows Feature roles available as a Custom Field
$AllWindowsFeatures = Get-WindowsFeature
$TRMM_Role_AllCustomFields = @()

foreach($WindowsFeature in $AllWindowsFeatures){

    if($WindowsFeature.Depth -eq 1){

        $PrimaryRole = $WindowsFeature.DisplayName

    } else {

        $TRMM_Role_AllCustomFields += [PSCustomObject]@{

            "Role" = $PrimaryRole
            "Value" = $WindowsFeature.DisplayName

        }

    }

}

$Unique_Roles = $TRMM_Role_AllCustomFields | Select -Unique Role
#Loop through each WindowsFeature
foreach($WindowsFeature in $Unique_Roles){

    if($TRMM_Agent_ExistingCustomFields.Name -like "*$($WindowsFeature.Role)*"){

        # Need to check if it also contains the value

    } else {

        # Check to see if the Role is an Array or an Object
        # Convert to Array otherwise

        if((($TRMM_Role_AllCustomFields | ? {$_.Role -eq $WindowsFeature.Role}).Value) -isnot [array]){

            $CustomFieldOptions = @(($TRMM_Role_AllCustomFields | ? {$_.Role -eq $WindowsFeature.Role}).Value)

        } else {

            $CustomFieldOptions = ($TRMM_Role_AllCustomFields | ? {$_.Role -eq $WindowsFeature.Role}).Value

        }

        # Create the Custom Field body
        $CustomFieldData = @{

            model = "agent"
            name = "Server Role - " + $WindowsFeature.Role
            type = "multiple"
            options = $CustomFieldOptions

        }

        $CreateCustomField = Invoke-RestMethod $TRMM_API_CustomFields_Endpoint -Headers $TRMM_Request_Headers -Method Post -Body ($CustomFieldData | ConvertTo-Json) -ContentType "application/json"

    }
}

Get Custom Fields

https://discord.com/channels/736478043522072608/888474369544847430/1004184193078669522

import requests
import json

API = "https://api.example.com"
HEADERS = {
    "Content-Type": "application/json",
    "X-API-KEY": "changeme",
}

payload = {"custom_fields": [{"string_value": "66666", "field": 2}]}

r = requests.put(
    f"{API}/agents/IcAhWvOHlADwjuYmndAkjbJuhSdWvWjtvYcYjCZk/",
    data=json.dumps(payload),
    headers=HEADERS,
)

print(r.__dict__)

Create Collector Tasks

https://discord.com/channels/736478043522072608/888474369544847430/1004383584070676610

# Query the TRMM API with PowerShell

# Add this to dotenv.ps1
#$TRMM_API_KEY = ""
#$TRMM_API_DOMAIN = ""
. .\dotenv.ps1

function GetURL {
    param (
        [string] $Path
    )
    return "https://${TRMM_API_DOMAIN}/${Path}/"
}

$Headers = @{
    'X-API-KEY' = $TRMM_API_KEY
}

$Collector = [PSCustomObject]@{
    policy = 1
    actions = @(@{
        name = "Collector - Location - City and State"
        type = "script"
        script = 121
        timeout = 90
        script_args = @()
    })
    assigned_check = $null
    custom_field = 14
    name = "Get city and state from API"
    # expire_date = $null
    run_time_date = "2022-08-02T08:00:00Z"
    # run_time_bit_weekdays = $null
    weekly_interval = 1
    daily_interval = 1
    # monthly_months_of_year = $null
    # monthly_days_of_month = $null
    # monthly_weeks_of_month = $null
    # task_instance_policy = 0
    # task_repetition_interval = $null
    # task_repetition_duration = $null
    # stop_task_at_duration_end = $false
    # random_task_delay = $null
    # remove_if_not_scheduled = $false
    run_asap_after_missed = $true
    task_type = "daily"
    alert_severity = "info"
    # collector_all_output = $false
    # continue_on_error = $false
}

$Tasks_Endpoint = GetURL("tasks")
$Payload = $Collector | ConvertTo-Json
$Response = Invoke-RestMethod $Tasks_Endpoint -Headers $Headers -ContentType "application/json" -Method "Post" -Body $Payload
if (! $?) {
    Write-Output "Failed to submit request: $($error[0].ToString() )"
    Write-Output "Stacktrace: $($error[0].Exception.ToString() )"
    Exit(1)
}
$Response | ConvertTo-Json

Show Tasks

# Query the TRMM API with PowerShell
. .\dotenv.ps1

$Tasks_Endpoint = GetURL("tasks")
$Response = Invoke-RestMethod $Tasks_Endpoint -Headers $Headers -ContentType "application/json" -Method "Get"
# $Response | ConvertTo-Json
$Response.foreach({ $_ }) | Select-Object ID, Policy, Name, Custom_Field | Format-Table

Show Task/collector results

# Query the TRMM API with PowerShell
. .\dotenv.ps1

$Agents_Endpoint = GetURL("agents")
$Agents = Invoke-RestMethod $Agents_Endpoint -Headers $Headers -ContentType "application/json" -Method "Get"
# $Agents | ConvertTo-Json
# $Agents.foreach({ $_ }) | Select-Object Agent_ID, Hostname, Site_Name, Client_Name, Monitoring_Type | Format-Table
$Agent_ID = "AGENT ID FROM TABLE"

$Agents.foreach({
    if ($_.Agent_ID -ne $Agent_ID) {
        return
    }
    $Fields_Endpoint = GetURL("agents/$($_.Agent_ID)/tasks")
    $Fields = Invoke-RestMethod $Fields_Endpoint -Headers $Headers -ContentType "application/json" -Method "Get"
    # $Fields | ConvertTo-Json
    $Fields.foreach({ $_ }) | Select-Object ID, Check_Name, Name, Actions, Task_Result | Format-Table
})

or to see results

# Query the TRMM API with PowerShell
. .\dotenv.ps1

$Agents_Endpoint = GetURL("agents")
$Agents = Invoke-RestMethod $Agents_Endpoint -Headers $Headers -ContentType "application/json" -Method "Get"
# $Agents | ConvertTo-Json
# $Agents.foreach({ $_ }) | Select-Object Agent_ID, Hostname, Site_Name, Client_Name, Monitoring_Type | Format-Table
$Agent_ID = "AGENT ID FROM TABLE"

$Agents.foreach({
    if ($_.Agent_ID -ne $Agent_ID) {
        return
    }
    $Fields_Endpoint = GetURL("agents/$($_.Agent_ID)/tasks")
    $Fields = Invoke-RestMethod $Fields_Endpoint -Headers $Headers -ContentType "application/json" -Method "Get"
    # $Fields | ConvertTo-Json
    $Fields.foreach({
        $_.Task_Result | Format-Table
     })
})

List Custom Fields of Agent

# Query the TRMM API with PowerShell
. .\dotenv.ps1

$Agent_ID = "ENTER AGENT ID HERE"
$Agents_Endpoint = GetURL("agents/$($Agent_ID)")
$Agents = Invoke-RestMethod $Agents_Endpoint -Headers $Headers -ContentType "application/json" -Method "Get"
ConvertTo-Json $Agents.Custom_Fields
$Agents.Custom_Fields

Update Custom Fields

https://discord.com/channels/736478043522072608/888474369544847430/1004422340815360111

This will update the custom fields for an agent. The field value come from the custom fields two posts above. Other fields that exist will not be updated. If you want to update only 1 custom field, use only 1 entry in the array.

# Query the TRMM API with PowerShell
. .\dotenv.ps1

$Agent_ID = "AGENT ID GOES HERE"
$Endpoint = GetURL("agents/$($Agent_ID)")

$Custom_Fields = [PSCustomObject]@{
    custom_fields = @(
        @{
            field = 13
            string_value = "Value updated from the API"
        },
        @{
            field = 14
            string_value = "Another value from the API"
        }
    )
}

$Payload = $Custom_Fields | ConvertTo-Json
$Response = Invoke-RestMethod $Endpoint -Headers $Headers -ContentType "application/json" -Method Put -Body $Payload
if (! $?) {
    Write-Output "Failed to submit request: $($error[0].ToString() )"
    Write-Output "Stacktrace: $($error[0].Exception.ToString() )"
    Exit(1)
}
$Response | ConvertTo-Json

List service information from Agent

# Query the TRMM API with PowerShell
. .\dotenv.ps1

$Payload = [PSCustomObject]@{
    code = [string](Get-Content -Path .\TRMM-Run-Cmd-Remote.ps1 -Raw -Encoding UTF8)
    timeout = 90
    args = @()
    shell = "powershell"
}

$Agent_ID = "ENTER AGENT ID HERE"
$Rest_Params = @{
    Uri = GetURL("scripts/${Agent_ID}/test")
    Headers = $Headers
    ContentType = "application/json"
    Method = "POST"
    Body = $Payload | ConvertTo-Json
}
# Write-Output $Rest_Params["Body"]

# Prefer to handle the error ourselves rather than fill the screen with red text.
$ErrorActionPreference = 'SilentlyContinue'

$Response = Invoke-RestMethod @Rest_Params
if (! $?) {
    Write-Output "Failed to submit request: $($error[0].ToString() )"
    Write-Output "Stacktrace: $($error[0].Exception.ToString() )"
    Exit(1)
}
# $Response | ConvertTo-Json
$Services = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Response))
ConvertFrom-Json $Services

Combining scripts

This will iterate over all agents, filter out non-Windows and offline agents, run the PowerShell above to the first 5 services that are running, and compile the information in a Servers array.

# Query the TRMM API with PowerShell
. .\dotenv.ps1

# Default parameters. Body is missing because GET does not include the body.
$Rest_Params = @{
    Uri = ""
    Headers = $Headers
    ContentType = "application/json"
    Method = "GET"
}

# Prefer to handle the error ourselves rather than fill the screen with red text.
$ErrorActionPreference = 'SilentlyContinue'

$Rest_Params["Uri"] = GetURL("agents")
$Agents = Invoke-RestMethod @Rest_Params
if (! $?) {
    Write-Output "Failed to get list of agents from API: $($error[0].ToString() )"
    Write-Output "Stacktrace: $($error[0].Exception.ToString() )"
    Exit(1)
}

# Remote command to run.
$Payload = [PSCustomObject]@{
    # Without the type casting to [string], this was returning properties relating to Get-Content.
    code = [string](Get-Content -Path .\TRMM-Run-Cmd-Remote.ps1 -Raw -Encoding UTF8)
    timeout = 90
    args = @()
    shell = "powershell"
}

$Rest_Params["Body"] = [string]($Payload | ConvertTo-Json)
$Rest_Params["Method"] = "POST"
$Servers = @()
foreach($Agent in $Agents) {
    # Build this object to match your needs.
    $Server = [PSCustomObject]@{
        Agent_ID = $Agent.agent_id
        # Services = "Error / Unavailable"
        Service_Name = "N/A"
        Service_Status = "N/A"
        Service_DisplayName = "N/A"
        Hostname = $Agent.hostname
        Platform = $Agent.plat
        Status = $Agent.status
    }

    if ($Agent.plat -ne "windows" -or $Agent.status -ne "online") {
        # Only process online Windows agents. Add other checks as necessary.
        # Uncomment this if you want to include filtered servers in the output.
        # $Servers += $Server.PSObject.Copy()
        continue
    }

    $Rest_Params["Uri"] = GetURL("scripts/$($Agent.Agent_ID)/test")
    $Response = Invoke-RestMethod @Rest_Params
    if (! $?) {
        $Servers += $Server.PSObject.Copy()
        continue
    }

    # Create a new server object for every item returned from the remote agent (Services in this case).
    $Services = ConvertFrom-Json ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Response)))
    foreach($Service in $Services) {
        $Server.Service_Name = $Service.Name
        $Server.Service_Status = $Service.Status
        $Server.Service_DisplayName = $Service.DisplayName
        $Servers += $Server.PSObject.Copy()
    }
}

$Servers | Format-Table Agent_ID, Hostname, Status, Service_Name, Service_Status, Service_DisplayName
# $Servers

Pulling Audit Logs

<?php
$ApiKey = "XXX";
$BasicUrl = "https://api.trmm.xxx.xxx/";
$Url = "/logs/audit/";
$curl = curl_init();

curl_setopt($curl, CURLOPT_URL, $BasicUrl.$Url);

$payload = '{"pagination":{"sortBy":"entry_time","descending":true,"page":1,"rowsPerPage":1000,"rowsNumber":2},"agentFilter":["QFhdnzJcFnodMibCGJYmfIwRyjgIazjajjMSvVWX"],"actionFilter":["modify"]}';
curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

$headers = ['Content-Type: application/json', 'X-API-KEY: '.$ApiKey];
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);

curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PATCH');

$data = curl_exec($curl);

curl_close($curl);

$audit = json_decode($data, true);

var_dump($audit);

?>

Update software using API

#!/rmm/api/env/bin/python

import concurrent.futures
import requests

API = "https://api.CHANGEME.com"
API_KEY = "CHANGEME"

HEADERS = {
    "Content-Type": "application/json",
    "X-API-KEY": API_KEY,
}

MAX_WORKERS = 10


def create_list_of_urls():
    ret = []
    agents = requests.get(f"{API}/agents/?detail=false", headers=HEADERS)
    for agent in agents.json():
        ret.append(f"{API}/software/{agent['agent_id']}/")  # refresh software endpoint

    return ret


def do_software_refresh(url):
    r = requests.put(url, headers=HEADERS)
    return r.json()


with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
    futures = []
    for url in create_list_of_urls():
        futures.append(executor.submit(do_software_refresh, url))

    for future in concurrent.futures.as_completed(futures):
        print(future.result())