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 ($UserInput,$EndCursor) { # Use variable definition to ensure 'Cursor' is within 'Variable' hashtable if ($UserInput.query -match '^(\s+)?query\s+?\(.+Cursor') { @([regex]::Matches($UserInput.query, '(?<=query\s+?\()(\$\w+:.[^\)]+)').Value -replace '\$',$null).foreach{ $Array = ($_ -split ':',2).Trim() if ($Array[1] -eq 'Cursor') { if (!$UserInput.variables) { $UserInput.Add('variables',@{ $Array[0] = $EndCursor }) } elseif ($UserInput.variables.($Array[0])) { $UserInput.variables.($Array[0]) = $EndCursor } } } } return $UserInput } function Invoke-GraphLoop ($Object,$Splat,$UserInput) { $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 ($UserInput.query -notmatch $RegEx.CursorVariable) { "'-All' parameter was specified but 'Cursor' definition is missing from statement." } elseif ($UserInput.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 $UserInput = Assert-CursorVariable $UserInput $Object.entities.pageInfo.endCursor Write-GraphResult (Invoke-Falcon @Splat -UserInput $UserInput -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 -UserInput $PSBoundParameters -OutVariable Request) } else { Invoke-Falcon @Param -UserInput $PSBoundParameters } } end { if ($Request -and $PSBoundParameters.All) { Invoke-GraphLoop $Request $Param $PSBoundParameters }} } function Get-FalconIdentityHost { <# .SYNOPSIS Search for Falcon Identity hosts .DESCRIPTION Requires 'Identity Protection Entities: Read'. .PARAMETER Filter Falcon Query Language expression to limit results .PARAMETER Sort Property and direction to sort results .PARAMETER Limit Maximum number of results per request .PARAMETER Offset Position to begin retrieving results .PARAMETER Detailed Retrieve detailed information .PARAMETER All Repeat requests until all available results are retrieved .PARAMETER Total Display total result count instead of results .LINK https://github.com/crowdstrike/psfalcon/wiki/Get-FalconIdentityHost #> [CmdletBinding(DefaultParameterSetName='/identity-protection/queries/devices/v1:get',SupportsShouldProcess)] param( [Parameter(ParameterSetName='/identity-protection/entities/devices/GET/v1:post', ValueFromPipelineByPropertyName,ValueFromPipeline,Mandatory)] [ValidatePattern('^[a-fA-F0-9]{32}$')] [Alias('Ids','device_id','host_ids','aid')] [string[]]$Id, [Parameter(ParameterSetName='/identity-protection/queries/devices/v1:get',Position=1)] [ValidateScript({ Test-FqlStatement $_ })] [string]$Filter, [Parameter(ParameterSetName='/identity-protection/queries/devices/v1:get',Position=2)] [string]$Sort, [Parameter(ParameterSetName='/identity-protection/queries/devices/v1:get',Position=3)] [ValidateRange(1,200)] [int]$Limit, [Parameter(ParameterSetName='/identity-protection/queries/devices/v1:get')] [int]$Offset, [Parameter(ParameterSetName='/identity-protection/queries/devices/v1:get')] [switch]$Detailed, [Parameter(ParameterSetName='/identity-protection/queries/devices/v1:get')] [switch]$All, [Parameter(ParameterSetName='/identity-protection/queries/devices/v1:get')] [switch]$Total ) begin { $Param = @{ Command = $MyInvocation.MyCommand.Name; Endpoint = $PSCmdlet.ParameterSetName } [System.Collections.Generic.List[string]]$List = @() } process { if ($Id) { @($Id).foreach{ $List.Add($_) } } else { Invoke-Falcon @Param -UserInput $PSBoundParameters } } end { if ($List) { $Param['Max'] = 5000 $PSBoundParameters['Id'] = @($List | Select-Object -Unique) Invoke-Falcon @Param -UserInput $PSBoundParameters } } } |