private/get-PnPGroupMembers.ps1

Function Get-PnPGroupMembers{
    <#
        Author = "Jos Lieben (jos@lieben.nu)"
        CompanyName = "Lieben Consultancy"
        Copyright = "https://www.lieben.nu/liebensraum/commercial-use/"
    #>
        
    Param(
        [Parameter(Mandatory=$true)]$group,
        $parentId,
        [Parameter(Mandatory=$true)]$siteConn
    )

    Write-Verbose "Getting members for group $($group.Title)"

    if($Null -eq $global:groupCache){
        $global:groupCache = @{}
    }
    if($global:groupCache.Keys -contains $($group.Title)){
        return $global:groupCache.$($group.Title)
    }else{
        $global:groupCache.$($group.Title) = @()
    }

    $groupGuid = $Null; try{$groupGuid = $group.LoginName.Split("|")[2].Split("_")[0]}catch{$groupGuid = $Null}
    if($groupGuid -and [guid]::TryParse($groupGuid, $([ref][guid]::Empty))){
        $graphMembers = New-GraphQuery -Uri "https://graph.microsoft.com/v1.0/groups/$groupGuid/transitiveMembers" -Method GET | Where-Object { $_."@odata.type" -eq "#microsoft.graph.user" }
        foreach($graphMember in $graphMembers){
            if(!($global:groupCache.$($group.Title).LoginName | Where-Object {$_ -and $_.EndsWith($graphMember.userPrincipalName)})){
                Write-Verbose "Found $($graphMember.displayName) in graph group"
                $global:groupCache.$($group.Title) += [PSCustomObject]@{
                    "Title" = $graphMember.displayName
                    "LoginName" = "i:0#.f|membership|$($graphMember.userPrincipalName)"
                    "PrincipalType" = "User"
                    "Email" = $graphMember.mail
                }
            }
        }
        continue
    }else{
        $members = Get-PnPGroupMember -Group $group.Id -Connection (Get-SpOConnection -Type User -Url $site.Url)
        foreach($member in $members){   
            $groupGuid = $Null; try{$groupGuid = $member.LoginName.Split("|")[2].Split("_")[0]}catch{$groupGuid = $Null}
            if($member.LoginName -like "*spo-grid-all-users*" -or $member.LoginName -eq "c:0(.s|true"){
                Write-Verbose "Found $($member.Title) special group"
                $global:groupCache.$($group.Title) += $member
                continue
            }
            if($groupGuid -and [guid]::TryParse($groupGuid, $([ref][guid]::Empty))){
                $graphMembers = New-GraphQuery -Uri "https://graph.microsoft.com/v1.0/groups/$groupGuid/transitiveMembers" -Method GET | Where-Object { $_."@odata.type" -eq "#microsoft.graph.user" }
                foreach($graphMember in $graphMembers){
                    if(!($global:groupCache.$($group.Title).LoginName | Where-Object {$_ -and $_.EndsWith($graphMember.userPrincipalName)})){
                        Write-Verbose "Found $($graphMember.displayName) in graph group"
                        $global:groupCache.$($group.Title) += [PSCustomObject]@{
                            "Title" = $graphMember.displayName
                            "LoginName" = "i:0#.f|membership|$($graphMember.userPrincipalName)"
                            "PrincipalType" = "User"
                            "Email" = $graphMember.mail
                        }
                    }
                }
                continue
            }
            if($member.Id -ne $parentId){
                if($member.PrincipalType -eq "User" -and $global:groupCache.$($group.Title) -notcontains $member){
                    Write-Verbose "Found $($member.Title) in sec group"
                    $global:groupCache.$($group.Title) += $member
                    continue
                }
                if($member.PrincipalType -eq "SecurityGroup" -or $member.PrincipalType -eq "SharePointGroup"){
                    $subMembers = Get-PnPGroupMembers -name $member.Title -parentId $member.Id -siteConn $siteConn
                    foreach($subMember in $subMembers){
                        if($global:groupCache.$($group.Title) -notcontains $subMember){
                            Write-Verbose "Found $($subMember.Title) in sub sec group"
                            $global:groupCache.$($group.Title) += $subMember
                        }
                    }
                }
            }
        }
    }   


    return $global:groupCache.$($group.Title)
}