Getting Started

Endpoints

Patch Management

Vulnerability Management

Software Deployment & IT Assets

Automation & Remote Desktop

Real-Time Reports & Alerts

Account Access & Management

SSO Authentication

Security Concerns

Need Help?

Action1 5 Documentation 5 Preparing Custom Data Source for Alerting

Preparing Custom Data Source for Alerting

In this section:

Understanding Data Sources, Snapshots, and the A1_key Field

A data source is a script that queries the endpoint data and presents it in a structured form, available for reporting or alerting.

An object is an individual item represented by the data source. For example, in a Disk Drive data source, each detected disk drive is a separate object.

A field represents a property of an object, and a field value contains the collected data for that property. A disk drive object may include, for example, the following fields and values:

  • Drive letter: C:
  • Capacity: 500 GB
  • Free space: 120 GB

A snapshot is the collection of all objects and their field values returned by a data source at a specific point in time. For example, a Disk Drive snapshot may contain one object for drive C: and another for drive D:, together with the capacity and free-space values collected for each drive.

The data source script runs at a fixed 10-minute interval. At each execution, the current data is collected, and a new snapshot is created. Action1 can then trigger alerts by comparing consecutive snapshots, as explained later in this section.

A1_key

When creating a data source, you must assign one of the fields to A1_Key.

A1_key is a required field in the data structure that uniquely identifies an object within an endpoint. All other field values may change over time. Objects may also be added or removed. However, the A1_key value for an existing object must remain unchanged.

When providing a script for your custom data source, you specify the data fields in a fixed and consistent order. Remember that Action1 always expects the last field to be the object’s A1_key.

Therefore, to become the A1_key, the field must meet the following requirements:

  • Its value must be unique for each object on the endpoint.
  • It must remain unchanged over time.
  • It must be the last field in the data source field list.

In relational database terminology, A1_key is similar to a primary key. It allows Action1 to identify the same object across consecutive snapshots.

Important! Alert behavior is undefined if two objects have the same A1_key value. The A1_key value must be unique for every object.

Example

The following PowerShell script represents Action1’s built-in Disk Drive data source with a designated A1_Key:

$result = New-Object System.Collections.ArrayList;

try {
    $wmiObject = Get-WmiObject -Namespace ROOT\CIMV2 -Class Win32_DiskDrive;

    try { 
        $helthStatusInfo = Get-PhysicalDisk;
    }
    catch {
        $helthStatusInfo = 'Unknown';
    }

    function convertBytesInGB ($size) {
        $rounding = if ( $size -lt 1Gb ) { 2 } else { 0 };
        $sizeGB = [math]::Round($size / 1Gb, $rounding);

        return $sizeGB;
    }

    if (![string]::IsNullOrEmpty($wmiObject)) {
        $wmiObject | ForEach-Object {
            $currentOutput = "" | Select-Object Model, 'Device ID', 'Interface Type', 'PNP Device Id', 'Serial Number', 'Size (Gb)', Name, 'Error Description', Status, A1_Key;
            $currentOutput.Model = $_.Model;
            $currentOutput.'Device ID' = $_.DeviceID;
            $currentOutput.'Interface Type' = $_.InterfaceType;
            $currentOutput.'PNP Device Id' = $_.PNPDeviceId;
            $currentOutput.'Serial Number' = $_.SerialNumber;
            $currentOutput.'Size (Gb)' = convertBytesInGB $_.Size;
            $currentOutput.Name = $_.Name;
            $currentOutput.'Error Description' = $_.ErrorDescription;
            $currentOutput.Status = if ($_.Status) { $_.Status } else { 'Unknown' };
            $index = $_.Index;

            if ($helthStatusInfo -ne 'Unknown') {
                $status = ($helthStatusInfo | Where-Object { $_.DeviceID -eq $index }).HealthStatus;
                if ($status -and $status -isnot [system.array]) {
                    $currentOutput.Status = $status;
                }
            };
            $currentOutput.A1_Key = $_.PNPDeviceId;

            $result.Add($currentOutput) | Out-Null;
        }
    }
}
catch { $_ }

$result;


The script assigns the disk’s Plug and Play device identifier to A1_Key:

$currentOutput.A1_Key = $_.PNPDeviceId

This PNPDeviceId is used to identify each physical disk across consecutive snapshots. A returned disk object may look like this:

Model: Samsung SSD 980

Device ID: \.\PHYSICALDRIVE0

Interface Type: SCSI

PNP Device Id: SCSI\DISK&VEN_NVME&PROD_SAMSUNG_SSD_980...

Serial Number: ABC123456

Size (Gb): 1000

Name: \.\PHYSICALDRIVE0

Error Description:

Status: Healthy

A1_Key: SCSI\DISK&VEN_NVME&PROD_SAMSUNG_SSD_980...

NOTE: When planning for custom reports, consider that the A1_Key designation is defined by the data source script, not by the report columns. In the script, A1_Key must be the last returned field.

Reports display the data source properties as selectable columns, but the column order in a report does not indicate which property is used as A1_Key. Reordering, adding, or removing report columns does not change the key used to identify objects.

In the example above, the script uses the PNPdeviceID as the value of A1_Key. PNP device ID remains available as a regular report column, while Action1 uses the corresponding A1_Key value internally to match disk objects across snapshots.

How Alerts Use A1_key

The alerting mechanism compares consecutive snapshots to identify:

  • Changes to an object’s field values.
  • Newly added objects.
  • Removed objects.

Action1 uses A1_key to match an object in one snapshot with the same object in the next snapshot.

The following examples show how this works.

Examples: Created, Modified, and Deleted Alerts

Suppose you create a custom data source that provides brief information about the disk drives detected on an endpoint. Each disk drive object contains the following fields:

  • Capacity
  • Free space
  • Drive letter

For this example, we assume that the drive letter uniquely identifies each drive on the endpoint. You can therefore use it as the A1_key for the disk drive object. Thus, the Drive letter must be placed last in the data source field list.

The data source script may look like the one below.

Custom Data Source Example

# ===== Disk Drive Capacity and Free Space - Action1 Custom Data Source ===== 

# Description: Collects the capacity and available free space of each fixed 

# disk detected on a Windows endpoint. The drive letter uniquely identifies 

# each disk object and is used as A1_Key. 


# ----- Result Collection ----- 

$result = New-Object System.Collections.ArrayList 


# ----- Helper Function ----- 

function Convert-BytesToGB { 

    param ( 

        [Parameter(Mandatory = $false)] 

        [Nullable[long]]$Bytes 

    ) 

 

    if ($null -eq $Bytes) { 

        return $null 

    } 

 

    return [math]::Round($Bytes / 1GB, 2) 

} 


# ----- Primary Data Collection ----- 

try { 

    # DriveType 3 represents fixed local disks. 

    $drives = Get-CimInstance -ClassName Win32_LogicalDisk ` 

        -Filter "DriveType = 3" ` 

        -ErrorAction Stop 

 

    foreach ($drive in $drives) { 

        # A1_Key must be the last property. 

        $currentOutput = [PSCustomObject][ordered]@{ 

            ComputerName = $env:COMPUTERNAME 

            CapacityGB   = Convert-BytesToGB -Bytes $drive.Size 

            FreeSpaceGB  = Convert-BytesToGB -Bytes $drive.FreeSpace 

            DriveLetter  = $drive.DeviceID 

            ErrorMessage = "" 

            A1_Key       = $drive.DeviceID 

        } 

 

        [void]$result.Add($currentOutput) 

    } 

} 

catch { 

    # Return an error row if disk information cannot be collected. 

    # A1_Key remains unique for this error object. 

    $errorOutput = [PSCustomObject][ordered]@{ 

        ComputerName = $env:COMPUTERNAME 

        CapacityGB   = $null 

        FreeSpaceGB  = $null 

        DriveLetter  = "Unknown" 

        ErrorMessage = $_.Exception.Message 

        A1_Key       = "$($env:COMPUTERNAME)_DiskCollectionError" 

    } 

 

    [void]$result.Add($errorOutput) 

} 


# ----- Output (required by Action1) ----- 

$result




It returns one object for each fixed disk detected on the endpoint. It uses the drive letter as A1_Key and keeps it as the last property field.

Example 1: Alert on Created

A Created alert is triggered when an object appears in the current snapshot but was not present in the previous snapshot.

Suppose a new drive with the drive letter R: is added to the endpoint. The next snapshot contains a new disk drive object whose A1_key value is R:.

Because no object with this key existed in the previous snapshot, Action1 triggers a Created alert.

Example 2: Alert on Modified

A Modified alert is triggered when one or more field values of an existing object change between consecutive snapshots.

Suppose the capacity or free space of an existing drive changes while its drive letter remains the same. The A1_key continues to identify the same disk drive object, but one of its other field values has changed.

Action1 matches the object across the two snapshots by its A1_key and triggers a Modified alert.

Example 3: Alert on Deleted

A Deleted alert is triggered when an object that existed in the previous snapshot is no longer present in the current snapshot.

Suppose a drive is removed from the endpoint. The disk drive object with the corresponding A1_key is present in the previous snapshot but missing from the next snapshot.

Action1 determines that the object has been removed and triggers a Deleted alert.

NOTE: A drive letter is suitable for illustrating how A1_key works, but it may not always be a stable identifier because drive letters can be reassigned. For its built-in Disk Drive data source, Action1 uses PNP device ID as a more persistent identifier.

Recommendations for A1_Key assignment

If you plan to trigger the alerts based on your custom data source, you should plan for your A1_Key field thoroughly.

  • Select a field that uniquely and consistently identifies each object, verify that its value does not change over time, and make sure it is the last data field in the list returned by your script.

  • Avoid changing the field order after the data source has been created. If you need to modify the field structure, delete the existing data source and create a new one.

Tips for Creating Custom Data Sources

To create your custom PowerShell scripts as data sources, you can use the Action1_DataSource_template.ps1 data source template provided below.

Always review and test your custom scripts before using them in a production environment. Verify that the script:

  • Collects the required data.
  • Returns fields in the expected order.
  • Places the A1_key field last.
  • Returns a unique and stable A1_key value for every object,
  • Handles errors and missing data correctly.

You can use an AI assistant, such as ChatGPT, Gemini, or Claude. Some AI assistants (e.g., Gemini)  may already be familiar with the Action1 data source format. You can start with a prompt such as:

Create an Action1 data source PowerShell script that reports BitLocker recovery keys. 
Create an Action1 data source PowerShell script that reports TPM status as true or false. 
Create an Action1 data source PowerShell script that reports <describe the required information>.

If the AI assistant is not familiar with the required Action1 data source structure, provide the Action1 data source script template:

  1. Use the Action1_DataSource_template.ps1 data source template (see below).
  2. Attach the template to the AI assistant or paste its contents into the conversation.
  3. Enter a prompt that clearly describes the required data. For example:
Using Action1_DataSource_template.ps1 as the template, create an Action1 data source PowerShell script that collects BitLocker recovery keys.
  1. Review the generated script and verify that it follows the template.
  2. Test the script on a limited group of endpoints before broader deployment.

Important! Do not provide confidential information, credentials, recovery keys, or endpoint data to a public AI service. The AI assistant needs only the script template and a description of the required output, not actual data collected from your environment.

Action1_DataSource_template.ps1

=====script title= Action1 Custom Data Source =====
# Description: What this data source collects and why


# ----- Result Object -----
# Rules:
#   - A1_Key MUST be the last property
#   - A1_Key value must uniquely identify the row (typically $env:COMPUTERNAME)
#   - Add/remove custom fields between ComputerName and ErrorMessage
#   - Initialize all fields to a safe default ($null, "", "Unknown", etc.)


$result = [PSCustomObject]@{
    ComputerName = $env:COMPUTERNAME


    # --- Custom fields: add your data columns here ---
    # Field1       = "Unknown"
    # Field2       = $null
    # Field3       = ""


    # --- Always keep these two last (before A1_Key) ---
    ErrorMessage = ""
    A1_Key       = $env:COMPUTERNAME   # <-- MUST be last
}




# ----- Primary Data Collection -----
try {
    # TODO: Replace with your primary query/cmdlet
    # Example: $data = Get-SomeWmiClass -ErrorAction Stop


    # Assign results to $result properties
    # $result.Field1 = $data.Property1
    # $result.Field2 = $data.Property2


} catch {
    # Set a safe fallback for any fields that depend on this block
    # $result.Field1 = "Error"


    $result.ErrorMessage = $_.Exception.Message
}




# ----- Secondary Data Collection (optional, repeat as needed) -----
# Remove this block if you only need one query.
try {
    # TODO: Replace with your secondary query/cmdlet
    # Example: $extra = Get-AnotherCmdlet -ErrorAction Stop


    # $result.Field3 = $extra.SomeProperty


} catch {
    # Append to ErrorMessage so the first error isn't lost
    if ($result.ErrorMessage) {
        $result.ErrorMessage += "; " + $_.Exception.Message
    } else {
        $result.ErrorMessage = $_.Exception.Message
    }
}




# ----- Output (required by Action1) -----
$result