functions/Get-XdrEndpointDeviceTimeline.ps1
|
function Get-XdrEndpointDeviceTimeline { <# .SYNOPSIS Retrieves the timeline of events for a specific device from Microsoft Defender XDR. .DESCRIPTION Gets the timeline of security events for a device from the Microsoft Defender XDR portal with options to filter by date range and other parameters. .PARAMETER DeviceId The unique identifier of the device. Accepts pipeline input and can also be specified as MachineId. Use this parameter set when identifying the device by ID. .PARAMETER MachineDnsName The DNS name of the machine. Use this parameter set when identifying the device by DNS name. .PARAMETER MarkedEventsOnly Only return events that have been marked in the timeline. .PARAMETER SenseClientVersion Optional. The version of the Sense client. .PARAMETER GenerateIdentityEvents Whether to generate identity events. Defaults to $true. .PARAMETER IncludeIdentityEvents Whether to include identity events. Defaults to $true. .PARAMETER SupportMdiOnlyEvents Whether to support MDI-only events. Defaults to $true. .PARAMETER FromDate The start date for the timeline. Defaults to 1 hour before current time. .PARAMETER ToDate The end date for the timeline. Defaults to current time. .PARAMETER LastNDays Specifies the number of days to look back. Overrides FromDate and ToDate if specified. .PARAMETER DoNotUseCache Whether to bypass the cache. Defaults to $false. .PARAMETER ForceUseCache Whether to force using the cache. Defaults to $false. .PARAMETER PageSize The number of events to return per page. Defaults to 200. .PARAMETER IncludeSentinelEvents Whether to include Sentinel events. Defaults to $false. .EXAMPLE Get-XdrEndpointDeviceTimeline -DeviceId "2bec169acc9def3ebd0bf8cdcbd9d16eb37e50e2" Retrieves the last hour of timeline events for the specified device. .EXAMPLE Get-XdrEndpointDeviceTimeline -MachineDnsName "computer.contoso.com" Retrieves the last hour of timeline events using the machine DNS name. .EXAMPLE Get-XdrEndpointDeviceTimeline -DeviceId "2bec169acc9def3ebd0bf8cdcbd9d16eb37e50e2" -FromDate (Get-Date).AddDays(-7) -ToDate (Get-Date) Retrieves timeline events for the last 7 days. .EXAMPLE "2bec169acc9def3ebd0bf8cdcbd9d16eb37e50e2" | Get-XdrEndpointDeviceTimeline Retrieves timeline events using pipeline input. #> [OutputType([System.Object[]])] [CmdletBinding(DefaultParameterSetName = 'ByDeviceId')] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'ByDeviceId')] [Alias('MachineId')] [string]$DeviceId, [Parameter(Mandatory, ParameterSetName = 'ByMachineDnsName')] [string]$MachineDnsName, [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [datetime]$FromDate = ((Get-Date).AddHours(-1)), [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [datetime]$ToDate = (Get-Date), [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [int]$LastNDays, [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [ValidateRange(1, 1000)] [int]$PageSize = 200, [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [switch]$MarkedEventsOnly, [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [string]$SenseClientVersion, [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [bool]$GenerateIdentityEvents = $true, [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [bool]$IncludeIdentityEvents = $true, [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [bool]$SupportMdiOnlyEvents = $true, [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [bool]$DoNotUseCache = $false, [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [bool]$ForceUseCache = $false, [Parameter(ParameterSetName = 'ByDeviceId')] [Parameter(ParameterSetName = 'ByMachineDnsName')] [bool]$IncludeSentinelEvents = $false ) begin { Update-XdrConnectionSettings } process { if ($PSBoundParameters.ContainsKey('LastNDays')) { $ToDate = Get-Date $FromDate = $ToDate.AddDays(-$LastNDays) } if (($ToDate - $FromDate).TotalDays -gt 30) { throw "The time range between FromDate and ToDate cannot exceed 30 days." } # Generate a new correlation ID $correlationId = [guid]::NewGuid().ToString() # Build the URI with query parameters $queryParams = @( "generateIdentityEvents=$($GenerateIdentityEvents.ToString().ToLower())" "includeIdentityEvents=$($IncludeIdentityEvents.ToString().ToLower())" "supportMdiOnlyEvents=$($SupportMdiOnlyEvents.ToString().ToLower())" "fromDate=$([System.Uri]::EscapeDataString($FromDate.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')))" "toDate=$([System.Uri]::EscapeDataString($ToDate.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')))" "correlationId=$correlationId" "doNotUseCache=$($DoNotUseCache.ToString().ToLower())" "forceUseCache=$($ForceUseCache.ToString().ToLower())" "pageSize=$PageSize" "includeSentinelEvents=$($IncludeSentinelEvents.ToString().ToLower())" ) if ($PSBoundParameters.ContainsKey('MachineDnsName')) { $queryParams = @("machineDnsName=$([System.Uri]::EscapeDataString($MachineDnsName))") + $queryParams } if ($PSBoundParameters.ContainsKey('SenseClientVersion')) { $queryParams = @("SenseClientVersion=$([System.Uri]::EscapeDataString($SenseClientVersion))") + $queryParams } if ($MarkedEventsOnly) { $queryParams = @("markedEventsOnly=true") + $queryParams } # Determine the device identifier to use in the URI $deviceIdentifier = if ($PSCmdlet.ParameterSetName -eq 'ByDeviceId') { $DeviceId } else { (Get-XdrEndpointDevice -MachineSearchPrefix $MachineDnsName).MachineId } try { $TimelineEvents = [System.Collections.Generic.List[object]]::new() $Uri = "https://security.microsoft.com/apiproxy/mtp/mdeTimelineExperience/machines/$deviceIdentifier/events/?$($queryParams -join '&')" do { # Parse Uri to extract fromDate, toDate, correlationId, and other parameters Write-Debug "URI: $Uri" $parsedUri = [System.Uri]::new($Uri) $query = [System.Web.HttpUtility]::ParseQueryString($parsedUri.Query) $fromDate = [datetime]$query["fromDate"] $toDate = [datetime]$query["toDate"] $correlationId = $query["correlationId"] Write-Verbose "Retrieving XDR Endpoint device timeline for device $deviceIdentifier (From: $fromDate, To: $toDate, CorrelationId: $correlationId)" # Try three times before giving up $attempt = 0 do { try { $attempt++ $response = Invoke-RestMethod -Uri $Uri -ContentType "application/json" -WebSession $script:session -Headers $script:headers break } catch { if ($attempt -lt 3) { Write-Warning "Attempt $($attempt + 1) failed. Retrying..." Start-Sleep -Seconds (Get-Random -Minimum 5 -Maximum 10) } else { throw "Failed to retrieve endpoint device timeline after 3 attempts." } } } while ($attempt -lt 3) if ($response -and $response.Items) { Write-Verbose "Retrieved $($response.Items.Count) timeline events for device $deviceIdentifier." $TimelineEvents.AddRange($response.Items) } else { Write-Verbose "No more timeline events found for device $deviceIdentifier." return $TimelineEvents } if ([string]::IsNullOrWhiteSpace($response.Prev)) { Write-Verbose "No more timeline events to retrieve for device $deviceIdentifier." return $TimelineEvents } else { Write-Debug "Previous page $($response.Prev)" $Uri = "https://security.microsoft.com/apiproxy/mtp/mdeTimelineExperience$($response.Prev)" } # Add a random delay between 3 and 10 seconds to avoid hitting rate limits $SleepTime = Get-Random -Minimum 3 -Maximum 10 Write-Debug "Sleeping for $SleepTime seconds to avoid rate limits." Start-Sleep -Seconds $SleepTime } while ($true) } catch { Write-Error "Failed to retrieve endpoint device timeline: $_" } } end { } } |