modules/Azure/Discovery/Private/BuildCIEMEntraTransitiveMembership.ps1

function BuildCIEMEntraTransitiveMembership {
    param(
        [Parameter(Mandatory)]
        [AllowEmptyCollection()]
        [object[]]$Relationships,

        [Parameter(Mandatory)]
        [string]$CollectedAt
    )

    $directMemberships = @($Relationships | Where-Object {
        $_.Relationship -eq 'member_of' -and $_.TargetType -eq 'group'
    })
    if ($directMemberships.Count -eq 0) {
        return @()
    }

    $groupParents = @{}
    foreach ($relationship in $directMemberships) {
        if ($relationship.SourceType -ne 'group') {
            continue
        }

        if (-not $groupParents.ContainsKey($relationship.SourceId)) {
            $groupParents[$relationship.SourceId] = [System.Collections.Generic.HashSet[string]]::new()
        }

        $null = $groupParents[$relationship.SourceId].Add($relationship.TargetId)
    }

    $results = [System.Collections.Generic.List[CIEMAzureResourceRelationship]]::new()
    $seenPairs = [System.Collections.Generic.HashSet[string]]::new()

    foreach ($relationship in $directMemberships) {
        $visitedGroups = [System.Collections.Generic.HashSet[string]]::new()
        $queue = [System.Collections.Generic.Queue[string]]::new()
        $queue.Enqueue($relationship.TargetId)

        while ($queue.Count -gt 0) {
            $groupId = $queue.Dequeue()
            if (-not $visitedGroups.Add($groupId)) {
                continue
            }

            $pairKey = "$($relationship.SourceId)|$groupId"
            if ($seenPairs.Add($pairKey)) {
                $transitiveRelationship = [CIEMAzureResourceRelationship]::new()
                $transitiveRelationship.SourceId = $relationship.SourceId
                $transitiveRelationship.SourceType = $relationship.SourceType
                $transitiveRelationship.TargetId = $groupId
                $transitiveRelationship.TargetType = 'group'
                $transitiveRelationship.Relationship = 'transitive_member_of'
                $transitiveRelationship.CollectedAt = $CollectedAt
                $results.Add($transitiveRelationship)
            }

            if ($groupParents.ContainsKey($groupId)) {
                foreach ($parentGroupId in $groupParents[$groupId]) {
                    $queue.Enqueue($parentGroupId)
                }
            }
        }
    }

    $results
}