functions/Get-XdrVulnerabilityManagementAdvisories.ps1
|
function Get-XdrVulnerabilityManagementAdvisories { <# .SYNOPSIS Retrieves security advisories from Vulnerability Management. .DESCRIPTION Gets security advisories from Threat and Vulnerability Management (TVM) in Microsoft Defender XDR. This function includes caching support with a 30-minute TTL to reduce API calls. By default, returns all pages of results with pagination handled automatically. .PARAMETER Force Bypasses the cache and forces a fresh retrieval from the API. .PARAMETER CountOnly Returns only the total count of advisories (numOfResults). .PARAMETER Top Limits the number of results returned. Useful for previewing data without fetching all pages. .PARAMETER ByVendor Returns only the vendor-specific advisory counts (meta object). .EXAMPLE Get-XdrVulnerabilityManagementAdvisories Retrieves all security advisories with full pagination. .EXAMPLE Get-XdrVulnerabilityManagementAdvisories -Force Forces a fresh retrieval of security advisories, bypassing the cache. .EXAMPLE Get-XdrVulnerabilityManagementAdvisories -CountOnly Returns only the total number of advisories. .EXAMPLE Get-XdrVulnerabilityManagementAdvisories -Top 10 Returns only the first 10 advisories. .EXAMPLE Get-XdrVulnerabilityManagementAdvisories -ByVendor Returns only the vendor-specific advisory counts (Dell, HP, Lenovo). .OUTPUTS System.Object[] Returns an array of advisory objects when using default or -Top parameters. .OUTPUTS System.Int64 When -CountOnly is specified, returns the total count as an integer. .OUTPUTS System.Management.Automation.PSCustomObject When -ByVendor is specified, returns the vendor metadata object. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType([System.Object[]])] [OutputType([System.Int64], ParameterSetName = 'CountOnly')] [OutputType([System.Management.Automation.PSCustomObject], ParameterSetName = 'ByVendor')] param ( [Parameter()] [switch]$Force, [Parameter(ParameterSetName = 'CountOnly')] [switch]$CountOnly, [Parameter()] [ValidateRange(1, 10000)] [int]$Top, [Parameter(ParameterSetName = 'ByVendor')] [switch]$ByVendor ) begin { Update-XdrConnectionSettings # Helper function for paginated requests function Invoke-PaginatedRequest { param ( [string]$BaseUri, [hashtable]$Headers, [string]$DisplayName, [int]$MaxResults = 0 ) $maxPages = 1000 $pageNum = 1 try { # Get first page $uri = "$BaseUri`?pageIndex=$pageNum" Write-Verbose "Fetching $DisplayName page 1" $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json" -WebSession $script:session -Headers $Headers $totalResults = $response.numOfResults $metaData = $response.meta $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 25 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 = "$BaseUri`?pageIndex=$pageNum" Write-Verbose "Fetching $DisplayName page $pageNum" $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json" -WebSession $script:session -Headers $Headers 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 Meta = $metaData Results = $finalResults } } catch { Write-Progress -Activity "Retrieving $DisplayName" -Completed throw "Failed to retrieve $DisplayName`: $_" } } } process { $cacheKey = "XdrVulnerabilityManagementAdvisories" $baseUri = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/advisories" # Check cache first (skip if -Top is specified as we may need subset) $useCache = -not $Top -or $Top -eq 0 $currentCacheValue = Get-XdrCache -CacheKey $cacheKey -ErrorAction SilentlyContinue if ($useCache -and -not $Force -and $currentCacheValue.NotValidAfter -gt (Get-Date)) { Write-Verbose "Using cached TVM advisories" Write-Information "Total advisories: $($currentCacheValue.Value.numOfResults)" -InformationAction Continue if ($ByVendor) { Write-Information "Advisory counts by vendor:" -InformationAction Continue if ($currentCacheValue.Value.meta) { $currentCacheValue.Value.meta.PSObject.Properties | Sort-Object Name | ForEach-Object { Write-Information ("{0,-30} {1}" -f $_.Name, $_.Value) -InformationAction Continue } } return $currentCacheValue.Value.meta } if ($CountOnly) { return $currentCacheValue.Value.numOfResults } return $currentCacheValue.Value.results } if ($Force) { Write-Verbose "Force parameter specified, bypassing cache" Clear-XdrCache -CacheKey $cacheKey } elseif (-not $useCache) { Write-Verbose "Top parameter specified, fetching fresh data" } else { Write-Verbose "TVM advisories cache is missing or expired" } # Prepare headers $tvmHeaders = $script:headers.Clone() $tvmHeaders["api-version"] = "1.0" try { $paginatedResult = Invoke-PaginatedRequest -BaseUri $baseUri -Headers $tvmHeaders -DisplayName "advisories" -MaxResults $Top # Display vendor info if requested if ($ByVendor) { Write-Information "Advisory counts by vendor:" -InformationAction Continue if ($paginatedResult.Meta) { $paginatedResult.Meta.PSObject.Properties | Sort-Object Name | ForEach-Object { Write-Information ("{0,-30} {1}" -f $_.Name, $_.Value) -InformationAction Continue } } } # Cache the full response only if we fetched everything if (-not $Top -or $Top -eq 0) { $cacheValue = [PSCustomObject]@{ numOfResults = $paginatedResult.Count meta = $paginatedResult.Meta results = $paginatedResult.Results } Set-XdrCache -CacheKey $cacheKey -Value $cacheValue -TTLMinutes 30 } # Return based on parameters if ($ByVendor) { # Return empty object if meta is null if ($null -eq $paginatedResult.Meta) { return [PSCustomObject]@{} } return $paginatedResult.Meta } if ($CountOnly) { return [int64]($(if ($null -ne $paginatedResult.Count) { $paginatedResult.Count } else { 0 })) } # 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 advisories: $_" } } end { } } |