functions/Get-XdrVulnerabilityManagementExtensions.ps1
|
function Get-XdrVulnerabilityManagementExtensions { <# .SYNOPSIS Retrieves browser extensions from Vulnerability Management. .DESCRIPTION Gets browser extension inventory data from TVM in Microsoft Defender XDR. This function includes caching support with a 30-minute TTL to reduce API calls. Supports various sub-endpoints for detailed extension information. .PARAMETER Force Bypasses the cache and forces a fresh retrieval from the API. .PARAMETER Top Limits the number of results returned for paginated endpoints. Useful for previewing data. .PARAMETER Summary Retrieves the extensions summary endpoint. .PARAMETER Installations Retrieves extension installation details. Requires -ExtensionId and -TargetSoftware. .PARAMETER InstallationsAggregate Retrieves aggregated extension installation data. Requires -ExtensionId and -TargetSoftware. .PARAMETER Users Retrieves users with the extension installed. Requires -ExtensionId and -TargetSoftware. .PARAMETER CountOnly Retrieves only the total count of extensions. .PARAMETER ExtensionId The extension ID required for -Installations, -InstallationsAggregate, and -Users parameters. .PARAMETER TargetSoftware The target software (e.g., 'edge', 'chrome') required for -Installations, -InstallationsAggregate, and -Users parameters. .EXAMPLE Get-XdrVulnerabilityManagementExtensions Retrieves all browser extensions using cached data if available. .EXAMPLE Get-XdrVulnerabilityManagementExtensions -Force Forces a fresh retrieval of browser extensions, bypassing the cache. .EXAMPLE Get-XdrVulnerabilityManagementExtensions -Top 10 Retrieves only the first 10 browser extensions. .EXAMPLE Get-XdrVulnerabilityManagementExtensions -Summary Retrieves the extensions summary data. .EXAMPLE Get-XdrVulnerabilityManagementExtensions -Installations -ExtensionId "ggjhpefgjjfobnfoldnjipclpcfbgbhl" -TargetSoftware "edge" Retrieves installation details for a specific extension. .EXAMPLE Get-XdrVulnerabilityManagementExtensions -CountOnly Returns only the total count of extensions. .OUTPUTS System.Object[] Returns an array of extension objects for paginated endpoints. .OUTPUTS System.Int64 When -CountOnly is specified, returns the total count as an integer. .OUTPUTS System.Management.Automation.PSCustomObject Returns a single object for non-paginated endpoints (summary, aggregate). #> # Suppress false positive: Switch parameters are used via $PSCmdlet.ParameterSetName, not direct reference [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType([System.Object[]])] [OutputType([System.Int64], ParameterSetName = 'CountOnly')] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter()] [switch]$Force, [Parameter()] [ValidateRange(1, 10000)] [int]$Top, [Parameter(ParameterSetName = 'Summary')] [switch]$Summary, [Parameter(ParameterSetName = 'Installations')] [switch]$Installations, [Parameter(ParameterSetName = 'InstallationsAggregate')] [switch]$InstallationsAggregate, [Parameter(ParameterSetName = 'Users')] [switch]$Users, [Parameter(ParameterSetName = 'CountOnly')] [switch]$CountOnly, [Parameter(ParameterSetName = 'Installations', Mandatory = $true)] [Parameter(ParameterSetName = 'InstallationsAggregate', Mandatory = $true)] [Parameter(ParameterSetName = 'Users', Mandatory = $true)] [string]$ExtensionId, [Parameter(ParameterSetName = 'Installations', Mandatory = $true)] [Parameter(ParameterSetName = 'InstallationsAggregate', Mandatory = $true)] [Parameter(ParameterSetName = 'Users', Mandatory = $true)] [string]$TargetSoftware ) begin { Update-XdrConnectionSettings # Prepare TVM headers $tvmHeaders = $script:headers.Clone() $tvmHeaders["api-version"] = "1.0" # Helper function for paginated requests function Invoke-PaginatedRequest { param ( [scriptblock]$BuildUri, [string]$DisplayName, [string]$CountProperty = 'numOfResults', [int]$MaxResults = 0, [int]$PageSize = 25 ) $maxPages = 1000 $pageNum = 1 try { # Get first page $uri = & $BuildUri $pageNum Write-Verbose "Fetching $DisplayName page 1" $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json" -WebSession $script:session -Headers $tvmHeaders $totalResults = $response.$CountProperty if ($null -eq $totalResults) { $totalResults = 0 } $targetResults = if ($MaxResults -gt 0 -and $MaxResults -lt $totalResults) { $MaxResults } else { $totalResults } Write-Information "Total $DisplayName`: $totalResults$(if ($MaxResults -gt 0 -and $MaxResults -lt $totalResults) { " (fetching $targetResults)" })" -InformationAction Continue # Collect results with pagination $allResults = [System.Collections.Generic.List[object]]::new() if ($response.results) { $allResults.AddRange($response.results) } $pageNum = 2 # Show progress for larger result sets (more than one page) $showProgress = $targetResults -gt $PageSize while ($allResults.Count -lt $targetResults -and $pageNum -le $maxPages) { if ($showProgress) { $percentComplete = [math]::Min(100, [math]::Round(($allResults.Count / $targetResults) * 100)) Write-Progress -Activity "Retrieving $DisplayName" -Status "$($allResults.Count) of $targetResults" -PercentComplete $percentComplete } $uri = & $BuildUri $pageNum Write-Verbose "Fetching $DisplayName page $pageNum" $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json" -WebSession $script:session -Headers $tvmHeaders if ($response.results) { $allResults.AddRange($response.results) } Write-Verbose "Retrieved $($allResults.Count) of $targetResults $DisplayName" $pageNum++ } if ($showProgress) { Write-Progress -Activity "Retrieving $DisplayName" -Completed } # Trim to MaxResults if specified $finalResults = if ($MaxResults -gt 0 -and $allResults.Count -gt $MaxResults) { $allResults.GetRange(0, $MaxResults).ToArray() } else { $allResults.ToArray() } return [PSCustomObject]@{ Count = $totalResults Results = $finalResults } } catch { Write-Progress -Activity "Retrieving $DisplayName" -Completed throw "Failed to retrieve $DisplayName`: $_" } } } process { # Define endpoint configuration based on parameter set $config = switch ($PSCmdlet.ParameterSetName) { 'Summary' { @{ CacheKey = "XdrVulnerabilityManagementExtensions_Summary" Simple = $true Endpoint = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/extensions/summary" } } 'Installations' { @{ CacheKey = "XdrVulnerabilityManagementExtensions_Installations_$($ExtensionId)_$($TargetSoftware)" DisplayName = "extension installations" BuildUri = { param($p) "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/extensions/installations/?pageIndex=$p&pageSize=25&`$orderby=assetCriticalityLevel%20desc&extensionId=$ExtensionId&targetSoftware=$TargetSoftware" }.GetNewClosure() } } 'InstallationsAggregate' { @{ CacheKey = "XdrVulnerabilityManagementExtensions_InstallationsAggregate_$($ExtensionId)_$($TargetSoftware)" Simple = $true Endpoint = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/extensions/installations/aggregate?extensionId=$ExtensionId&targetSoftware=$TargetSoftware" } } 'Users' { @{ CacheKey = "XdrVulnerabilityManagementExtensions_Users_$($ExtensionId)_$($TargetSoftware)" DisplayName = "extension users" BuildUri = { param($p) "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/extensions/users?pageIndex=$p&pageSize=25&targetSoftware=$TargetSoftware&extensionId=$ExtensionId" }.GetNewClosure() } } 'CountOnly' { @{ CacheKey = "XdrVulnerabilityManagementExtensions_Count" Simple = $true Endpoint = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/extensions/count" CountOnly = $true } } default { @{ CacheKey = "XdrVulnerabilityManagementExtensions" DisplayName = "extensions" BuildUri = { param($p) "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/extensions?pageIndex=$p&pageSize=25" } } } } # Check cache first (skip if -Top is specified as we may need subset) $useCache = -not $Top -or $Top -eq 0 $currentCacheValue = Get-XdrCache -CacheKey $config.CacheKey -ErrorAction SilentlyContinue if ($useCache -and -not $Force -and $currentCacheValue.NotValidAfter -gt (Get-Date)) { Write-Verbose "Using cached TVM extensions data" if ($config.Simple) { if ($config.CountOnly) { Write-Information "Total extensions: $($currentCacheValue.Value)" -InformationAction Continue } return $currentCacheValue.Value } Write-Information "Total $($config.DisplayName): $($currentCacheValue.Value.numOfResults)" -InformationAction Continue return $currentCacheValue.Value.results } if ($Force) { Write-Verbose "Force parameter specified, bypassing cache" Clear-XdrCache -CacheKey $config.CacheKey } elseif (-not $useCache) { Write-Verbose "Top parameter specified, fetching fresh data" } else { Write-Verbose "TVM extensions cache is missing or expired" } try { # Handle simple (non-paginated) endpoints if ($config.Simple) { Write-Verbose "Retrieving from: $($config.Endpoint)" $result = Invoke-RestMethod -Uri $config.Endpoint -Method Get -ContentType "application/json" -WebSession $script:session -Headers $tvmHeaders # Handle CountOnly - extract just the count value if ($config.CountOnly) { $countValue = if ($result.count) { $result.count } else { $result } Write-Information "Total extensions: $countValue" -InformationAction Continue Set-XdrCache -CacheKey $config.CacheKey -Value $countValue -TTLMinutes 30 return $countValue } # Handle null/empty results gracefully if ($null -eq $result) { $result = @{} } Set-XdrCache -CacheKey $config.CacheKey -Value $result -TTLMinutes 30 return $result } # Handle paginated endpoints $paginatedResult = Invoke-PaginatedRequest -DisplayName $config.DisplayName -BuildUri $config.BuildUri -MaxResults $Top # Cache the full response only if we fetched everything if (-not $Top -or $Top -eq 0) { $cacheValue = [PSCustomObject]@{ numOfResults = $(if ($null -ne $paginatedResult.Count) { $paginatedResult.Count } else { 0 }) results = $(if ($null -ne $paginatedResult.Results) { $paginatedResult.Results } else { @() }) } Set-XdrCache -CacheKey $config.CacheKey -Value $cacheValue -TTLMinutes 30 } # Return empty array if results are null if ($null -eq $paginatedResult.Results -or $paginatedResult.Results.Count -eq 0) { return } return $paginatedResult.Results } catch { Write-Error "Failed to retrieve TVM extensions: $_" } } end { } } |