public/discover.ps1

function Get-FalconAsset {
<#
.SYNOPSIS
Search for assets in Falcon Discover
.DESCRIPTION
Requires 'Falcon Discover: Read' and 'Falcon Discover IoT: Read'.
.PARAMETER Id
Asset identifier
.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 Include
Include additional properties
.PARAMETER Offset
Position to begin retrieving results
.PARAMETER After
Pagination token to retrieve the next set of 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
.PARAMETER Account
Search for user account assets
.PARAMETER Application
Search for applications
.PARAMETER External
Search for external assets
.PARAMETER IoT
Search for IoT assets
.PARAMETER Login
Search for login events
.LINK
https://github.com/crowdstrike/psfalcon/wiki/Get-FalconAsset
#>

  [CmdletBinding(DefaultParameterSetName='/discover/queries/hosts/v1:get',SupportsShouldProcess)]
  param(
    [Parameter(ParameterSetName='/discover/entities/accounts/v1:get',Mandatory,ValueFromPipelineByPropertyName,
      ValueFromPipeline)]
    [Parameter(ParameterSetName='/discover/entities/applications/v1:get',Mandatory,
      ValueFromPipelineByPropertyName,ValueFromPipeline)]
    [Parameter(ParameterSetName='/discover/entities/hosts/v1:get',Mandatory,ValueFromPipelineByPropertyName,
      ValueFromPipeline)]
    [Parameter(ParameterSetName='/discover/entities/iot-hosts/v1:get',Mandatory,
      ValueFromPipelineByPropertyName,ValueFromPipeline)]
    [Parameter(ParameterSetName='/discover/entities/logins/v1:get',Mandatory,ValueFromPipelineByPropertyName,
      ValueFromPipeline)]
    [Parameter(ParameterSetName='/fem/entities/external-assets/v1:get',Mandatory,ValueFromPipelineByPropertyName,
      ValueFromPipeline)]
    [Alias('ids')]
    [string[]]$Id,
    [Parameter(ParameterSetName='/discover/combined/applications/v1:get',Position=1)]
    [Parameter(ParameterSetName='/discover/combined/hosts/v1:get',Position=1)]
    [Parameter(ParameterSetName='/discover/queries/accounts/v1:get',Position=1)]
    [Parameter(ParameterSetName='/discover/queries/applications/v1:get',Position=1)]
    [Parameter(ParameterSetName='/discover/queries/hosts/v1:get',Position=1)]
    [Parameter(ParameterSetName='/discover/queries/iot-hosts/v2:get',Position=1)]
    [Parameter(ParameterSetName='/discover/queries/logins/v1:get',Position=1)]
    [Parameter(ParameterSetName='/fem/queries/external-assets/v1:get',Position=1)]
    [ValidateScript({ Test-FqlStatement $_ })]
    [string]$Filter,
    [Parameter(ParameterSetName='/discover/combined/applications/v1:get',Position=2)]
    [Parameter(ParameterSetName='/discover/combined/hosts/v1:get',Position=2)]
    [Parameter(ParameterSetName='/discover/queries/accounts/v1:get',Position=2)]
    [Parameter(ParameterSetName='/discover/queries/applications/v1:get',Position=2)]
    [Parameter(ParameterSetName='/discover/queries/hosts/v1:get',Position=2)]
    [Parameter(ParameterSetName='/discover/queries/iot-hosts/v2:get',Position=2)]
    [Parameter(ParameterSetName='/discover/queries/logins/v1:get',Position=2)]
    [Parameter(ParameterSetName='/fem/queries/external-assets/v1:get',Position=2)]
    [string]$Sort,
    [Parameter(ParameterSetName='/discover/combined/applications/v1:get',Position=3)]
    [Parameter(ParameterSetName='/discover/combined/hosts/v1:get',Position=3)]
    [Parameter(ParameterSetName='/discover/queries/accounts/v1:get',Position=3)]
    [Parameter(ParameterSetName='/discover/queries/applications/v1:get',Position=3)]
    [Parameter(ParameterSetName='/discover/queries/hosts/v1:get',Position=3)]
    [Parameter(ParameterSetName='/discover/queries/iot-hosts/v2:get',Position=3)]
    [Parameter(ParameterSetName='/discover/queries/logins/v1:get',Position=3)]
    [Parameter(ParameterSetName='/fem/queries/external-assets/v1:get',Position=3)]
    [int32]$Limit,
    [Parameter(ParameterSetName='/discover/combined/applications/v1:get',Position=4)]
    [Parameter(ParameterSetName='/discover/combined/hosts/v1:get',Position=4)]
    [Parameter(ParameterSetName='/discover/queries/hosts/v1:get',Position=4)]
    [Parameter(ParameterSetName='/discover/queries/accounts/v1:get',Position=4)]
    [ValidateSet('login_event','browser_extension','host_info','install_usage','system_insights','third_party',
      'risk_factors',IgnoreCase=$false)]
    [string[]]$Include,
    [Parameter(ParameterSetName='/discover/queries/hosts/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/accounts/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/applications/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/logins/v1:get')]
    [Parameter(ParameterSetName='/fem/queries/external-assets/v1:get')]
    [int32]$Offset,
    [Parameter(ParameterSetName='/discover/queries/iot-hosts/v2:get')]
    [Parameter(ParameterSetName='/discover/combined/applications/v1:get')]
    [Parameter(ParameterSetName='/discover/combined/hosts/v1:get')]
    [string]$After,
    [Parameter(ParameterSetName='/discover/combined/applications/v1:get',Mandatory)]
    [Parameter(ParameterSetName='/discover/combined/hosts/v1:get',Mandatory)]
    [Parameter(ParameterSetName='/discover/queries/accounts/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/iot-hosts/v2:get')]
    [Parameter(ParameterSetName='/discover/queries/logins/v1:get')]
    [Parameter(ParameterSetName='/fem/queries/external-assets/v1:get')]
    [switch]$Detailed,
    [Parameter(ParameterSetName='/discover/combined/applications/v1:get')]
    [Parameter(ParameterSetName='/discover/combined/hosts/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/accounts/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/applications/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/hosts/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/iot-hosts/v2:get')]
    [Parameter(ParameterSetName='/discover/queries/logins/v1:get')]
    [Parameter(ParameterSetName='/fem/queries/external-assets/v1:get')]
    [switch]$All,
    [Parameter(ParameterSetName='/discover/queries/accounts/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/applications/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/hosts/v1:get')]
    [Parameter(ParameterSetName='/discover/queries/iot-hosts/v2:get')]
    [Parameter(ParameterSetName='/discover/queries/logins/v1:get')]
    [Parameter(ParameterSetName='/fem/queries/external-assets/v1:get')]
    [switch]$Total,
    [Parameter(ParameterSetName='/discover/entities/accounts/v1:get',Mandatory)]
    [Parameter(ParameterSetName='/discover/queries/accounts/v1:get',Mandatory)]
    [switch]$Account,
    [Parameter(ParameterSetName='/discover/combined/applications/v1:get',Mandatory)]
    [Parameter(ParameterSetName='/discover/entities/applications/v1:get',Mandatory)]
    [Parameter(ParameterSetName='/discover/queries/applications/v1:get',Mandatory)]
    [switch]$Application,
    [Parameter(ParameterSetName='/fem/entities/external-assets/v1:get',Mandatory)]
    [Parameter(ParameterSetName='/fem/queries/external-assets/v1:get',Mandatory)]
    [switch]$External,
    [Parameter(ParameterSetName='/discover/entities/iot-hosts/v1:get',Mandatory)]
    [Parameter(ParameterSetName='/discover/queries/iot-hosts/v2:get',Mandatory)]
    [switch]$IoT,
    [Parameter(ParameterSetName='/discover/entities/logins/v1:get',Mandatory)]
    [Parameter(ParameterSetName='/discover/queries/logins/v1:get',Mandatory)]
    [switch]$Login
  )
  begin {
    $Param = @{ Command = $MyInvocation.MyCommand.Name; Endpoint = $PSCmdlet.ParameterSetName }
    [System.Collections.Generic.List[string]]$List = @()
    $RegEx = @{
      CombinedUrl = '/discover/combined/(applications|hosts)/v1:get'
      AppFacet = '^(browser_extension|host_info|install_usage)$'
      HostFacet = '^(login_event|system_insights|third_party|risk_factors)$'
    }
    if ($PSBoundParameters.All -and !$PSBoundParameters.Limit) {
      # Add appropriate 'Limit' when using 'All'
      $PSBoundParameters['Limit'] = if ($PSCmdlet.ParameterSetName -match $RegEx.CombinedUrl) { 1000 } else { 100 }
    }
  }
  process { if ($Id) { @($Id).foreach{ $List.Add($_) }}}
  end {
    if ($List) {
      # Submit 'id' values to /entities/ API in groups of 100
      $PSBoundParameters['Id'] = @($List)
      $Param['Max'] = 100
    } else {
      # Error when 'Limit' exceeds 100 or 1,000 for relevant API
      $MaxLimit = if ($PSCmdlet.ParameterSetName -match $RegEx.CombinedUrl) { 1000 } else { 100 }
      if ($PSBoundParameters.Limit -and $PSBoundParameters.Limit -gt $MaxLimit) {
        $Message = (('Cannot validate argument on parameter {0}. The {1} argument is greater than the maximum al' +
          'lowed range of {2}. Supply an argument that is less than or equal to {2} and then try the command aga' +
          'in.') -f "'Limit'",$PSBoundParameters.Limit,$MaxLimit)
        throw $Message
      }
      if ($PSBoundParameters.Include) {
        # Error if individual 'Include' value is not valid for target API
        [string[]]$Valid = if ($PSCmdlet.ParameterSetName -eq '/discover/combined/applications/v1:get') {
          $RegEx.AppFacet -replace '[\^\(\)\$]',$null -split '\|'
        } elseif ($PSCmdlet.ParameterSetName -eq '/discover/combined/hosts/v1:get') {
          $RegEx.HostFacet -replace '[\^\(\)\$]',$null -split '\|'
        } elseif ($PSCmdlet.ParameterSetName -match '/queries/') {
          'login_event'
        }
        [string[]]$Facet = @($PSBoundParameters.Include).foreach{
          $Message = (('Cannot validate argument on parameter {0}. The argument "{1}" does not belong to the set' +
            ' "{2}" specified by the ValidateSet attribute. Supply an argument that is in the set and then try t' +
            'he command again.') -f "'Include'",$_,($Valid -join ','))
          if ($Valid -notcontains $_) { throw $Message } elseif ($_ -ne 'login_event') { $_ }
        }
        if ($Facet) {
          # Move 'Include' values to 'facet' for /combined/ API(s)
          $PSBoundParameters['facet'] = $Facet
          $PSBoundParameters.Include = @($PSBoundParameters.Include).Where({$Facet -notcontains $_})
          if (!$PSBoundParameters.Include) { [void]$PSBoundParameters.Remove('Include') }
        }
      }
    }
    $Request = if ($Include -contains 'login_event' -and !$PSBoundParameters.Detailed -and !$Account) {
      $PSBoundParameters['Detailed'] = $true
      Invoke-Falcon @Param -UserInput $PSBoundParameters | Select-Object id,aid
    } elseif ($Detailed -and ($Account -or $External -or $IoT -or $Login)) {
      # Re-submit 'id' values to appropriate /entities/ API to retrieve detail
      [void]$PSBoundParameters.Remove('Detailed')
      $IdList = Invoke-Falcon @Param -UserInput $PSBoundParameters
      if ($IdList -and $Account) {
        $IdList | & $MyInvocation.MyCommand.Name -Account
      } elseif ($IdList -and $External) {
        $IdList | & $MyInvocation.MyCommand.Name -External
      } elseif ($IdList -and $IoT) {
        $IdList | & $MyInvocation.MyCommand.Name -IoT
      } elseif ($IdList -and $Login) {
        $IdList | & $MyInvocation.MyCommand.Name -Login
      }
    } else {
      Invoke-Falcon @Param -UserInput $PSBoundParameters
    }
    if ($Request -and $PSBoundParameters.Include -and $PSBoundParameters.Include -contains 'login_event') {
      # Match 'login_event' results with $ReqId
      if (!$Request.id) { $Request = @($Request).foreach{ ,[PSCustomObject]@{ id = $_ }}}
      [string]$ReqId = if ($PSCmdlet.ParameterSetName -match 'hosts') { 'aid' } else { 'id' }
      [string]$Property = if ($ReqId -eq 'aid') { 'aid' } else { 'account_id' }
      for ($i=0; $i -lt ($Request | Measure-Object).Count; $i+=100) {
        # In groups of 100, perform filtered search for login events
        [string[]]$Group = @(@($Request)[$i..($i+99)]).Where({![string]::IsNullOrEmpty($_.$ReqId)}).$ReqId
        Write-Host ($Group -join ',')
        [string[]]$Filter = @($Group).foreach{ $Property,"'$_'" -join ':' }
        $Content = & $MyInvocation.MyCommand.Name -Filter ($Filter -join ',') -Login -Detailed -All -EA 0
        foreach ($Value in $Group) {
          @($Request).Where({$_.$ReqId -eq $Value}).foreach{
            # Append matched login events using 'aid' or 'account_id'
            Set-Property $_ login_event @($Content).Where({$_.$Property -eq $Value})
          }
        }
      }
    }
    $Request
  }
}