public/Get-odscTargetUser.ps1

function Get-odscTargetUser {
    [CmdletBinding(DefaultParameterSetName = 'AllUsers')]
    param(
        [Parameter(Mandatory = $true, ParameterSetName = 'Csv')]
        [string] $CsvPath,

        [Parameter(Mandatory = $true, ParameterSetName = 'Group')]
        [string] $GroupId,

        [Parameter(Mandatory = $true, ParameterSetName = 'Filter')]
        [string] $Filter,

        [Parameter(Mandatory = $true, ParameterSetName = 'AllUsers')]
        [switch] $AllUsers,

        [Parameter(Mandatory = $false, ParameterSetName = 'Group')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Filter')]
        [Parameter(Mandatory = $false, ParameterSetName = 'AllUsers')]
        [switch] $IncludeDisabled
    )

    process {
        try {
            # Advanced queries (OData casts, $count, most filters) require ConsistencyLevel: eventual.
            $AdvancedQuery = @{
                Method = [Microsoft.PowerShell.Commands.WebRequestMethod]::Get
                Headers = @{ ConsistencyLevel = 'eventual' }
                AllPages = $true
                ErrorAction = 'Stop'
            }
            $Select = '$select=id,userPrincipalName,mail,accountEnabled&$count=true'

            switch ($PsCmdlet.ParameterSetName) {
                'Csv' {
                    Import-Csv -Path $CsvPath | ForEach-Object {
                        $UserObjectId = if ($_.UserObjectId) { $_.UserObjectId } else { $_.Id }
                        if (-not $_.UserPrincipalName -and -not $UserObjectId) {
                            Write-Warning "Skipping a row in '$CsvPath' without UserPrincipalName, UserObjectId or Id."
                            return
                        }

                        [pscustomobject]@{
                            UserPrincipalName = $_.UserPrincipalName
                            UserObjectId = $UserObjectId
                            Mail = $_.Mail
                            AccountEnabled = $null
                            Source = $CsvPath
                        }
                    }
                }
                'Group' {
                    try {
                        $Users = Invoke-odscApiRequest @AdvancedQuery -Resource "groups/${GroupId}/transitiveMembers/microsoft.graph.user?$Select"
                    } catch {
                        Stop-odscGraphError -ErrorRecord $_ `
                            -ForbiddenMessage "Unable to read transitive user members for group '$GroupId'. Microsoft Graph returned 403. Grant admin consent for GroupMember.Read.All, or use a broader equivalent such as Group.Read.All or Directory.Read.All. Hidden membership groups also require Member.Read.Hidden." `
                            -FallbackMessage "Unable to resolve users from group '$GroupId'."
                    }
                    $Source = $GroupId
                }
                'Filter' {
                    $EncodedFilter = [uri]::EscapeDataString($Filter)
                    try {
                        $Users = Invoke-odscApiRequest @AdvancedQuery -Resource "users?`$filter=${EncodedFilter}&$Select"
                    } catch {
                        Stop-odscGraphError -ErrorRecord $_ -FallbackMessage "Unable to resolve users with filter '$Filter'."
                    }
                    $Source = $Filter
                }
                'AllUsers' {
                    $null = $AllUsers
                    try {
                        $Users = Invoke-odscApiRequest @AdvancedQuery -Resource "users?$Select"
                    } catch {
                        Stop-odscGraphError -ErrorRecord $_ -FallbackMessage 'Unable to list users.'
                    }
                    $Source = 'AllUsers'
                }
            }

            if ($PsCmdlet.ParameterSetName -eq 'Csv') {
                return
            }

            # Disabled accounts are skipped unless requested. Users without the property are kept.
            $Disabled = @($Users | Where-Object { $_.accountEnabled -eq $false })
            if ($Disabled.Count -gt 0 -and -not $IncludeDisabled) {
                Write-Verbose "Skipping $($Disabled.Count) disabled account(s) from '$Source'. Use -IncludeDisabled to include them."
                $Users = @($Users | Where-Object { $_.accountEnabled -ne $false })
            }

            $Users | ForEach-Object {
                [pscustomobject]@{
                    UserPrincipalName = $_.userPrincipalName
                    UserObjectId = $_.id
                    Mail = $_.mail
                    AccountEnabled = $_.accountEnabled
                    Source = $Source
                }
            }
        } catch {
            Write-Error $_.Exception.Message
        }
    }
}