Groups update to bulk roles

Hi ,

i am trying to add 3 entitlements to few roles which is already exist in sailpoint. via VS code but it is adding only 1 entitlement.
i tried with “;” separator and also tried with one one entitlement in each row but still it is updating only latest onr

Hi Rahul,

Could you provide a bit more detail on how you are updating the roles in ISC?

It is not fully clear from the description whether this is being done through:

  • VS Code plugin / SailPoint extension

  • API / JSON payload

  • Bulk import process

  • Custom script or automation

Also, when you mention using a “;” separator, where exactly are you applying it?

For example:

  • In the role definition JSON?

  • In the access profile / entitlement mapping?

  • In a CSV or bulk input file?

If possible, please share a sanitized example (without confidential data) of the structure you are using. That will help identify why only the latest entitlement is being added and whether the current update is overwriting previous values or if there is a formatting issue.

For example in VS code through CSV file i am update the existing roles.

I am adding groups to roles, below is the example of csv data i am uploading in entitlements column

Sourcename1|mem of|group name1;sourcename2|mem of|groupname2

@RahulMekala

Can you please explain the steps that you are taking and through which API or VS Code configuration.

@RahulMekala you can create a small script which can be useful here .

Can you share me script

Hi @RahulMekala,

You can use the SailPoint provided Bulk Role Importer Ruby script for this purpose. Please refer to the link below for more details:

IdentityNow Bulk Access Profile and Role Importer - Compass

type or
<#
.SYNOPSIS
    Patch SailPoint ISC roles by adding entitlements from a CSV file.
.DESCRIPTION
    Reads a CSV with columns: roleId, entitlementId, entitlementName
    and patches each role to add the specified entitlements via the V2025 API.
    Groups entitlements by roleId so each role is patched once with all its new entitlements.
.PARAMETER CsvPath
    Path to the input CSV file.
.PARAMETER Tenant
    Your ISC tenant name (e.g., "sample").
.PARAMETER ClientId
    OAuth client ID for API authentication.
.PARAMETER ClientSecret
    OAuth client secret for API authentication.
#>

param(
    [Parameter(Mandatory = $true)]
    [string]$CsvPath,

    [Parameter(Mandatory = $false)]
    [string]$Tenant = "sample",

    [Parameter(Mandatory = $true)]
    [string]$ClientId,

    [Parameter(Mandatory = $true)]
    [string]$ClientSecret
)

# ── Validate CSV exists ──
if (-not (Test-Path $CsvPath)) {
    Write-Error "CSV file not found: $CsvPath"
    exit 1
}

# ── Get OAuth access token ──
function Get-AccessToken {
    param([string]$Tenant, [string]$ClientId, [string]$ClientSecret)

    $tokenUrl = "https://$Tenant.api.identitynow.com/oauth/token"
    $body = @{
        grant_type    = "client_credentials"
        client_id     = $ClientId
        client_secret = $ClientSecret
    }

    try {
        $response = Invoke-RestMethod -Uri $tokenUrl -Method Post -Body $body -ContentType "application/x-www-form-urlencoded"
        return $response.access_token
    }
    catch {
        Write-Error "Failed to obtain access token: $_"
        exit 1
    }
}

# ── Get current entitlements on a role ──
function Get-RoleEntitlements {
    param([string]$AccessToken, [string]$Tenant, [string]$RoleId)

    $url = "https://$Tenant.api.identitynow.com/v2025/roles/$RoleId"
    $headers = @{
        "Authorization" = "Bearer $AccessToken"
        "Accept"        = "application/json"
    }

    try {
        $role = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
        if ($role.entitlements) {
            return $role.entitlements
        }
        return @()
    }
    catch {
        Write-Warning "Failed to get role $RoleId : $($_.Exception.Message)"
        return @()
    }
}

# ── Patch a role to add entitlements ──
function Add-EntitlementsToRole {
    param(
        [string]$AccessToken,
        [string]$Tenant,
        [string]$RoleId,
        [array]$NewEntitlements
    )

    $url = "https://$Tenant.api.identitynow.com/v2025/roles/$RoleId"

    $headers = @{
        "Authorization" = "Bearer $AccessToken"
        "Content-Type"  = "application/json-patch+json"
        "Accept"        = "application/json"
    }

    # Build the add operations for each entitlement
    $patchOps = @()
    foreach ($ent in $NewEntitlements) {
        $patchOps += @{
            op    = "add"
            path  = "/entitlements/-"
            value = @{
                id   = $ent.entitlementId
                type = "ENTITLEMENT"
                name = $ent.entitlementName
            }
        }
    }

    $bodyJson = $patchOps | ConvertTo-Json -Depth 5
    # Ensure it's always an array
    if ($patchOps.Count -eq 1) {
        $bodyJson = "[$bodyJson]"
    }

    try {
        $response = Invoke-RestMethod -Uri $url -Method Patch -Headers $headers -Body $bodyJson
        return @{ Success = $true; Response = $response }
    }
    catch {
        $errorDetail = $_.ErrorDetails.Message
        if (-not $errorDetail) { $errorDetail = $_.Exception.Message }
        return @{ Success = $false; Error = $errorDetail }
    }
}

# ── Main ──
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Role Entitlement Patcher" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan

$accessToken = Get-AccessToken -Tenant $Tenant -ClientId $ClientId -ClientSecret $ClientSecret
Write-Host "Access token obtained successfully." -ForegroundColor Green

$csvData = Import-Csv -Path $CsvPath
Write-Host "Loaded $($csvData.Count) rows from CSV.`n" -ForegroundColor Yellow

# Group entitlements by roleId so each role is patched once
$grouped = $csvData | Group-Object -Property roleId

$successCount = 0
$failCount = 0
$results = @()

foreach ($group in $grouped) {
    $roleId = $group.Name
    $entitlements = $group.Group
    $entNames = ($entitlements | ForEach-Object { $_.entitlementName }) -join ", "

    Write-Host "Patching role [$roleId] with $($entitlements.Count) entitlement(s): $entNames ..." -NoNewline

    $result = Add-EntitlementsToRole `
        -AccessToken $accessToken `
        -Tenant $Tenant `
        -RoleId $roleId `
        -NewEntitlements $entitlements

    if ($result.Success) {
        Write-Host " SUCCESS" -ForegroundColor Green
        $successCount++
        $results += [PSCustomObject]@{
            RoleId  = $roleId
            Count   = $entitlements.Count
            Status  = "Success"
            Detail  = "Patched"
        }
    }
    else {
        Write-Host " FAILED" -ForegroundColor Red
        Write-Host "   Error: $($result.Error)" -ForegroundColor Red
        $failCount++
        $results += [PSCustomObject]@{
            RoleId  = $roleId
            Count   = $entitlements.Count
            Status  = "Failed"
            Detail  = $result.Error
        }
    }

    # Brief pause to avoid rate limiting
    Start-Sleep -Milliseconds 500
}

# ── Summary ──
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host " Summary: $successCount role(s) patched, $failCount failed out of $($grouped.Count)" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan

# Export results log
$logPath = [System.IO.Path]::ChangeExtension($CsvPath, "_results.csv")
$results | Export-Csv -Path $logPath -NoTypeInformation
Write-Host "Results exported to: $logPath" -ForegroundColor Yellow
 paste code here

csv sample
roleId,entitlementId,entitlementName
8aa952420f9f4d9286e9ca3daea2333b,2c91808a7e6f12ab017e6f12ab0c0001,SalesforceEntitlement1
8aa952420f9f4d9286e9ca3daea2333b,2c91808a7e6f12ab017e6f12ab0c0002,SalesforceEntitlement2
abcd1234ef5678901234567890abcdef,2c91808a7e6f12ab017e6f12ab0c0003,AnotherRoleEntitlement

Hi @RahulMekala You cannot import multiple entitlements into a single role using “;”. I recommend using the pipe symbol “|” instead and then retrying—it should work.

Error: Invalid entitlement format: Active directory|memberOf|wlannet|Active directory|memberOf|1234Staff

i am getting error with the formate

Hi @RahulMekala Just do one thing, export the file from vscode and put replace with your needed roles and entitlements, also just fyi, i have added the sample file for your visibility, hoping this will work for you now

company19188-poc.identitynow-demo.com-Roles.csv (680 Bytes)

Tried but not working

Can you please share the Screenshot of error.

Error: Invalid entitlement format: Active directory|memberOf|wlannet|Active directory|memberOf|1234Staff

If still your issues not yet resolved then please create a separate topic as this is already marked as resolved so that we can talk in brief . anyway, kindly try to remove member of and then try and do let me know. If not work

I unfrotunately clicked on solution, i am getting 0 lines processed error

try once like this Active directory|wlannet;Active directory|1234Staff

@RahulMekala , You can use the bulk role importer written in ruby script, it is available in sailpoint compass channel, it makes easy to bulk update or import roles.