internal/functions/user/New-EntraIdentityFilter.ps1

function New-EntraIdentityFilter {
    <#
    .SYNOPSIS
        Builds the OData clause that filters users on their identities collection.

    .DESCRIPTION
        Composes the lambda Microsoft Graph expects for the identities collection:

            identities/any(i:i/issuer eq '<issuer>' and i/issuerAssignedId eq '<id>')

        Every value goes through ConvertTo-ODataFilterString, so an apostrophe in an
        issuer or a sign-in name cannot break out of the string literal.

        Graph does not support the full cross product here, and the unsupported shapes
        fail by returning nothing rather than by erroring - which reads exactly like
        "no such guest". This function therefore refuses the one shape Graph documents
        as unsupported, and warns about the one that only works for four issuers.

    .PARAMETER Cmdlet
        The $PSCmdlet of the calling command, so a rejected filter terminates in the
        caller's context rather than here.

    .PARAMETER Issuer
        The issuer to match - the home organization's domain for a B2B guest, or a
        provider such as google.com. Required: Graph supports issuerAssignedId and
        signInType only in combination with it.

    .PARAMETER IssuerAssignedId
        The identifier the issuer assigned to the user, typically their sign-in name at
        the home organization.

    .PARAMETER SignInType
        The sign-in type to match, such as federated, userName or emailAddress.
        userPrincipalName is rejected - Graph documents it as unsupported for filtering.

    .EXAMPLE
        PS C:\> New-EntraIdentityFilter -Cmdlet $PSCmdlet -Issuer 'contoso.com' -IssuerAssignedId 'j.smith@contoso.com'

        identities/any(i:i/issuer eq 'contoso.com' and i/issuerAssignedId eq 'j.smith@contoso.com')

    .EXAMPLE
        PS C:\> New-EntraIdentityFilter -Cmdlet $PSCmdlet -Issuer 'google.com'

        identities/any(i:i/issuer eq 'google.com')

        One of the four issuers Graph will match on its own.

    .NOTES
        Internal helper. See
        https://learn.microsoft.com/en-us/graph/api/resources/objectidentity
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Builds a filter string in memory. The New verb names what it returns; nothing local or remote is changed, so there is nothing to confirm.')]
    [OutputType([string])]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        $Cmdlet,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $Issuer,

        [Parameter()]
        [AllowNull()]
        [AllowEmptyString()]
        [string] $IssuerAssignedId,

        [Parameter()]
        [AllowNull()]
        [AllowEmptyString()]
        [string] $SignInType
    )

    # Graph: "Filtering for entries with a signInType of userPrincipalName isn't
    # supported. You can filter on the userPrincipalName property on the user object
    # instead." Left to Graph this returns an empty set, not an error.
    if ($SignInType -and $SignInType -eq 'userPrincipalName') {
        Invoke-TerminatingException -Cmdlet $Cmdlet -Category InvalidArgument -Message (
            Get-PSFLocalizedString -Module $script:ModuleName -Name 'User.Identity.SignInType.Unsupported'
        )
    }

    # Graph: filtering on issuer alone works for these four only. Anything else needs
    # issuerAssignedId as well, and silently matches nothing without it.
    $issuerAloneSupported = @('google.com', 'facebook.com', 'mail', 'phone')
    if (-not $IssuerAssignedId -and $Issuer -notin $issuerAloneSupported) {
        Write-PSFMessage -Level Warning -String 'User.Identity.Issuer.Alone' -StringValues $Issuer, ($issuerAloneSupported -join ', ')
    }

    $clauses = @("i/issuer eq '{0}'" -f (ConvertTo-ODataFilterString -Value $Issuer))

    if ($IssuerAssignedId) {
        $clauses += "i/issuerAssignedId eq '{0}'" -f (ConvertTo-ODataFilterString -Value $IssuerAssignedId)
    }

    if ($SignInType) {
        $clauses += "i/signInType eq '{0}'" -f (ConvertTo-ODataFilterString -Value $SignInType)
    }

    return 'identities/any(i:{0})' -f ($clauses -join ' and ')
}