Understanding Pagination
PSFalcon handles pagination automatically when using the-All parameter. For manual control, use -Limit and -Offset.
# Automatic pagination - retrieves all results
$AllHosts = Get-FalconHost -Detailed -All
# Manual pagination - retrieve in batches of 1000
$Offset = 0
$Limit = 1000
$AllResults = @()
do {
$Batch = Get-FalconHost -Limit $Limit -Offset $Offset -Detailed
if ($Batch) {
$AllResults += $Batch
$Offset += $Limit
Write-Host "Retrieved $($AllResults.Count) hosts..."
}
} while ($Batch.Count -eq $Limit)
The maximum limit varies by API. Most endpoints support up to 5000 results per request, but some are limited to 1000 or 2000.
Bulk Host Operations
Process Thousands of Hosts by Filter
Hide all stale hosts not seen in 30+ days.#Requires -Version 5.1
using module @{ModuleName='PSFalcon';ModuleVersion='2.2'}
param(
[Parameter(Mandatory)]
[ValidateRange(1,44)]
[int]$Days
)
# Create tracking file
$OutputFile = Join-Path (Get-Location).Path "hidden_hosts_$(Get-Date -Format FileDateTime).csv"
# Build filter for last_seen date
$Filter = "last_seen:<'now-$($Days)d'"
# Check total count first
$Total = Get-FalconHost -Filter $Filter -Total
Write-Host "Found $Total hosts not seen in $Days days"
if ($Total -eq 0) {
Write-Host "No hosts to hide."
return
}
# Process in batches
$ProcessedCount = 0
$Offset = 0
$Limit = 1000
do {
# Retrieve batch of hosts
$Hosts = Get-FalconHost -Filter $Filter -Limit $Limit -Offset $Offset -Detailed
if ($Hosts) {
# Hide hosts
$Hosts.device_id | Invoke-FalconHostAction -Name hide_host
# Export to CSV for tracking
$Hosts | Select-Object device_id, hostname, last_seen, platform_name |
Export-Csv -Path $OutputFile -NoTypeInformation -Append
$ProcessedCount += $Hosts.Count
$Offset += $Limit
Write-Host "Processed $ProcessedCount of $Total hosts..."
# Rate limiting
Start-Sleep -Seconds 2
}
} while ($Hosts.Count -eq $Limit)
Write-Host "Completed: Hidden $ProcessedCount hosts"
if (Test-Path $OutputFile) {
Get-ChildItem $OutputFile | Select-Object FullName, Length, LastWriteTime
}
Batch Containment from Multiple Sources
Contain hosts from multiple CSV files and data sources.#Requires -Version 5.1
using module @{ModuleName='PSFalcon';ModuleVersion='2.2'}
param(
[Parameter(Mandatory)]
[string[]]$CsvFiles,
[Parameter()]
[string[]]$AdditionalHostnames
)
# Output file
$OutputFile = Join-Path (Get-Location).Path "bulk_containment_$(Get-Date -Format FileDateTime).csv"
# Collect all hostnames from CSV files
[System.Collections.Generic.List[string]]$AllHostnames = @()
foreach ($File in $CsvFiles) {
if (Test-Path $File) {
$Import = Import-Csv -Path $File
if ($Import.hostname) {
@($Import.hostname).foreach{ $AllHostnames.Add($_) }
Write-Host "Loaded $($Import.Count) hostnames from $File"
}
} else {
Write-Warning "File not found: $File"
}
}
# Add additional hostnames
if ($AdditionalHostnames) {
@($AdditionalHostnames).foreach{ $AllHostnames.Add($_) }
}
# Remove duplicates
$UniqueHostnames = $AllHostnames | Select-Object -Unique
Write-Host "Total unique hostnames: $($UniqueHostnames.Count)"
# Process in batches of 100
$BatchSize = 100
$Results = @()
for ($i = 0; $i -lt $UniqueHostnames.Count; $i += $BatchSize) {
$Batch = $UniqueHostnames[$i..([Math]::Min($i + $BatchSize - 1, $UniqueHostnames.Count - 1))]
Write-Host "Processing batch $([Math]::Floor($i/$BatchSize) + 1)..."
foreach ($Hostname in $Batch) {
# Find host (most recently seen)
$Host = Get-FalconHost -Filter "hostname:['$Hostname']" -Sort last_seen.desc -Limit 1 -Detailed
if ($Host) {
# Attempt containment
$Action = $Host.device_id | Invoke-FalconHostAction -Name contain
$Results += [PSCustomObject]@{
hostname = $Hostname
device_id = $Host.device_id
platform = $Host.platform_name
last_seen = $Host.last_seen
contained = if ($Action.id -eq $Host.device_id) { 'Success' } else { 'Failed' }
}
} else {
$Results += [PSCustomObject]@{
hostname = $Hostname
device_id = ''
platform = ''
last_seen = ''
contained = 'Not Found'
}
}
}
# Rate limiting between batches
Start-Sleep -Seconds 5
}
# Export results
$Results | Export-Csv -Path $OutputFile -NoTypeInformation
# Summary
$Contained = ($Results | Where-Object { $_.contained -eq 'Success' }).Count
$Failed = ($Results | Where-Object { $_.contained -eq 'Failed' }).Count
$NotFound = ($Results | Where-Object { $_.contained -eq 'Not Found' }).Count
Write-Host "`nSummary:"
Write-Host " Total: $($Results.Count)"
Write-Host " Contained: $Contained"
Write-Host " Failed: $Failed"
Write-Host " Not Found: $NotFound"
Get-ChildItem $OutputFile | Select-Object FullName, Length, LastWriteTime
Bulk Detection Operations
Process Thousands of Detections
Assign and update status for all new critical detections.#Requires -Version 5.1
using module @{ModuleName='PSFalcon';ModuleVersion='2.2'}
param(
[Parameter(Mandatory)]
[string]$AssignToUsername
)
# Get user identifier
$UserUuid = Get-FalconUser -Username $AssignToUsername
if (!$UserUuid) {
throw "No user found for '$AssignToUsername'"
}
# Build filter
$Filter = "status:'new'+max_severity_displayname:'Critical'"
# Check total count
$Total = Get-FalconDetection -Filter $Filter -Total
Write-Host "Found $Total new critical detections"
if ($Total -eq 0) {
Write-Host "No detections to process."
return
}
# Process in batches of 1000 (API limit)
$ProcessedCount = 0
do {
if ($ProcessedCount -gt 0) {
# Rate limiting between batches
Start-Sleep -Seconds 5
}
# Get next batch of unassigned detections
$Param = @{
Filter = "status:'new'+max_severity_displayname:'Critical'+assigned_to_uuid:!'$UserUuid'"
Limit = 1000
OutVariable = 'DetectionIds'
}
$Updated = Get-FalconDetection @Param | Edit-FalconDetection -AssignedToUuid $UserUuid -Status in_progress
if ($Updated.writes.resources_affected) {
$ProcessedCount += $Updated.writes.resources_affected
Write-Host "Processed $ProcessedCount detections..."
}
} while ($DetectionIds)
Write-Host "Completed: Assigned $ProcessedCount detections to $AssignToUsername"
Hide Detections in Bulk with Tracking
Hide thousands of detections involving a specific file and track them.#Requires -Version 5.1
using module @{ModuleName='PSFalcon';ModuleVersion='2.2'}
param(
[Parameter(Mandatory)]
[string]$Filename
)
# Output file to track hidden detections
$OutputFile = Join-Path (Get-Location).Path "hidden_detections_$(Get-Date -Format FileDateTime).txt"
# Verify detections exist
$Total = Get-FalconDetection -Filter "behaviors.filename:'$Filename'" -Total
if ($Total -eq 0) {
throw "No detections found for '$Filename'."
}
Write-Host "Found $Total detections to hide"
$HiddenCount = 0
do {
if ($HiddenCount -gt 0) {
# Rate limiting
Start-Sleep -Seconds 5
}
# Hide in batches of 1000
$Edit = Get-FalconDetection -Filter "behaviors.filename:'$Filename'" -Limit 1000 -OutVariable Id |
Edit-FalconDetection -ShowInUi $false
if ($Edit.writes.resources_affected) {
$HiddenCount += $Edit.writes.resources_affected
Write-Host "Hidden $HiddenCount of $Total detection(s)..."
# Output detection IDs to file for audit trail
$Id | Out-File -FilePath $OutputFile -Append
}
} while ($Id)
Write-Host "Completed: Hidden $HiddenCount detections"
if (Test-Path $OutputFile) {
Get-ChildItem $OutputFile | Select-Object FullName, Length, LastWriteTime
}
Bulk IOC Operations
Import Custom IOCs from CSV
Create thousands of custom indicators from a CSV file.#Requires -Version 5.1
using module @{ModuleName='PSFalcon';ModuleVersion='2.2'}
param(
[Parameter(Mandatory)]
[ValidatePattern('\.csv$')]
[ValidateScript({
if (Test-Path -Path $_ -PathType Leaf) {
$true
} else {
throw "Cannot find path '$_'"
}
})]
[string]$Path,
[Parameter(Mandatory)]
[ValidateSet('detect', 'prevent', 'no_action', IgnoreCase=$false)]
[string]$Action,
[Parameter()]
[string[]]$Tags
)
# Import CSV (expected columns: type, value, description)
$Import = Import-Csv -Path $Path
if (!$Import.type -or !$Import.value) {
throw "CSV must contain 'type' and 'value' columns"
}
Write-Host "Loaded $($Import.Count) indicators from CSV"
# Process in batches of 1000 (API limit for bulk creation)
$BatchSize = 1000
$Created = 0
$Failed = 0
for ($i = 0; $i -lt $Import.Count; $i += $BatchSize) {
$Batch = $Import[$i..([Math]::Min($i + $BatchSize - 1, $Import.Count - 1))]
Write-Host "Processing batch $([Math]::Floor($i/$BatchSize) + 1)..."
# Build indicator objects
$Indicators = @($Batch).foreach{
$Ioc = @{
type = $_.type
value = $_.value
action = $Action
platforms = @('windows', 'mac', 'linux')
severity = 'medium'
applied_globally = $true
}
if ($_.description) { $Ioc['description'] = $_.description }
if ($Tags) { $Ioc['tags'] = $Tags }
[PSCustomObject]$Ioc
}
# Bulk create IOCs
try {
$Result = $Indicators | New-FalconIoc
if ($Result) {
$Created += @($Result).Count
Write-Host " Created $(@($Result).Count) IOCs"
}
} catch {
Write-Error "Batch failed: $_"
$Failed += $Batch.Count
}
# Rate limiting
Start-Sleep -Seconds 3
}
Write-Host "`nSummary:"
Write-Host " Total: $($Import.Count)"
Write-Host " Created: $Created"
Write-Host " Failed: $Failed"
Bulk Update IOC Tags
Update tags on thousands of existing IOCs.#Requires -Version 5.1
using module @{ModuleName='PSFalcon';ModuleVersion='2.2'}
param(
[Parameter(Mandatory)]
[string]$Filter,
[Parameter(Mandatory)]
[string[]]$NewTags
)
# Get all matching IOCs
$Iocs = Get-FalconIoc -Filter $Filter -Detailed -All
if (!$Iocs) {
throw "No IOCs found matching filter: $Filter"
}
Write-Host "Found $($Iocs.Count) IOCs to update"
# Update in batches of 1000
$BatchSize = 1000
$Updated = 0
for ($i = 0; $i -lt $Iocs.Count; $i += $BatchSize) {
$Batch = $Iocs[$i..([Math]::Min($i + $BatchSize - 1, $Iocs.Count - 1))]
Write-Host "Processing batch $([Math]::Floor($i/$BatchSize) + 1)..."
# Build update objects
$Updates = @($Batch).foreach{
[PSCustomObject]@{
id = $_.id
tags = $NewTags
}
}
# Bulk update
$Result = $Updates | Edit-FalconIoc
if ($Result) {
$Updated += @($Result).Count
Write-Host " Updated $(@($Result).Count) IOCs"
}
# Rate limiting
Start-Sleep -Seconds 3
}
Write-Host "Completed: Updated $Updated IOCs"
Bulk Deletion Operations
Delete Expired IOCs in Bulk
Remove thousands of expired or outdated indicators.#Requires -Version 5.1
using module @{ModuleName='PSFalcon';ModuleVersion='2.2'}
param(
[Parameter()]
[string]$Filter = "action:'no_action'",
[Parameter()]
[switch]$WhatIf
)
# Get IOCs to delete
$Iocs = Get-FalconIoc -Filter $Filter -All
if (!$Iocs) {
Write-Host "No IOCs found matching filter: $Filter"
return
}
Write-Host "Found $($Iocs.Count) IOCs to delete"
if ($WhatIf) {
Write-Host "WhatIf: Would delete $($Iocs.Count) IOCs"
return
}
# Confirm deletion
$Confirm = Read-Host "Delete $($Iocs.Count) IOCs? (yes/no)"
if ($Confirm -ne 'yes') {
Write-Host "Deletion cancelled"
return
}
# Delete in batches of 1000
$BatchSize = 1000
$Deleted = 0
for ($i = 0; $i -lt $Iocs.Count; $i += $BatchSize) {
$Batch = $Iocs[$i..([Math]::Min($i + $BatchSize - 1, $Iocs.Count - 1))]
Write-Host "Deleting batch $([Math]::Floor($i/$BatchSize) + 1)..."
# Bulk delete by ID
$Batch.id | Remove-FalconIoc
$Deleted += $Batch.Count
Write-Host " Deleted $Deleted of $($Iocs.Count) IOCs"
# Rate limiting
Start-Sleep -Seconds 3
}
Write-Host "Completed: Deleted $Deleted IOCs"
Always test bulk operations with filters and limits before running on production data. Use
-WhatIf when available, or limit initial runs to small batches.Performance Best Practices
Rate Limiting Strategy
# Implement progressive backoff on rate limit errors
$MaxRetries = 3
$RetryCount = 0
$BackoffSeconds = 5
do {
try {
# Perform bulk operation
$Result = Get-FalconHost -Filter $Filter -Limit 1000 -Offset $Offset -Detailed
$RetryCount = 0 # Reset on success
} catch {
if ($_.Exception.Message -match '429|rate limit') {
$RetryCount++
if ($RetryCount -le $MaxRetries) {
$WaitTime = $BackoffSeconds * $RetryCount
Write-Warning "Rate limit hit. Waiting $WaitTime seconds... (Retry $RetryCount/$MaxRetries)"
Start-Sleep -Seconds $WaitTime
} else {
throw "Max retries exceeded: $_"
}
} else {
throw $_
}
}
} while ($Result.Count -eq 1000 -or $RetryCount -gt 0)
Memory Management for Large Datasets
# Use streaming to file instead of loading all into memory
$OutputFile = 'large_export.csv'
$Offset = 0
$Limit = 5000
$FirstBatch = $true
do {
$Hosts = Get-FalconHost -Limit $Limit -Offset $Offset -Detailed
if ($Hosts) {
# Append to CSV (write headers only on first batch)
if ($FirstBatch) {
$Hosts | Export-Csv -Path $OutputFile -NoTypeInformation
$FirstBatch = $false
} else {
$Hosts | Export-Csv -Path $OutputFile -NoTypeInformation -Append
}
$Offset += $Limit
Write-Host "Exported $Offset hosts..."
# Clear variable to free memory
$Hosts = $null
[System.GC]::Collect()
}
} while ($Hosts -ne $null)
Next Steps
Custom IOC Workflows
Automate indicator management
MSSP Operations
Manage multiple customer CIDs