functions/Get-XdrVulnerabilityManagementCertificates.ps1
|
function Get-XdrVulnerabilityManagementCertificates { <# .SYNOPSIS Retrieves certificates from Vulnerability Management. .DESCRIPTION Gets certificate inventory data from TVM in Microsoft Defender XDR. This includes information about server certificates, machine certificates, certificate issues, and issuers. This function includes caching support with a 30-minute TTL to reduce API calls. .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 ServerCertificateIssues Retrieves server certificate issues. .PARAMETER MachineCertificateIssues Retrieves machine certificate issues. .PARAMETER PrivateCaCertificateIssues Retrieves private CA certificate issues. .PARAMETER ServerIssuers Retrieves server certificate issuers. .PARAMETER MachineIssuers Retrieves machine certificate issuers. .PARAMETER CountOnly Retrieves only the total count of certificates. .PARAMETER CountByExpirationDate Retrieves certificate count grouped by expiration date. .PARAMETER AggregatedMachineCertificates Retrieves aggregated machine certificate data from the aggregatedCertificate endpoint. .EXAMPLE Get-XdrVulnerabilityManagementCertificates Retrieves all certificates using cached data if available. .EXAMPLE Get-XdrVulnerabilityManagementCertificates -Force Forces a fresh retrieval of certificate inventory, bypassing the cache. .EXAMPLE Get-XdrVulnerabilityManagementCertificates -Top 10 Retrieves only the first 10 certificates. .EXAMPLE Get-XdrVulnerabilityManagementCertificates -ServerCertificateIssues Retrieves server certificate issues. .EXAMPLE Get-XdrVulnerabilityManagementCertificates -CountOnly Returns only the total count of certificates. .EXAMPLE Get-XdrVulnerabilityManagementCertificates -AggregatedMachineCertificates Retrieves aggregated machine certificate data. .OUTPUTS System.Object[] Returns an array of certificate 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 (issues, issuers, counts). #> # 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 = 'ServerCertificateIssues')] [switch]$ServerCertificateIssues, [Parameter(ParameterSetName = 'MachineCertificateIssues')] [switch]$MachineCertificateIssues, [Parameter(ParameterSetName = 'PrivateCaCertificateIssues')] [switch]$PrivateCaCertificateIssues, [Parameter(ParameterSetName = 'ServerIssuers')] [switch]$ServerIssuers, [Parameter(ParameterSetName = 'MachineIssuers')] [switch]$MachineIssuers, [Parameter(ParameterSetName = 'CountOnly')] [switch]$CountOnly, [Parameter(ParameterSetName = 'CountByExpirationDate')] [switch]$CountByExpirationDate, [Parameter(ParameterSetName = 'AggregatedMachineCertificates')] [switch]$AggregatedMachineCertificates ) 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) { 'ServerCertificateIssues' { @{ CacheKey = "XdrVulnerabilityManagementCertificates_ServerCertificateIssues" Simple = $true Endpoint = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/certificates/serverCertificateIssues" } } 'MachineCertificateIssues' { @{ CacheKey = "XdrVulnerabilityManagementCertificates_MachineCertificateIssues" Simple = $true Endpoint = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/certificates/machineCertificateIssues" } } 'PrivateCaCertificateIssues' { @{ CacheKey = "XdrVulnerabilityManagementCertificates_PrivateCaCertificateIssues" Simple = $true Endpoint = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/certificates/privateCaCertificateIssues" } } 'ServerIssuers' { @{ CacheKey = "XdrVulnerabilityManagementCertificates_ServerIssuers" Simple = $true Endpoint = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/certificates/serverIssuers" } } 'MachineIssuers' { @{ CacheKey = "XdrVulnerabilityManagementCertificates_MachineIssuers" Simple = $true Endpoint = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/certificates/machineIssuers" } } 'CountOnly' { @{ CacheKey = "XdrVulnerabilityManagementCertificates_Count" Simple = $true Endpoint = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/certificates/count" CountOnly = $true } } 'CountByExpirationDate' { @{ CacheKey = "XdrVulnerabilityManagementCertificates_CountByExpirationDate" Simple = $true Endpoint = "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/certificates/countByExpirationDate" } } 'AggregatedMachineCertificates' { @{ CacheKey = "XdrVulnerabilityManagementCertificates_AggregatedMachineCertificates" DisplayName = "aggregated machine certificates" BuildUri = { param($p) "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/aggregatedCertificate/machineCertificates?pageIndex=$p&pageSize=25" } } } default { @{ CacheKey = "XdrVulnerabilityManagementCertificates" DisplayName = "certificates" BuildUri = { param($p) "https://security.microsoft.com/apiproxy/mtp/tvm/analytics/certificates?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 certificates data" if ($config.Simple) { if ($config.CountOnly) { Write-Information "Total certificates: $($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 certificates 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 certificates: $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 certificates: $_" } } end { } } |