Exporting security event logs¶
Enclave records security events - such as authentication attempts, system enrolments, policy changes, and administrative actions - in the organisation's security log. This script exports the full security log for a given organisation to a CSV file, making it available for offline analysis, compliance reporting, or ingestion into a SIEM.
If you need to review audit trails, investigate incidents, or produce compliance evidence for a customer, this script gives you the complete log in a portable format.
Prerequisites¶
- An Enclave API key
- Your Organisation ID
- PowerShell 5.1 or later
How it works¶
The script pages through the GET /org/{orgId}/logs endpoint, retrieving up to 200 log entries per request. Because the security log can be large, the script includes a short delay between API calls to stay within rate limits.
Each log entry includes a timestamp, severity level, source IP address, username, and event message. The script writes all entries to a CSV file. If the output file already exists, you'll be prompted before it's overwritten.
The script¶
Save the following script as a .ps1 file.
-orgId, the organisation to run queries against.-apiKey, the Enclave API key. Set theENCLAVE_API_KEYenvironment variable as an alternative.-outfile, the path to the output CSV file. Defaults tooutput.csvin the current directory.
Param(
[Parameter(Mandatory=$false)]
[string]$orgId,
[Parameter(Mandatory=$false)]
[string]$apiKey,
[Parameter(Mandatory=$false)]
[string]$outfile
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
if (-not $apiKey) {
$apiKey = if ($env:ENCLAVE_API_KEY) {
$env:ENCLAVE_API_KEY
} else {
Read-Host "Please enter your Enclave Personal Access Token"
}
}
if (-not $apiKey) {
Write-Error "No API key provided; either specify the '-apiKey' argument, or set the ENCLAVE_API_KEY environment variable."
return;
}
if (-not $orgId) {
$orgId = Read-Host "Please enter your Enclave Organisation Id"
}
if (-not $outfile) {
$outfile = "output.csv"
}
$headers = @{Authorization = "Bearer $apiKey"}
$contentType = "application/json"
# ------------
function Invoke-EnclaveApi {
param (
[Parameter(Mandatory = $true)]
[string]$Uri,
[Parameter(Mandatory = $true)]
[string]$Method,
[Parameter(Mandatory = $false)]
[object]$Body
)
try {
if ($null -ne $Body) {
return Invoke-RestMethod -ContentType $contentType -Method $Method -Uri $Uri -Headers $headers -Body ($Body | ConvertTo-Json -Depth 10)
} else {
return Invoke-RestMethod -ContentType $contentType -Method $Method -Uri $Uri -Headers $headers
}
} catch {
throw "Request to $Uri failed with error: $($_.Exception.Message)"
}
}
# ------------
function Get-HumanReadableTimeDifference {
param ([datetime]$pastTime)
$timeDifference = [datetime]::Now - $pastTime
if ($timeDifference.TotalDays -ge 2) {
return "{0} days ago" -f [math]::Floor($timeDifference.TotalDays)
} elseif ($timeDifference.TotalHours -ge 2) {
return "{0} hours ago" -f [math]::Floor($timeDifference.TotalHours)
} elseif ($timeDifference.TotalMinutes -ge 2) {
return "{0} minutes ago" -f [math]::Floor($timeDifference.TotalMinutes)
} else {
return "{0} seconds ago" -f [math]::Floor($timeDifference.TotalSeconds)
}
}
function Format-DateTime {
param (
[datetime]$dateTime
)
return Get-Date $dateTime -Format "dd MMMM yyyy"
}
function Get-AllEnclaveLogs {
param (
[Parameter(Mandatory = $true)]
[string]$InitialUri,
[Parameter(Mandatory = $false)]
[hashtable]$Headers
)
$allItems = @()
$currentUri = $InitialUri
$i = 0
while ($null -ne $currentUri) {
$i++
$response = Invoke-EnclaveApi -Method Get -Uri $currentUri
Write-Host "Requesting page $i/$($response.metadata.lastPage+1)"
# Cloudflare rate limit is 1200 requests over 300 seconds cumulativley. Lowest this value can safely be is 250ms
Start-Sleep -Milliseconds 300
if ($response -and $response.items) {
$allItems += $response.items
}
if ($response.links -and $response.links.next) {
$currentUri = $response.links.next
} else {
$currentUri = $null
}
}
return $allItems
}
if (Test-Path -Path $OutFile) {
Write-Host "Warning: The file '$OutFile' already exists and will be overwritten."
$confirmation = Read-Host "Do you want to proceed? (y/n)"
if ($confirmation -ne 'y') {
Write-Host "Operation cancelled by the user."
return
}
}
# Request the maximum number of logs available from the API per query
$allLogs = Get-AllEnclaveLogs -InitialUri "https://api.enclave.io/org/$orgId/logs?page=0&per_page=200" -Headers $headers
$allLogs | Select-Object timeStamp, level, ipAddress, userName, message | Export-Csv -Path $outfile -NoTypeInformation
Write-Output "$($allLogs.Count) log entries successfully written to $outfile`n"
Usage¶
Run the script interactively (you'll be prompted for credentials) and export to the default output.csv:
.\security-logs-report.ps1
Pass parameters directly and specify a custom output file:
.\security-logs-report.ps1 -orgId "b87f2e1a3d9f" -apiKey $env:ENCLAVE_API_KEY -outfile "trantor-security-logs.csv"
Scheduling¶
Security log exports are typically run ad-hoc for audits or incident investigation. If you need a recurring export for compliance purposes, you can schedule the script using Windows Task Scheduler, your RMM platform, or Azure Functions. Consider using a timestamped filename (e.g. logs-2026-07-31.csv) to avoid overwriting previous exports.