How to Populate AD Group Owner into SailPoint IDN Entitlement Custom Attributes?

I’m working on an IdentityNow (IDN) integration where I need to aggregate AD entitlements (AD Security Groups) and also populate the AD group owner (from the managedBy attribute) into IDN entitlement custom/extended attributes.

Goal

  • Aggregate the AD group attribute managedBy (DN of the owner).

  • Store this DN in a custom entitlement attribute like ownerDn.

Why I need custom/extended attribute?

  • managedBy value cannot be written directly into the OOTB entitlement owner field.

Hi @Amrit1897

As far as I know, there’s no way to do this out of the box. I’ve implemented a custom powershell script that can perform this daily by using task scheduler.

At a high level, this script basically scans all the entitlements in your Active Directory source, uses a get-adgroup commandlet call to find the managedBy field, compares it against the current value to see if it needs to be updated, then if it does, it will use a patch request to update the field in ISC.

Let me know if you have any questions or concerns!

$client_id = "CLIENTID"
$importedsecret = Import-Clixml -Path 'PATH TO SECRET FILE'
$client_secret = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($importedsecret))

$tokenRequest = Invoke-RestMethod -Method Post -Uri "https://tenant.api.identitynow.com/oauth/token?grant_type=client_credentials&client_id=$($client_id)&client_secret=$($client_secret)"
$headertoken = $tokenRequest.access_token
$headers = New-Object "System.Collections.Generic.Dictionary[[String], [String]]"
$headers.Add("Content-Type", "application/json")
$headers.Add("Accept", "application/json")
$headers.Add("Authorization", "Bearer $($headertoken)")
$patchheaders = New-Object "System.Collections.Generic.Dictionary[[String], [String]]"
$patchheaders.Add("Content-Type", "application/json-patch+json")
$patchheaders.Add("Accept", "application/json")
$patchheaders.Add("Authorization", "Bearer $($headertoken)")

$LogFile = "D:\powershell\ISC\logs\SetADOwnerField.log"
$sbADId = "ACTIVEDIRECTORYSOURCEID"

$offset = 0
$limit = 250
$allresults = @()
while ($true) {

    $url = "https://tenant.api.identitynow.com/v2024/entitlements?offset=$offset&filters=source.id eq `"" + $sbADId + "`""
    $getresponse = Invoke-RestMethod -Method get -Uri $url -Headers $headers

    if($getresponse.Count -eq 0) {
        Write-Host "No More Records"
        break
    }
    Write-Host "Fetched $($getresponse.Count) Records at offset $offset"
    $allresults += $getresponse
    $offset += $limit
}


$date = get-date -Format 'MMddyyyy'

$groupsfound = 0
$groupsupdated = 0
$groupsnotfound = 0

foreach ($item in $allresults) {
    $group = $null
    $group = get-adgroup -Identity $item.value -Properties ManagedBy
    if($group.DistinguishedName -ne $null) {
        "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [INFO] Found group: $($item.name)" | out-file -FilePath $LogFile -Append
        $groupsfound++
        if($null -ne $group.ManagedBy) {
            $user = (Get-ADUser -Identity $group.ManagedBy).SamAccountName
            if($null -ne $user) {
                $userlookupurl = "https://tenant.api.identitynow.com/v2024/identities?filters=alias eq `"" + $user + "`""
                $userlookup = Invoke-RestMethod -Method get -Uri $userlookupurl -Headers $headers
                $userid = $userlookup.id
                $username = $userlookup.name
                if($item.owner.id -ne $userid) { # only updating if we see a mismatch between AD & SP.
                    $patchbodyOwner = @"
                    [
                        {
                            "op": "replace",
                            "path": "/owner",
                            "value":
                            {
                                "id": "$userid",
                                "type": "IDENTITY",
                                "name": "$username"
                            }
                        }
                    ]
"@

                    $urlpatch = "https://tenant.api.identitynow.com/v2024/entitlements/" + $item.id

                    try {
                        $patchresponse = Invoke-RestMethod -Method patch -Uri $urlpatch -Headers $patchheaders -body $patchbodyOwner
                        "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [INFO] Updated Group Owner: $($item.name) -> $user" | Out-File -FilePath $LogFile -Append
                        $groupsupdated++
                    }
                    catch {
                        "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [ERROR] Unexpected Error during patch request: $($item.name) -> $user" | Out-File -FilePath $LogFile -Append
                    }
                }
            }
        }
    }
    else
    {
        "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [ERROR] Failed to find Group: $($item.value)" | Out-File -FilePath $LogFile -Append
        $groupsnotfound++
    }
}

"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [INFO] Finished updates with Stats: Groups Not Found: $groupsnotfound --- Groups Found: $groupsfound --- Groups Updated: $groupsupdated" | Out-File -FilePath $LogFile -Append

Thanks for the response @trettkowski

Apart from OOTB owner attribute in SailPoint, can managedBy value be stored in some custom attribute in SailPoint upon entitlement aggregation?

Hi @Amrit1897 What happens when you add managedBy to the Entitlement Schema? You won’t be able to rename the attribute to ownerDn

As far as I know, no that’s not possible. I would try what @j_place mentioned to bring in the managedBy field automatically. Otherwise, you will need to do what I suggested and stored it in either the owner field or some other patchable field like below:

requestable
privileged
segments
owner
name
description
manuallyUpdatedFields

Hi,
Our ‘solution’ was to create a 2nd custom connector which can bring in that value, then import data from both connectors into ServiceNow and let it combine the two.
Not pretty, but does work