Get number of sources for each identity

Hello i need to get report on how many sources does the user identity has.
This post is what would work: Search Identities and number of sources - Identity Security Cloud (ISC) / ISC Discussion and Questions - SailPoint Developer Community Forum

But in post solution you are saying that accountCount=sourceCount, that is not true, as under one source i can have multiple accounts.
Example:
My identity is present in 3 sources, but in one of the sources i have 5 accounts, so if i run search with “accountCount” i will have 7 accounts returned. But i need to get returned number of sources, that would be 3.

The BEST would be if i could generate a report with both - number of sources and number of accounts.
Example:
Identty | Number of sources | Number of accounts
Aleksandra H. | 3 | 7

Any ideas? Maybe some API call modification?

Thank you in advance,

Regards,
Aleksandra

Hey @karapuuzina, I wrote a quick PowerShell script that will generate a report according to what you’re looking for. Please feel free to modify as you see fit.

Script Purpose

Based on what you requested, this quick script will generate a report in CSV format with the following headers:

  • Identity Id
  • Identity Name
  • Number of Sources
  • Number of Accounts

Preqrequisites

  1. Generate an Access Token by either grabbing the token from your web browser with an active session with Identity Security Cloud, or other means such as through Postman. You can also generate one via PowerShell. Whatever works for you!
  2. Either enter the token into the $token variable, or provide it in the console when prompted.
  3. Provide your SailPoint Identity Security Cloud API url, such as https://{Org name}.api.identitynow.com
  4. Provide a name of a file and its path in the $exportFilePath variable to which the data should be exported. Make sure it is a text or CSV file. You can also just provide this in the console when prompted.

Script

# Preqrequisites
# 1. Generate an Access Token by either grabbing the token from your web browser with an active session with Identity Security Cloud, or other means such as through Postman. You can also generate one via PowerShell. Whatever works for you!
# 2. Either enter the token into the `$token` variable, or provide it in the console when prompted.
# 3. Provide your SailPoint Identity Security Cloud API url, such as `https://{Org name}.api.identitynow.com`
# 4. Provide a name of a file and its path in the `$exportFilePath` variable to which the data should be exported. **Make sure it is a text or CSV file**. You can also just provide this in the console when prompted.

#-------------------------------------------------- Variables --------------------------------------------------#
$token = "" # Either enter your access (bearer) token here or enter it into the console when prompted.
$uri = "https://{Org name}.api.identitynow.com"
$fileExportPath = "C:\SailPoint\AccountsReport.csv" # Define an appropriate file path for the data to be saved.
#---------------------------------------------------------------------------------------------------------------#

#------------------------------------------------ Begin Process ------------------------------------------------#
while (-not $token) {$token = Read-Host "Enter your Access Token"}
$AuthHeader = @{"Accept" = "application/json"; "Authorization" = "Bearer $token"}

try {
    $allAccounts = Invoke-RestMethod -Uri "$uri/v3/accounts" -Method GET -Headers $AuthHeader
    if (-not $allAccounts -or $allAccounts.Count -eq 0) {Write-Host "Response is empty." -BackgroundColor DarkYellow; return}
}
catch {
    Write-Host "Exception returned when attempting to retrieve accounts. Exception: ""$($_.Exception.Message)""" -BackgroundColor DarkRed
    return
}

$accountsGrouped = $allAccounts | Group-Object {$_.IdentityId}
$outData = @("Identity Id,Identity Name,Number of Sources,Number of Accounts")
$accountsGrouped | ForEach-Object {
    $identityId = $_.Group.identityId | Sort-Object -Unique
    $identityName = $_.Group.identity.name | Sort-Object -Unique
    $sourceCount = $_.Group | Sort-Object sourceName -Unique | Select-Object sourceName | Measure-Object | % Count
    $accountCount = $_.Count
    $outData += "$identityId,$identityName,$sourceCount,$accountCount"
}

if (-not $outData) {Write-Host "The outData variable is empty; nothing is present to save." -BackgroundColor DarkYellow; return}
while (-not $fileExportPath) {$fileExportPath = Read-Host "Enter a file path to which the data should be saved. Provide a csv file name as well"}
$outData | Out-File $fileExportPath
#-------------------------------------------------- End Process --------------------------------------------------#

:warning: Disclaimer

If saving this script locally or remotely, be sure to remove the value from the $token variable. Although tokens are issued temporarily and will expire, it is always a best practice to ensure any form of credentials are not stored in plain text and insecure manners.

Let me know if this helps!

2 Likes

Hello @brennenscott ,
thank you for the prompt response.
Script works perfectly, except that it stops when account sum hits the default limit of 250.
I’ve tried to workaround it with "$uri/v3/accounts?limit=550", but it gives error 400 Bad request.
Any suggestions, how we can skip the limit, so all identities and accounts are in report.
As i have thousands of those in total :slight_smile:

Thank you in advance,

Hey @karapuuzina!

That’s a great point; the maximum count for list endpoints is 250, so we could either use the a Search endpoint which has a limit of 10,000, or we can just iterate with the list endpoint. I modified the script to iterate until no accounts are left by adjusting the offset by increments of 250 each time we retrieve accounts. Please see the updated script below, and let me know how it goes.

# Preqrequisites
# 1. Generate an Access Token by either grabbing the token from your web browser with an active session with Identity Security Cloud, or other means such as through Postman. You can also generate one via PowerShell. Whatever works for you!
# 2. Either enter the token into the `$token` variable, or provide it in the console when prompted.
# 3. Provide your SailPoint Identity Security Cloud API url, such as `https://{Org name}.api.identitynow.com`
# 4. Provide a name of a file and its path in the `$exportFilePath` variable to which the data should be exported. **Make sure it is a text or CSV file**. You can also just provide this in the console when prompted.

#-------------------------------------------------- Variables --------------------------------------------------#
$token = "" # Either enter your access (bearer) token here or enter it into the console when prompted.
$url = "https://{Org name}.api.identitynow.com"
$fileExportPath = "C:\Users\brennen.scott\Downloads\AccountsReport.csv" # Define an appropriate file path for the data to be saved.
#---------------------------------------------------------------------------------------------------------------#

#------------------------------------------------ Begin Process ------------------------------------------------#
while (-not $token) {$token = Read-Host "Enter your Access Token"}
$AuthHeader = @{"Accept" = "application/json"; "Authorization" = "Bearer $token"}

try {
    $allAccounts = Invoke-RestMethod -Uri "$url/v3/accounts" -Method GET -Headers $AuthHeader
    if (-not $allAccounts -or $allAccounts.Count -eq 0) {Write-Host "Response is empty." -BackgroundColor DarkYellow; return}
    
    # Begin iterating if count is 250, then continue until response is empty, meaning there are no more accounts left to gather
    if ($allAccounts.Count -eq 250) {
        $iteratedAccounts = @("Empty")
        for ($offset = 250; $iteratedAccounts.Count -ne 0; $offset += 250) {
            $iteratedAccounts = Invoke-RestMethod -Uri "$url/v3/accounts?offset=$offset" -Method GET -Headers $AuthHeader
            $allAccounts += $iteratedAccounts
        }
    }
}
catch {
    Write-Host "Exception returned when attempting to retrieve accounts. Exception: ""$($_.Exception.Message)""" -BackgroundColor DarkRed
    return
}

$accountsGrouped = $allAccounts | Group-Object {$_.IdentityId}
$outData = @("Identity Id,Identity Name,Number of Sources,Number of Accounts")
$accountsGrouped | ForEach-Object {
    $identityId = $_.Group.identityId | Sort-Object -Unique
    $identityName = $_.Group.identity.name | Sort-Object -Unique
    $sourceCount = $_.Group | Sort-Object sourceName -Unique | Select-Object sourceName | Measure-Object | % Count
    $accountCount = $_.Count
    $outData += "$identityId,$identityName,$sourceCount,$accountCount"
}

if (-not $outData) {Write-Host "The outData variable is empty; nothing is present to save." -BackgroundColor DarkYellow; return}
while (-not ($fileExportPath)) {$fileExportPath = Read-Host "Enter a file path to which the data should be saved. Provide a csv file name as well"}
$outData | Out-File $fileExportPath
#-------------------------------------------------- End Process --------------------------------------------------#
2 Likes

Just to build on this, it can also be done by using the search endpoint and pulling back all identities instead of accounts. Since the returned identities include nested accounts, we can have Powershell count the accounts under each identity.

Using the bones from @brennenscott’s script (note: I also did my loop a little differently as a matter of personal preference, but either way should realistically work):

# Preqrequisites
# 1. Generate an Access Token by either grabbing the token from your web browser with an active session with Identity Security Cloud, or other means such as through Postman. You can also generate one via PowerShell. Whatever works for you!
# 2. Either enter the token into the `$token` variable, or provide it in the console when prompted.
# 3. Provide your SailPoint Identity Security Cloud API url, such as `https://{Org name}.api.identitynow.com`
# 4. Provide a name of a file and its path in the `$exportFilePath` variable to which the data should be exported. **Make sure it is a text or CSV file**. You can also just provide this in the console when prompted.

#-------------------------------------------------- Variables --------------------------------------------------#
$token = "" # Either enter your access (bearer) token here or enter it into the console when prompted.
$url = "https://{Org name}.api.identitynow.com"
$fileExportPath = "C:\Users\brennen.scott\Downloads\AccountsReport.csv" # Define an appropriate file path for the data to be saved.
#---------------------------------------------------------------------------------------------------------------#

#------------------------------------------------ Begin Process ------------------------------------------------#
while (-not $token) {$token = Read-Host "Enter your Access Token"}
$AuthHeader = @{"Accept" = "application/json"; "Authorization" = "Bearer $token"}

$body = @{
    query = @{
        query = '*'
    }
    queryResultFilter = @{
        includes = @(
            "id"
            "name"
            "accounts"
        )
    }
    sort = @(
        'id'
    )
    indices = @(
        'identities'
    )
}

try {    
    $allIdentities = Invoke-RestMethod -Uri "$url/v3/search?count=true" -Method POST -Body ($body | ConvertTo-Json) -Headers $AuthHeader -ResponseHeadersVariable responseHeaders -ContentType 'application/json' -ErrorAction Stop
    if (-not $allIdentities -or $allIdentities.Count -eq 0) {
        Write-Host "Response is empty." -BackgroundColor DarkYellow
        return
    }
    $totalCount = [int]::Parse($responseHeaders.'X-Total-Count')
    $retrievedCount = $allIdentities.Count
    Write-Host "Retrieved $retrievedCount items out of total $totalCount."
    while ($retrievedCount -lt $totalCount) {
        $p = $retrievedCount/$totalCount
        Write-Progress -Activity "Retrieving identities" -Status "Retrieved $retrievedCount of $totalCount - $($p.ToString("P")) Complete" -PercentComplete ($p*100)
        try {
            $nextQuery = $body + @{searchAfter = @($allIdentities[-1].id) }
            $allIdentities += Invoke-RestMethod -Uri "$url/v3/search" -Method Post -Body ($nextQuery | ConvertTo-Json) -Headers $AuthHeader -ContentType 'application/json' -ErrorAction Stop
            $retrievedCount = $allIdentities.count
        }
        catch {
            Write-Host "Retrieval failed. Will try again. Exception: $($_.Exception.Message)"
        }
    }
}
catch {
    Write-Host "Exception returned when attempting to retrieve identities. Exception: $($_.Exception.Message)" -BackgroundColor DarkRed
    return
}

$outData = $allIdentities | Select-Object id,name,@{label="sourceCount";expression={($_.accounts.source.id | Sort-Object -Unique).count}},@{label="accountCount";expression={$_.accounts.count}}

if (-not $outData) {Write-Host "The outData variable is empty; nothing is present to save." -BackgroundColor DarkYellow; return}
while (-not ($fileExportPath)) {$fileExportPath = Read-Host "Enter a file path to which the data should be saved. Provide a csv file name as well"}
$outData | Export-Csv $fileExportPath -NoTypeInformation
#-------------------------------------------------- End Process --------------------------------------------------#
2 Likes

This seems like a lot of extra code to write when Invoke-Paginate exists for this specific purpose in the PowerShell SDK

2 Likes

Hello @brennenscott this worked perfectly fine.
Thnak you!

@mcheek this looks very useful.
Thank you, will test out the module.

True, but our examples don’t otherwise leverage the SDK and can be used as-is without any other dependencies like installing modules. No sense making someone go through the effort of installing the module just for the sake of pagination.

On a somewhat related topic, I have an Idea in on the Ideas portal asking for the implementation of pagination response headers which could help simplify all of this :slight_smile:

2 Likes