Asset export and Veeam adapters

Manual · API and availability · Set up data sources · Ingest reference

These push adapters belong to the extension after v1.25.0. On an instance with this extension, download the adapter package under Settings > API access (/settings/api-keys/adapters.zip). It contains push.mjs, asset-export.mjs, Export-VeeamSessions.ps1 and German and English guides. Configure the source, keys and bindings before sending data.

Which tool does what?

Tool Purpose and limits
Windows Inventory Collector Reads the individual computer on which it runs, collects additional details through a local form and writes inventory JSON. No network discovery or recurring push service.
asset-export.mjs Converts a normalised asset export into an ingest batch. Does not query Checkmk, Jamf or other vendors itself.
Export-VeeamSessions.ps1 Reads completed VM backup sessions using Get-VBRBackupSession and creates a delta batch.
push.mjs Sends a persisted batch over HTTPS with an adapter key and writes a receipt.

Windows Inventory Collector

Download the collector under Assets > Inventory computer or Settings > API access and run it on the target computer. It reads OS, hardware, networking, installed software, services, open ports and security status. Without elevated permissions, details such as BitLocker and Secure Boot remain incomplete.

The script does not transmit data to ISMS Lite. Its JSON file supports guided asset creation through an API/MCP client. Its structure differs from the normalised multi-device export accepted by asset-export.mjs. For recurring synchronisation from an existing inventory system, use the following workflow.

Prepare a normalised asset export

The adapter machine needs Node.js 22 or later; the two .mjs examples require no additional npm packages. A customer-side query reads the agreed coverage from the source and writes, for example, export.json:

{
  "observedAt": "2026-09-19T08:00:00Z",
  "coverage": "site-a-managed-assets",
  "complete": true,
  "assets": [
    {
      "externalId": "device-123",
      "name": "srv-01",
      "location": "Site A",
      "ip_address": "10.0.0.10",
      "os_platform": "Linux"
    }
  ]
}

Replace observedAt with the actual collection time. Keep externalId stable across renaming and IP changes. coverage must exactly match the configured source. complete: true creates a full snapshot; use it only after successfully reading every page. false creates a delta batch.

node ./asset-export.mjs export.json pending.json 1

The final number is the current source revision. The generator creates a batch ID and refuses to overwrite an existing output file. It transfers technical asset fields, not domain decisions about ownership, criticality or RPO. Its asset deliveries contain no compliance observations; a custom adapter must add them according to the ingest contract.

Send a batch

$env:ISMS_URL = 'https://isms.example.org'
$env:ISMS_SOURCE_ID = 'UUID-OF-SOURCE'
# ISMS_API_KEY comes from the task's protected environment.
node ./push.mjs pending.json

ISMS_URL must be the instance's HTTPS origin without an API path, user information or query. The sender does not follow redirects. Success creates pending.json.receipt.json with the server response. Only then archive the input file and generate a new batch.

After a lost response, resend the same file. Do not regenerate the export and batch ID every time: only the same ID and content can be retried idempotently. The sender makes at most five attempts on transport errors, 429 and server errors, with delays and a 30-second timeout per attempt. Other errors require correction. The error reference explains typical conflicts.

JSON files and receipts contain operational data. Restrict access to the task user, keep keys out of files, prevent overlapping tasks and remove completed files according to local retention policy. This does not remove remote evidence history.

Export Veeam sessions

The script requires PowerShell 5.1 or later and a PowerShell environment appropriate to the Veeam installation.

The example uses Get-VBRBackupSession in an already authenticated Veeam PowerShell session inside the customer network. The Veeam cmdlet reference describes VM backup sessions. Check product variant, version, cmdlet availability, permissions and returned fields before use. The example was checked with fixtures; compatibility with every Veeam installation is not guaranteed. Other workloads and Veeam for Microsoft 365 need their own mappings.

  1. Configure a source with objectKind: "backup_job" and createAssets: null.
  2. Create ISMS backup jobs and bind their IDs to stable Veeam job IDs, using managedFields: [].
  3. Set rules.backupFailures for failure evaluation, for example 2 for two consecutive failures.
  4. Export with read-only Veeam access. Replace job ID, coverage and revision in the example.
./Export-VeeamSessions.ps1 -JobIds 'UUID-OF-VEEAM-JOB' `
    -Coverage 'veeam-vm-jobs' -SourceRevision 1 -OutputPath pending.json
node ./push.mjs pending.json

Set the environment variables as above before sending. Export and delivery may run on separate computers; the Veeam machine then does not need Node.js. Transfer the file securely to the delivery machine.

Veeam data Ingest meaning
Only sessions with State: Stopped Completed session is included
Result: Success backup_run with passed
Result: Failed backup_run with failed
Warning or another result unknown
Session ID Stable event ID
Session end Observation time of the result

By default the example reads an overlapping seven-day window (LookbackHours: 168); a result remains valid until 48 hours after session end (ValidHours: 48). Choose these values for the backup frequency. Do not change the validity of an already delivered event while retaining its event ID.

More than 20 sessions per job produces an error rather than silent truncation. The window must cover late sessions. For larger histories, implement a custom adapter with several delta batches. The Veeam export always sends delta and therefore does not detect disappeared jobs.

Complete PowerShell example

The following code matches Export-VeeamSessions.ps1 from the adapter package. Save it as a UTF-8 file under that name and run it using the command above. An already authenticated Veeam PowerShell session is still required. The export writes only pending.json; push.mjs from the same package then handles HTTPS delivery.

#requires -Version 5.1
[CmdletBinding()]
param(
    [Parameter(Mandatory)][guid[]]$JobIds,
    [Parameter(Mandatory)][string]$Coverage,
    [Parameter(Mandatory)][ValidateRange(1, 2147483647)][int]$SourceRevision,
    [Parameter(Mandatory)][string]$OutputPath,
    [ValidateRange(1, 8760)][int]$ValidHours = 48,
    [ValidateRange(1, 8760)][int]$LookbackHours = 168,
    [string]$SessionsJson
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
if (Test-Path -LiteralPath $OutputPath) { throw 'Output exists. Deliver or resolve the pending batch before exporting again.' }
if ($JobIds.Count -gt 500) { throw 'At most 500 configured jobs are supported per source.' }
$observedAt = [DateTime]::UtcNow
if ($SessionsJson) {
    $sessions = @(Get-Content -LiteralPath $SessionsJson -Raw | ConvertFrom-Json)
} else {
    # Run inside an authenticated Veeam PowerShell session in the customer network.
    $sessions = @(Get-VBRBackupSession -ErrorAction Stop)
}
$objects = @()
foreach ($jobId in ($JobIds | Select-Object -Unique)) {
    $runs = @($sessions | Where-Object { [guid]$_.JobId -eq $jobId -and [string]$_.State -eq 'Stopped' } | Sort-Object EndTimeUTC -Descending)
    $observations = @()
    foreach ($run in $runs) {
        # EndTimeUTC is explicitly UTC, including when Veeam returns DateTimeKind.Unspecified.
        $end = [DateTime]::SpecifyKind([DateTime]$run.EndTimeUTC, [DateTimeKind]::Utc)
        if ($end -lt $observedAt.AddHours(-$LookbackHours)) { continue }
        if ($end -gt $observedAt) { throw 'Session timestamp is in the future.' }
        $result = switch ([string]$run.Result) { 'Success' { 'passed' } 'Failed' { 'failed' } default { 'unknown' } }
        $sessionId = ([guid]$run.Id).ToString()
        $observations += @{
            eventId = $sessionId; checkType = 'backup_run'; result = $result
            observedAt = $end.ToString('o'); validUntil = $end.AddHours($ValidHours).ToString('o')
            evidence = @{ summary = "Veeam session=$sessionId; result=$($run.Result)" }
        }
    }
    # Never truncate history silently: split a large export into explicit delta batches.
    if ($observations.Count -gt 20) { throw 'More than 20 sessions per job. Use a shorter overlap window or implement delta paging.' }
    $objects += @{ externalId = $jobId.ToString(); observations = @($observations) }
}
if (($objects | ForEach-Object { $_.observations.Count } | Measure-Object -Sum).Sum -gt 1000) { throw 'More than 1000 observations. Split into delta batches.' }
$payload = @{
    schemaVersion = 1; batchId = [guid]::NewGuid().ToString(); sourceRevision = $SourceRevision
    coverage = $Coverage; mode = 'delta'; observedAt = $observedAt.ToString('o')
    totalObjects = $objects.Count; objects = @($objects)
} | ConvertTo-Json -Depth 8
if ([Text.Encoding]::UTF8.GetByteCount($payload) -gt 1MB) { throw 'Batch exceeds 1 MiB.' }
$stream = [IO.File]::Open([IO.Path]::GetFullPath($OutputPath), [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None)
try {
    $bytes = [Text.UTF8Encoding]::new($false).GetBytes($payload)
    $stream.Write($bytes, 0, $bytes.Length)
} finally { $stream.Dispose() }

SessionsJson is only for offline testing with prepared fixtures. Omit it when using Veeam so the script calls Get-VBRBackupSession.

Map RPO and SureBackup separately

A successful job proves neither a usable restore point nor a successful recovery test.

  • RPO: An asset source needs backup_point with passed, the actually reported usable restorePointAt and a justified validity period. Configure the RPO on the ISMS asset. A job source alone cannot provide this asset assessment.
  • Restore/SureBackup: Deliver an actual test report as restore_test with result and validity. Missing or expired evidence remains unknown.
  • Domain runs and test completion: Use the backup domain API. Ingest observations do not automatically complete these workflows.

These additional vendor mappings must be developed against the installed version and actual data, then checked in a pilot. They are not part of the session example.

Other vendors and directories

Source Possible adapter content
Checkmk, Jamf Stable device IDs and selected asset fields; explicitly assessed compliance as device_compliance
Portnox Mapped device status or substantiated compliance observations
Unimus Configuration backup result and reference as config_backup, excluding configuration files and secrets
Microsoft Entra ID / Microsoft 365 Selected asset/compliance evidence from a customer-side query, when mapped to a supported target record

These are possible mappings, not supplied direct connectors. Implement vendor retrieval, permissions, pagination and assessment in the customer-side adapter. Report unknown results as unknown, not success.

Built-in Entra sign-in and AD/Entra group integration are separate features; see Groups and Settings. The push channel does not provision users or groups and does not replace directory configuration.