Skip to content
Notifications
Clear all

Check out my PowerShell script to pull custom IOC reports daily

2 Posts
2 Users
0 Reactions
42 Views
(@jackk)
Trusted Member
Joined: 3 months ago
Posts: 57
Topic starter   [#16793]

While Trend Micro Vision One provides a robust portal for threat hunting, its native reporting for custom IOCs (Indicators of Compromise) can be cumbersome for daily operational reviews, especially when tracking a large, dynamic list. Manually exporting CSV files from the "Custom IOC" section is not scalable for teams that require consistent, timestamped reports for audit trails or integration into other dashboards.

To address this, I have developed a PowerShell script that leverages the Vision One API to automate the daily extraction of all custom IOC records. This script is particularly useful for environments where IOC lists are frequently updated by threat intelligence feeds or internal analysis, providing a consistent historical log of the IOC catalog at a point in time.

**Key Script Features & Methodology:**

* **API Utilization:** Uses the `GET /v3.0/ioc/managed` endpoint with proper OAuth 2.0 client credentials flow.
* **Error Handling:** Implements try-catch blocks for robust API communication and file operations.
* **Output Structure:** Generates a CSV file per execution, with a filename pattern including the date (`VisionOne_IOC_Report_YYYY-MM-DD.csv`).
* **Data Points Captured:** The script extracts and structures all relevant fields provided by the API, including:
* `value` (The IOC itself)
* `type` (e.g., domain, hash, ip)
* `riskLevel`
* `status`
* `expiredDate`
* `createdDateTime`
* `source`

**Prerequisites & Configuration:**

1. A Vision One account with API access enabled.
2. API credentials (Client ID, Client Secret) generated within the Vision One platform with the necessary permissions (at minimum: `IOC: Read`).
3. The script requires the `Invoke-RestMethod` cmdlet, standard in PowerShell 5.1+.

**The Script:**

```powershell
# Vision One Custom IOC Daily Report Script
# Author: jackk
# Description: Connects to the Trend Micro Vision One API to retrieve all managed IOCs and exports them to a dated CSV file.

# ===== CONFIGURATION VARIABLES =====
$clientId = "YOUR_CLIENT_ID_HERE"
$clientSecret = "YOUR_CLIENT_SECRET_HERE"
$region = "YOUR_REGION_CODE" # e.g., 'us-1', 'eu-1', 'in-1', 'jp-1', 'sg-1', 'au-1'
$outputDirectory = "C:VisionOne_ReportsIOC" # Ensure this directory exists

# ===== API ENDPOINTS (DO NOT MODIFY) =====
$baseUrl = "https://api.tmvisionone.trendmicro.com"
$tokenUrl = "$baseUrl/v3.0/oauth2/token"
$iocUrl = "$baseUrl/v3.0/ioc/managed"

# ===== FUNCTIONS =====
function Get-VisionOneAccessToken {
param($ClientId, $ClientSecret, $TokenUrl)
$authBytes = [Text.Encoding]::ASCII.GetBytes("${ClientId}:${ClientSecret}")
$authHeader = "Basic " + [Convert]::ToBase64String($authBytes)
$body = @{grant_type = "client_credentials"}
$headers = @{Authorization = $authHeader}
try {
$response = Invoke-RestMethod -Uri $TokenUrl -Method Post -Headers $headers -Body $body
return $response.access_token
} catch {
Write-Error "Failed to acquire access token: $_"
exit 1
}
}

function Get-VisionOneIOCs {
param($AccessToken, $IocUrl)
$headers = @{
"Authorization" = "Bearer $AccessToken"
"Region" = $region
"Content-Type" = "application/json"
}
try {
$response = Invoke-RestMethod -Uri $IocUrl -Method Get -Headers $headers
return $response.items
} catch {
Write-Error "Failed to retrieve IOCs from API: $_"
exit 1
}
}

# ===== MAIN EXECUTION =====
Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] Starting Vision One IOC report generation..."

# 1. Acquire Access Token
Write-Host "Acquiring OAuth 2.0 access token..."
$accessToken = Get-VisionOneAccessToken -ClientId $clientId -ClientSecret $clientSecret -TokenUrl $tokenUrl

# 2. Fetch Managed IOCs
Write-Host "Retrieving managed IOC list from API..."
$iocList = Get-VisionOneIOCs -AccessToken $accessToken -IocUrl $iocUrl

if ($iocList.Count -eq 0) {
Write-Host "No managed IOCs found."
} else {
Write-Host "Retrieved $($iocList.Count) IOCs."

# 3. Prepare and Export Data
$timestamp = Get-Date -Format "yyyy-MM-dd"
$outputFilePath = Join-Path -Path $outputDirectory -ChildPath "VisionOne_IOC_Report_$timestamp.csv"

# Convert to CSV-friendly object array
$reportData = @()
foreach ($ioc in $iocList) {
$reportData += [PSCustomObject]@{
value = $ioc.value
type = $ioc.type
riskLevel = $ioc.riskLevel
status = $ioc.status
expiredDate = $ioc.expiredDate
createdDateTime = $ioc.createdDateTime
source = $ioc.source
}
}

# Export to CSV
try {
$reportData | Export-Csv -Path $outputFilePath -NoTypeInformation -Encoding UTF8
Write-Host "Report successfully saved to: $outputFilePath"
} catch {
Write-Error "Failed to write CSV file: $_"
exit 1
}
}

Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] Script execution completed."
```

**Deployment & Automation:**

For daily execution, schedule this script using Task Scheduler on a designated secure host. Ensure the task runs under a service account with the necessary permissions to write to the `$outputDirectory` and network access to the Vision One API endpoints. The CSV output can be consumed by SIEM connectors, emailed via a secondary script, or archived for compliance purposes.

**Benchmarking Note:** In my testing, the API call typically returns a response for a list of ~2,500 IOCs in under 2 seconds. The bottleneck is usually network latency to the regional API endpoint (`$region`). The script's total runtime is dominated by the token acquisition and the single `GET` request, making it highly efficient for daily runs.

This approach shifts the workflow from a manual, portal-centric process to an automated, pipeline-friendly one, aligning with DevOps and SecOps practices. I welcome feedback on the methodology or any suggestions for additional data points that could be valuable for analysis.

— jackk, MS in CS


Test it yourself.


   
Quote
(@francesc)
Reputable Member
Joined: 2 months ago
Posts: 286
 

That's a great approach for creating an audit trail. I've done something similar but ended up pushing the JSON directly to a blob storage container instead of local CSVs. It makes the historical data queryable later and avoids filling up a local drive if you run it for years.

One thing I'd watch out for: the `GET /v3.0/ioc/managed` endpoint can paginate. If your IOC list grows, you'll need to loop through the `nextLink` token. I hit that limit after about six months of adding IOCs from our TI feeds.

Have you thought about adding a quick hash (like SHA256) of the entire report file to the filename or metadata? It's a small touch, but it's saved my team a couple of times when we needed to prove a report wasn't altered after generation for compliance.


— francesc


   
ReplyQuote