Dear all,
Can someone help me with the following. It’s been a while since I have done this, but basically a couple years ago it was possible to export Campaign Status Reports using an API, post-refresh…etc etc but long story short, I am unable to do it now. I have been using v2025 as much as possible, but hit a road block and exporting the Campaign Status reports to csv seems to be the last step I am struggling with.
Can some advise accordingly and let me know where I am going wrong, cause I am sure its something quite silly.
# ================================
# CONFIGURATION
# ================================
$AppProps = ConvertFrom-StringData (Get-Content "$PSScriptRoot/config/appdata.properties" -Raw)
$TenantWeb = $AppProps.'tenantweb'
$TenantAPI = $AppProps.'tenantapi'
$ClientID = $AppProps.'clientId'
$ClientSecret = $AppProps.'clientSecret'
# ================================
# FUNCTIONS
# ================================
function Get-APIData {
param(
[string]$Url,
[hashtable]$Headers
)
try {
return Invoke-RestMethod -Uri $Url -Headers $Headers -Method Get
} catch {
Write-Warning "Failed to get data from $Url : $_"
return $null
}
}
# ================================
# AUTHENTICATION
# ================================
Write-Host "Authenticating to SailPoint ISC..."
$AuthBody = @{
grant_type = "client_credentials"
client_id = $ClientID
client_secret = $ClientSecret
}
$TokenResponse = Invoke-RestMethod -Uri "https://$TenantAPI/oauth/token" -Method POST -Body $AuthBody -ContentType "application/x-www-form-urlencoded"
$Headers = @{
"Authorization" = "Bearer $($TokenResponse.access_token)"
"Content-Type" = "application/json"
}
$AuthTimestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$AuthStatus = if ($TokenResponse.access_token) { "Authentication succeeded" } else { "Authentication failed" }
Write-Host "$AuthStatus at $AuthTimestamp"
# ================================
# STEP 1: LIST ACTIVE CAMPAIGNS
# ================================
$Limit = 250
$Offset = 0
$AllCampaigns = @()
$MoreData = $true
while ($MoreData) {
$CampaignsURL = "https://$TenantAPI/v2025/campaigns?detail=FULL&limit=$Limit&offset=$Offset&count=true"
$Response = Get-APIData -Url $CampaignsURL -Headers $Headers
if ($Response) {
$ActiveCampaigns = $Response | Where-Object { $_.status -eq "ACTIVE" } | ForEach-Object {
[PSCustomObject]@{
id = $_.id
name = $_.name
ReportTriggered = $false
ReportId = $null
Processed = $false
}
}
$AllCampaigns += $ActiveCampaigns
if ($Response.Count -lt $Limit) { $MoreData = $false } else { $Offset += $Limit }
} else {
$MoreData = $false
}
}
Write-Host "Total active campaigns retrieved: $($AllCampaigns.Count)"
# ================================
# STEP 2: TRIGGER REPORTS
# ================================
$ReportType = "CAMPAIGN_STATUS_REPORT"
$PollIntervalSeconds = 15
foreach ($campaign in $AllCampaigns) {
$CampaignID = $campaign.id
$CampaignName = $campaign.name -replace '[\\\/:*?"<>|]', '' # sanitize filename
$RunReportURL = "https://$TenantAPI/v2025/campaigns/$CampaignID/run-report/$ReportType"
try {
$RunResponse = Invoke-RestMethod -Uri $RunReportURL -Headers $Headers -Method Post
$campaign.ReportTriggered = $true
$campaign.ReportId = $RunResponse.id
Write-Host "Triggered report for $CampaignName, Run ID: $($campaign.id)"
} catch {
Write-Warning "Failed to trigger report for $CampaignName"
$campaign.ReportTriggered = $false
}
}
# ================================
# STEP 3: POLL REPORTS AND EXPORT TO CSV USING REPORT ID
# ================================
$Remaining = $AllCampaigns | Where-Object { $_.ReportTriggered -eq $true } | Measure-Object | Select-Object -ExpandProperty Count
while ($Remaining -gt 0) {
foreach ($campaign in $AllCampaigns) {
# Skip if already processed or report hasn't been triggered
if ($campaign.Processed -eq $true -or $campaign.ReportTriggered -ne $true) { continue }
$CampaignName = $campaign.name -replace '[\\\/:*?"<>|]', ''
$ReportId = $campaign.id # <-- Use the run-report ID saved during Step 2
if (-not $ReportId) {
Write-Warning "No report ID available yet for $CampaignName. Skipping for now."
continue
}
$StatusUrl = "https://$TenantAPI/v2025/report-results/$ReportId"
try {
$StatusResponse = Invoke-RestMethod -Uri $StatusUrl -Headers $Headers -Method GET
Write-Host "DEBUG: Report $ReportId for $CampaignName status = $($StatusResponse.status) at $(Get-Date)"
} catch {
Write-Warning "Error checking status for $CampaignName - $_"
continue
}
if ($StatusResponse.status -eq "SUCCESS") {
$CsvURL = "https://$TenantAPI/cc/api/report/get/$ReportId?format=csv"
$Timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$CsvFile = "$PSScriptRoot\$CampaignName`_$Timestamp.csv"
# Optional short delay to ensure the report is fully available
Start-Sleep -Seconds 5
try {
$CsvResponse = Invoke-RestMethod -Uri $CsvURL -Headers @{ "Authorization" = $Headers.Authorization } -Method GET -ContentType "text/csv"
Set-Content -Path $CsvFile -Value $CsvResponse -Encoding UTF8
Write-Host "Exported report for $CampaignName to $CsvFile"
$campaign.Processed = $true
} catch {
Write-Warning "Failed to export report for $CampaignName $($_.Exception.Message)"
}
}
}
Start-Sleep -Seconds $PollIntervalSeconds
$Remaining = $AllCampaigns | Where-Object { $_.ReportTriggered -eq $true -and $_.Processed -ne $true } | Measure-Object | Select-Object -ExpandProperty Count
}
Write-Host "All active campaigns processed."