public/identity-protection.ps1

function Invoke-FalconIdentityGraph {
<#
.SYNOPSIS
Interact with Falcon Identity using GraphQL
.DESCRIPTION
The 'All' parameter requires that your GraphQL statement contain an 'after' cursor variable definition and
'pageInfo { hasNextPage endCursor }'.
 
Requires 'Identity Protection GraphQL: Write', and other 'Identity Protection' permission(s) depending on query.
.PARAMETER String
A complete GraphQL statement
.PARAMETER Variable
A hashtable containing variables used in your GraphQL statement
.PARAMETER All
Repeat requests until all available results are retrieved
.LINK
https://github.com/crowdstrike/psfalcon/wiki/Invoke-FalconIdentityGraph
#>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory,ValueFromPipeline,Position=1)]
        [Alias('query','mutation')]
        [string]$String,
        [Parameter(ValueFromPipeline,Position=2)]
        [Alias('variables')]
        [hashtable]$Variable,
        [switch]$All
    )
    begin {
        function Assert-CursorVariable ($Inputs,$EndCursor) {
            # Use variable definition to ensure 'Cursor' is within 'Variable' hashtable
            if ($Inputs.query -match '^(\s+)?query\s+?\(.+Cursor') {
                @([regex]::Matches($Inputs.query,
                    '(?<=query\s+?\()(\$\w+:.[^\)]+)').Value -replace '\$',$null).foreach{
                    $Array = ($_ -split ':',2).Trim()
                    if ($Array[1] -eq 'Cursor') {
                        if (!$Inputs.variables) {
                            $Inputs.Add('variables',@{ $Array[0] = $EndCursor })
                        } elseif ($Inputs.variables.($Array[0])) {
                            $Inputs.variables.($Array[0]) = $EndCursor
                        }
                    }
                }
            }
            return $Inputs
        }
        function Invoke-GraphLoop ($Object,$Splat,$Inputs) {
            $RegEx = @{
                # Patterns to validate statement for 'pageInfo' and 'Cursor' variable
                CursorVariable = '^(\s+)?query\s+?\(.+Cursor'
                PageInfo = 'pageInfo(\s+)?{(\s+)?(hasNextPage([,\s]+)?|endCursor([,\s]+)?){2}(\s+)?}'
            }
            [string]$Message = if ($Inputs.query -notmatch $RegEx.CursorVariable) {
                "'-All' parameter was specified but 'Cursor' definition is missing from statement."
            } elseif ($Inputs.query -notmatch $RegEx.PageInfo) {
                "'-All' parameter was specified but 'pageInfo' is missing from statement."
            }
            if ($Message) {
                $PSCmdlet.WriteWarning(("[$($Splat.Command)]",$Message -join ' '))
            } else {
                do {
                    if ($Object.entities.pageInfo.endCursor) {
                        # Update 'Cursor' and repeat
                        $Inputs = Assert-CursorVariable $Inputs $Object.entities.pageInfo.endCursor
                        Write-GraphResult (Invoke-Falcon @Splat -Inputs $Inputs -OutVariable Object)
                    }
                } while (
                    $Object.entities.pageInfo.hasNextPage -eq $true -and $null -ne
                        $Object.entities.pageInfo.endCursor
                )
            }
        }
        function Write-GraphResult ($Object) {
            if ($Object.entities.pageInfo) {
                # Output verbose 'pageInfo' detail
                [string]$Message = (@($Object.entities.pageInfo.PSObject.Properties).foreach{
                    $_.Name,$_.Value -join '='
                }) -join ', '
                Write-Log 'Invoke-FalconIdentityGraph' ($Message -join ' ')
            }
            # Output 'nodes'
            if ($Object.entities.nodes) { $Object.entities.nodes } else { $Object }
        }
        $Param = @{
            Command = $MyInvocation.MyCommand.Name
            Endpoint = '/identity-protection/combined/graphql/v1:post'
            Format = @{ Body = @{ root = @('query','variables') }}
        }
    }
    process {
        if ($PSBoundParameters.All) {
            Write-GraphResult (Invoke-Falcon @Param -Inputs $PSBoundParameters -OutVariable Request)
        } else {
            Invoke-Falcon @Param -Inputs $PSBoundParameters
        }
    }
    end { if ($Request -and $PSBoundParameters.All) { Invoke-GraphLoop $Request $Param $PSBoundParameters }}
}