Merge pull request #546 from bwuch/bwuch-SkylineInsights
Timeout upates and bugfixes.
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Configuration>
|
||||||
|
<ViewDefinitions>
|
||||||
|
<View>
|
||||||
|
<Name>SkylineConnection</Name>
|
||||||
|
<ViewSelectedBy>
|
||||||
|
<TypeName>SkylineConnection</TypeName>
|
||||||
|
</ViewSelectedBy>
|
||||||
|
<TableControl>
|
||||||
|
<TableHeaders>
|
||||||
|
<TableColumnHeader>
|
||||||
|
<Width>30</Width>
|
||||||
|
<Label>Name</Label>
|
||||||
|
</TableColumnHeader>
|
||||||
|
<TableColumnHeader>
|
||||||
|
<Width>30</Width>
|
||||||
|
<Label>APIKey</Label>
|
||||||
|
</TableColumnHeader>
|
||||||
|
<TableColumnHeader>
|
||||||
|
<Label>CSPName</Label>
|
||||||
|
</TableColumnHeader>
|
||||||
|
</TableHeaders>
|
||||||
|
<TableRowEntries>
|
||||||
|
<TableRowEntry>
|
||||||
|
<TableColumnItems>
|
||||||
|
<TableColumnItem>
|
||||||
|
<PropertyName>Name</PropertyName>
|
||||||
|
</TableColumnItem>
|
||||||
|
<TableColumnItem>
|
||||||
|
<PropertyName>APIKey</PropertyName>
|
||||||
|
</TableColumnItem>
|
||||||
|
<TableColumnItem>
|
||||||
|
<PropertyName>CSPName</PropertyName>
|
||||||
|
</TableColumnItem>
|
||||||
|
</TableColumnItems>
|
||||||
|
</TableRowEntry>
|
||||||
|
</TableRowEntries>
|
||||||
|
</TableControl>
|
||||||
|
</View>
|
||||||
|
</ViewDefinitions>
|
||||||
|
</Configuration>
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
<#
|
||||||
|
Copyright 2021 VMware, Inc.
|
||||||
|
SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
#>
|
||||||
|
|
||||||
|
#
|
||||||
|
# Module manifest for module 'VMware.Skyline.InsightsApi'
|
||||||
|
#
|
||||||
|
# Generated by: Brian Wuchner
|
||||||
|
#
|
||||||
|
# Generated on: 2/21/2022
|
||||||
|
#
|
||||||
|
|
||||||
|
@{
|
||||||
|
|
||||||
|
# Script module or binary module file associated with this manifest.
|
||||||
|
RootModule = 'VMware.Skyline.InsightsApi.psm1'
|
||||||
|
|
||||||
|
# Version number of this module.
|
||||||
|
ModuleVersion = '1.0.0'
|
||||||
|
|
||||||
|
# Supported PSEditions
|
||||||
|
# CompatiblePSEditions = @()
|
||||||
|
|
||||||
|
# ID used to uniquely identify this module
|
||||||
|
GUID = '4dfcb1e5-69b9-405d-aecd-06119ec12649'
|
||||||
|
|
||||||
|
# Author of this module
|
||||||
|
Author = 'Brian Wuchner'
|
||||||
|
|
||||||
|
# Company or vendor of this module
|
||||||
|
CompanyName = 'VMware'
|
||||||
|
|
||||||
|
# Copyright statement for this module
|
||||||
|
Copyright = '(c) VMware. All rights reserved.'
|
||||||
|
|
||||||
|
# Description of the functionality provided by this module
|
||||||
|
Description = 'Community sourced PowerShell wrapper module for the Skyline Insights API.'
|
||||||
|
|
||||||
|
# Minimum version of the Windows PowerShell engine required by this module
|
||||||
|
PowerShellVersion = '4.0'
|
||||||
|
|
||||||
|
# Name of the Windows PowerShell host required by this module
|
||||||
|
# PowerShellHostName = ''
|
||||||
|
|
||||||
|
# Minimum version of the Windows PowerShell host required by this module
|
||||||
|
# PowerShellHostVersion = ''
|
||||||
|
|
||||||
|
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||||
|
# DotNetFrameworkVersion = ''
|
||||||
|
|
||||||
|
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||||
|
# CLRVersion = ''
|
||||||
|
|
||||||
|
# Processor architecture (None, X86, Amd64) required by this module
|
||||||
|
# ProcessorArchitecture = ''
|
||||||
|
|
||||||
|
# Modules that must be imported into the global environment prior to importing this module
|
||||||
|
# RequiredModules = @()
|
||||||
|
|
||||||
|
# Assemblies that must be loaded prior to importing this module
|
||||||
|
# RequiredAssemblies = @()
|
||||||
|
|
||||||
|
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||||
|
# ScriptsToProcess = @()
|
||||||
|
|
||||||
|
# Type files (.ps1xml) to be loaded when importing this module
|
||||||
|
# TypesToProcess = @()
|
||||||
|
|
||||||
|
# Format files (.ps1xml) to be loaded when importing this module
|
||||||
|
FormatsToProcess = @('VMware.Skyline.InsightsApi.Format.ps1xml')
|
||||||
|
|
||||||
|
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
|
||||||
|
# NestedModules = @()
|
||||||
|
|
||||||
|
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
|
||||||
|
FunctionsToExport = @('Connect-SkylineInsights','Disconnect-SkylineInsights','Invoke-SkylineInsightsApi','Get-SkylineFinding',
|
||||||
|
'Get-SkylineAffectedObject','Format-SkylineResult','Start-SkylineInsightsApiExplorer')
|
||||||
|
|
||||||
|
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
|
||||||
|
CmdletsToExport = @()
|
||||||
|
|
||||||
|
# Variables to export from this module
|
||||||
|
VariablesToExport = '*'
|
||||||
|
|
||||||
|
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
|
||||||
|
AliasesToExport = @()
|
||||||
|
|
||||||
|
# DSC resources to export from this module
|
||||||
|
# DscResourcesToExport = @()
|
||||||
|
|
||||||
|
# List of all modules packaged with this module
|
||||||
|
# ModuleList = @()
|
||||||
|
|
||||||
|
# List of all files packaged with this module
|
||||||
|
# FileList = @()
|
||||||
|
|
||||||
|
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
|
||||||
|
PrivateData = @{
|
||||||
|
|
||||||
|
PSData = @{
|
||||||
|
|
||||||
|
# Tags applied to this module. These help with module discovery in online galleries.
|
||||||
|
# Tags = @()
|
||||||
|
|
||||||
|
# A URL to the license for this module.
|
||||||
|
# LicenseUri = ''
|
||||||
|
|
||||||
|
# A URL to the main website for this project.
|
||||||
|
# ProjectUri = ''
|
||||||
|
|
||||||
|
# A URL to an icon representing this module.
|
||||||
|
# IconUri = ''
|
||||||
|
|
||||||
|
# ReleaseNotes of this module
|
||||||
|
# ReleaseNotes = ''
|
||||||
|
|
||||||
|
} # End of PSData hashtable
|
||||||
|
|
||||||
|
} # End of PrivateData hashtable
|
||||||
|
|
||||||
|
# HelpInfo URI of this module
|
||||||
|
# HelpInfoURI = ''
|
||||||
|
|
||||||
|
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
|
||||||
|
# DefaultCommandPrefix = ''
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,425 @@
|
|||||||
|
<#
|
||||||
|
Copyright 2021 VMware, Inc.
|
||||||
|
SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
#>
|
||||||
|
|
||||||
|
Function Connect-SkylineInsights {
|
||||||
|
<#
|
||||||
|
.NOTES
|
||||||
|
===========================================================================
|
||||||
|
Created by: Brian Wuchner
|
||||||
|
Date: February 21, 2022
|
||||||
|
Blog: www.enterpriseadmins.org
|
||||||
|
Twitter: @bwuch
|
||||||
|
===========================================================================
|
||||||
|
.SYNOPSIS
|
||||||
|
Use this function to create the auth header to connect to Skyline Insights API
|
||||||
|
.DESCRIPTION
|
||||||
|
This function will allow you to connect to a Skyline Insights API.
|
||||||
|
A global variable will be set with the Servername & Header value for use by other functions.
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Connect-SkylineInsights -apiKey 'my-key-from-csp'
|
||||||
|
This will use the provided API key to create a connection to Skyline Insights.
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Connect-SkylineInsights -apiKey 'my-key-from-csp' -SaveCredentials
|
||||||
|
This will use the PowerCLI VICredentialStore Item to save the provided API key. On next use this key will be provided automatically.
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[string]$apiKey,
|
||||||
|
[switch]$SaveCredentials,
|
||||||
|
[Parameter(DontShow)]$cspApi = 'console.cloud.vmware.com',
|
||||||
|
[Parameter(DontShow)]$skylineApi = 'skyline.vmware.com'
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($PSEdition -eq 'Core' -And $SaveCredentials) {
|
||||||
|
write-error 'The parameter SaveCredentials of Connect-SkylineInsights cmdlet is not supported on PowerShell Core.'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($PSEdition -eq 'Core' -AND !$apiKey) {
|
||||||
|
write-error 'An API key is required.'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create VICredentialStore item to save the API key
|
||||||
|
if ($apiKey -AND $SaveCredentials) {
|
||||||
|
if ( (Get-Command Get-VICredentialStoreItem -ErrorAction:SilentlyContinue | Measure-Object).Count -gt 0 ) {
|
||||||
|
$savedCred = Get-VICredentialStoreItem -host $skylineApi -ErrorAction:SilentlyContinue
|
||||||
|
if ($savedCred) {
|
||||||
|
$savedCred | Remove-VICredentialStoreItem -Confirm:$false
|
||||||
|
}
|
||||||
|
New-VICredentialStoreItem -Host $skylineApi -User 'api-key' -Password $apiKey
|
||||||
|
} else {
|
||||||
|
Write-Warning 'Use of -SaveCredentials requires the PowerCLI VICredentialStoreItem cmdlets.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$apiKey) {
|
||||||
|
if ( (Get-Command Get-VICredentialStoreItem -ErrorAction:SilentlyContinue | Measure-Object).Count -gt 0 ) {
|
||||||
|
$savedCred = Get-VICredentialStoreItem -host $skylineApi -ErrorAction:SilentlyContinue
|
||||||
|
}
|
||||||
|
if ( ($savedCred | Measure-Object).Count -eq 1) {
|
||||||
|
$apiKey = $savedCred.Password
|
||||||
|
} else {
|
||||||
|
write-error 'An API key is required.'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$loginHeader = @{
|
||||||
|
'Accept' = 'application/json'
|
||||||
|
'Content-Type' = 'application/x-www-form-urlencoded'
|
||||||
|
}
|
||||||
|
$loginBody = @{'refresh_token' = $apiKey }
|
||||||
|
|
||||||
|
try {
|
||||||
|
$webRequest = Invoke-RestMethod -Uri "https://$cspApi/csp/gateway/am/api/auth/api-tokens/authorize?grant_type=refresh_token" -method POST -Headers $loginHeader -Body $loginBody
|
||||||
|
|
||||||
|
$global:DefaultSkylineConnection = New-Object psobject -property @{ 'Name'=$skylineApi; 'CSPName'=$cspApi; 'ConnectionDetail'=$webRequest; APIKey = $apiKey;
|
||||||
|
'Refresh_Token'=$webRequest.refresh_token; 'SkylineAPI'="https://$skylineApi/public/api/data"; PSTypeName='SkylineConnection' }
|
||||||
|
|
||||||
|
# Return the connection object
|
||||||
|
$global:SkylineInsightsApiQueryCount = 0
|
||||||
|
$global:SkylineInsightsApiQueryLastTime = $null
|
||||||
|
$global:DefaultSkylineConnection
|
||||||
|
} catch {
|
||||||
|
Write-Error ("Failure connecting to $skylineAPI. Posted $loginBody " + $_)
|
||||||
|
} # end try/catch block
|
||||||
|
}
|
||||||
|
|
||||||
|
Function Disconnect-SkylineInsights {
|
||||||
|
<#
|
||||||
|
.NOTES
|
||||||
|
===========================================================================
|
||||||
|
Created by: Brian Wuchner
|
||||||
|
Date: February 21, 2022
|
||||||
|
Blog: www.enterpriseadmins.org
|
||||||
|
Twitter: @bwuch
|
||||||
|
===========================================================================
|
||||||
|
.SYNOPSIS
|
||||||
|
Use this function to disconnect from Skyline Insights API
|
||||||
|
.DESCRIPTION
|
||||||
|
This function will allow you to disconnect from a Skyline Insights API.
|
||||||
|
The global variable will be set with the Servername & Header value for use by other functions.
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Disconnect-SkylineInsights
|
||||||
|
This will remove a connection to Skyline Insights.
|
||||||
|
#>
|
||||||
|
if ($global:DefaultSkylineConnection) {
|
||||||
|
$global:DefaultSkylineConnection = $null
|
||||||
|
} else {
|
||||||
|
Write-Error 'Could not find an existing connection to SkylineInsights API.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Function Invoke-SkylineInsightsApi {
|
||||||
|
<#
|
||||||
|
.NOTES
|
||||||
|
===========================================================================
|
||||||
|
Created by: Brian Wuchner
|
||||||
|
Date: February 21, 2022
|
||||||
|
Blog: www.enterpriseadmins.org
|
||||||
|
Twitter: @bwuch
|
||||||
|
===========================================================================
|
||||||
|
.SYNOPSIS
|
||||||
|
Use this function to post a query to the Skyline Insights API.
|
||||||
|
.DESCRIPTION
|
||||||
|
This function will allow you to query the Skyline Insights API.
|
||||||
|
Proper headers will be formatted and posted if a DefaultSkylineConnection is present.
|
||||||
|
This is primarily a helper function used by other functions included in the module.
|
||||||
|
It is exported in the module manifest to be used for any custom queries.
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Invoke-SkylineInsightsApi -queryBody '{formatted-query-string-converted-to-json}'
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)][string]$queryBody,
|
||||||
|
[Parameter(DontShow=$true)][int]$sleepTimerMs=501
|
||||||
|
)
|
||||||
|
|
||||||
|
if ( !$global:DefaultSkylineConnection ) {
|
||||||
|
Write-Error 'You are not currently connected to any servers. Please connect first using Connect-SkylineInsights.'
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
write-debug "Querybody: $queryBody"
|
||||||
|
try {
|
||||||
|
if ($global:SkylineInsightsApiQueryLastTime) {
|
||||||
|
$timeSinceLastQuery = (New-TimeSpan $global:SkylineInsightsApiQueryLastTime (Get-Date)).TotalMilliseconds
|
||||||
|
if ($timeSinceLastQuery -lt $sleepTimerMs) {
|
||||||
|
Write-Debug "Waiting $($sleepTimerMs-$timeSinceLastQuery)ms to prevent HTTP 429 TOO_MANY_REQUESTS error"
|
||||||
|
Start-Sleep -Milliseconds ($sleepTimerMs-$timeSinceLastQuery)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$restCall = invoke-restmethod -method post -Uri $($global:DefaultSkylineConnection.SkylineAPI) -Headers @{Authorization = "Bearer $($global:DefaultSkylineConnection.ConnectionDetail.access_token)"} -body $queryBody -ContentType "application/json"
|
||||||
|
$global:SkylineInsightsApiQueryCount++
|
||||||
|
$global:SkylineInsightsApiQueryLastTime = Get-Date
|
||||||
|
if ($restCall.errors) {
|
||||||
|
Write-Error $restCall.errors.Message
|
||||||
|
}
|
||||||
|
return $restCall
|
||||||
|
} catch {
|
||||||
|
$incomingError = $_
|
||||||
|
try {
|
||||||
|
# are nested try/catch blocks the powershell equilivent of vbscript On Error Resume Next?
|
||||||
|
$errorStatusAsJson = ($incomingError | ConvertFrom-Json).status
|
||||||
|
if ($errorStatusAsJson -eq '429 TOO_MANY_REQUESTS') {
|
||||||
|
write-error 'Encountered HTTP 429 TOO_MANY_REQUESTS error, consider increasing sleepTimerMs value.'
|
||||||
|
start-sleep -Milliseconds (2*$sleepTimerMs)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
# this was the error from trying to cast the incoming error to Json
|
||||||
|
}
|
||||||
|
if (!$errorStatusAsJson) { write-error $incomingError }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Function Get-SkylineFinding {
|
||||||
|
<#
|
||||||
|
.NOTES
|
||||||
|
===========================================================================
|
||||||
|
Created by: Brian Wuchner
|
||||||
|
Date: February 21, 2022
|
||||||
|
Blog: www.enterpriseadmins.org
|
||||||
|
Twitter: @bwuch
|
||||||
|
===========================================================================
|
||||||
|
.SYNOPSIS
|
||||||
|
Use this function to query findings from the Skyline Insights API.
|
||||||
|
.DESCRIPTION
|
||||||
|
This function will allow you to query the Skyline Insights API for Findings.
|
||||||
|
As described in the documentation, the maximum limit per page is 200 records. This function provides
|
||||||
|
an optional pagesize parameter to request smaller batches, but by default assumes 200 records.
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Get-SkylineFinding
|
||||||
|
#>
|
||||||
|
[cmdletbinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(ValueFromPipelineByPropertyName=$true)][string]$findingId,
|
||||||
|
[Parameter(ValueFromPipelineByPropertyName=$true)][string[]]$products,
|
||||||
|
[Parameter(ValueFromPipelineByPropertyName=$true)][ValidateSet('CRITICAL','MODERATE','TRIVIAL')][string]$severity,
|
||||||
|
[Parameter(DontShow=$true)][ValidateRange(1,200)][int]$pagesize=200
|
||||||
|
)
|
||||||
|
|
||||||
|
begin {
|
||||||
|
$queryBody = @"
|
||||||
|
{
|
||||||
|
activeFindings(limit: $pagesize, start: 0 filter: {}) {
|
||||||
|
findings {
|
||||||
|
findingId
|
||||||
|
accountId
|
||||||
|
findingDisplayName
|
||||||
|
severity
|
||||||
|
products
|
||||||
|
findingDescription
|
||||||
|
findingImpact
|
||||||
|
recommendations
|
||||||
|
kbLinkURLs
|
||||||
|
recommendationsVCF
|
||||||
|
kbLinkURLsVCF
|
||||||
|
categoryName
|
||||||
|
findingTypes
|
||||||
|
firstObserved
|
||||||
|
totalAffectedObjectsCount
|
||||||
|
}
|
||||||
|
totalRecords
|
||||||
|
timeTaken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"@
|
||||||
|
|
||||||
|
}
|
||||||
|
process {
|
||||||
|
if (!$products) { $products = 'NO_PRODUCT_FILTER'}
|
||||||
|
foreach ($thisProduct in $products) {
|
||||||
|
if ($findingId) { $filterString = "findingId: `"$findingId`"," }
|
||||||
|
if ($thisProduct -ne 'NO_PRODUCT_FILTER') { $filterString += "product: `"$thisProduct`"," }
|
||||||
|
|
||||||
|
# Try to get results the first time
|
||||||
|
$results = @()
|
||||||
|
$thisQueryBody = $queryBody -Replace 'filter: {}', "filter: { $filterString }"
|
||||||
|
$thisIteration = 0
|
||||||
|
do {
|
||||||
|
$thisQueryBody = $thisQueryBody -Replace 'start: 0', "start: $thisIteration"
|
||||||
|
Write-Debug $thisQueryBody
|
||||||
|
$thisResult = Invoke-SkylineInsightsApi -queryBody (@{'query' = $thisQueryBody} | ConvertTo-Json -Compress)
|
||||||
|
$totalRecords = $thisResult.data.activeFindings.totalRecords
|
||||||
|
if ($severity) {
|
||||||
|
$thisResult.data.activeFindings.Findings | Where-Object {$_.severity -eq $severity}
|
||||||
|
} else {
|
||||||
|
$thisResult.data.activeFindings.Findings
|
||||||
|
}
|
||||||
|
$results += ($thisResult.data.activeFindings.Findings)
|
||||||
|
$thisIteration += $pageSize
|
||||||
|
} while ($results.count -lt $totalRecords ) # end do/while loop
|
||||||
|
|
||||||
|
#return $results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Function Get-SkylineAffectedObject {
|
||||||
|
<#
|
||||||
|
.NOTES
|
||||||
|
===========================================================================
|
||||||
|
Created by: Brian Wuchner
|
||||||
|
Date: February 21, 2022
|
||||||
|
Blog: www.enterpriseadmins.org
|
||||||
|
Twitter: @bwuch
|
||||||
|
===========================================================================
|
||||||
|
.SYNOPSIS
|
||||||
|
Use this function to query affected objects from the Skyline Insights API.
|
||||||
|
.DESCRIPTION
|
||||||
|
This function will allow you to query the Skyline Insights API for affected objects.
|
||||||
|
Input parameters are required for the findingId and product. Products can be provided as an object (from Get-SkylineFinding) or
|
||||||
|
a single product can be specified by name (or delimited list).
|
||||||
|
As described in the documentation, the maximum limit per page is 200 records. This function provides
|
||||||
|
an optional pagesize parameter to request smaller batches, but by default assumes 200 records.
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Get-SkylineAffectedObject -findingId 'vSphere-Vmtoolsmemoryleak-KB#76163' -product 'core-vcenter01.lab.enterpriseadmins.org'
|
||||||
|
This example uses the ByName parameter set to pass in specific findings/product and expects either a single product or a 'separator' delimited list
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Get-SkylineFinding | Select-Object -First 2 | Get-SkylineAffectedObject
|
||||||
|
This example uses the ByObject parameter set to pass in products as an object from Get-SkylineFinding
|
||||||
|
#>
|
||||||
|
[cmdletbinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)][string]$findingId,
|
||||||
|
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)][string[]]$products,
|
||||||
|
[Parameter(DontShow=$true)][ValidateRange(1,200)][int]$pagesize=200
|
||||||
|
)
|
||||||
|
|
||||||
|
begin {
|
||||||
|
$queryBody = @"
|
||||||
|
{
|
||||||
|
activeFindings(
|
||||||
|
filter: {
|
||||||
|
findingId: "",
|
||||||
|
product: "",
|
||||||
|
}) {
|
||||||
|
findings {
|
||||||
|
totalAffectedObjectsCount
|
||||||
|
affectedObjects(start: 0, limit: $pagesize) {
|
||||||
|
sourceName
|
||||||
|
objectName
|
||||||
|
objectType
|
||||||
|
version
|
||||||
|
buildNumber
|
||||||
|
solutionTags {
|
||||||
|
type
|
||||||
|
version
|
||||||
|
}
|
||||||
|
firstObserved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalRecords
|
||||||
|
timeTaken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"@
|
||||||
|
|
||||||
|
# Try to get results the first time
|
||||||
|
}
|
||||||
|
|
||||||
|
process {
|
||||||
|
$thisQueryBody = $queryBody -Replace 'findingId: "",', "findingId: `"$findingId`","
|
||||||
|
foreach ( $thisProduct in $products ) {
|
||||||
|
$thisIteration = 0
|
||||||
|
$results = @() # reset results variable between products
|
||||||
|
do {
|
||||||
|
$thisQueryBody = $thisQueryBody -Replace 'product: "",', "product: `"$thisProduct`","
|
||||||
|
$thisQueryBody = $thisQueryBody -Replace 'start: 0', "start: $thisIteration"
|
||||||
|
Write-Debug $thisQueryBody
|
||||||
|
$thisResult = Invoke-SkylineInsightsApi -queryBody (@{'query' = $thisQueryBody} | ConvertTo-Json -Compress)
|
||||||
|
$totalRecords = $thisResult.data.activeFindings.Findings.totalAffectedObjectsCount
|
||||||
|
$thisResult.data.activeFindings.Findings.affectedObjects | Select-Object @{N='findingId';E={$findingId}}, *
|
||||||
|
$results += ($thisResult.data.activeFindings.Findings.affectedObjects) | Select-Object @{N='findingId';E={$findingId}}, *
|
||||||
|
$thisIteration += $pagesize
|
||||||
|
} while ($results.count -lt $totalRecords ) # end do/while loop
|
||||||
|
} # end foreach product loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Function Format-SkylineResult {
|
||||||
|
<#
|
||||||
|
.NOTES
|
||||||
|
===========================================================================
|
||||||
|
Created by: Brian Wuchner
|
||||||
|
Date: February 21, 2022
|
||||||
|
Blog: www.enterpriseadmins.org
|
||||||
|
Twitter: @bwuch
|
||||||
|
===========================================================================
|
||||||
|
.SYNOPSIS
|
||||||
|
Use this function to format results from the Skyline Insights API
|
||||||
|
.DESCRIPTION
|
||||||
|
This function will format the output from the Skyline Insights API.
|
||||||
|
For example, Get-SkylineFinding and Get-SkylineAffectedObject will return some strings, date values as numbers, and object properties.
|
||||||
|
This function will convert date numbers to powershell dates and objects to delimiter separated stings. This should help with exporting
|
||||||
|
results to CSV files for example.
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Get-SkylineFinding | Format-SkylineResult | Export-Csv c:\temp\findings.csv -NoTypeInformation
|
||||||
|
This will return Skyline Findings, format them as needed, and export results to a CSV file.
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][PSCustomObject]$inputObject,
|
||||||
|
[string]$separator = '; '
|
||||||
|
)
|
||||||
|
begin {
|
||||||
|
$results = @()
|
||||||
|
|
||||||
|
# To format the dates, we need to add the value returned by the API to the begining of time
|
||||||
|
$startOfTime = Get-Date '1970-01-01'
|
||||||
|
}
|
||||||
|
|
||||||
|
process {
|
||||||
|
if ( $inputObject.accountId ) {
|
||||||
|
#This appears to be a Finding
|
||||||
|
$results += $inputObject | Select-Object findingId, accountId, findingDisplayName, severity, @{N='product';E={[string]::join($separator, $_.products)}}, findingDescription,
|
||||||
|
findingImpact, @{N='recommendations';E={[string]::Join($separator,$_.recommendations)}}, @{N='kbLinkURLs';E={[string]::Join($separator, $_.kbLinkURLs)}},
|
||||||
|
@{N='recommendationsVCF';E={[string]::Join($separator,$_.recommendationsVCF)}}, @{N='kbLinkURLsVCF';E={[string]::Join($separator, $_.kbLinkURLsVCF)}},
|
||||||
|
categoryName, @{N='findingTypes';E={[string]::Join($sep, $_.findingTypes)}}, @{N='firstObserved';E={ $startOfTime+[timespan]::FromMilliseconds($_.firstObserved) }},
|
||||||
|
totalAffectedObjectsCount
|
||||||
|
|
||||||
|
} elseif ( $inputObject.objectName ) {
|
||||||
|
#This appears to be an AffectedObject
|
||||||
|
$results += $inputObject | Select-Object findingId, sourceName, objectName, objectType, version, buildNumber, @{N='solutionTags-Type';E={$_.solutionTags.type}},
|
||||||
|
@{N='solutionTags-Version';E={$_.solutionTags.version}}, @{N='firstObserved';E={ $startOfTime+[timespan]::FromMilliseconds($_.firstObserved) }}
|
||||||
|
} else {
|
||||||
|
write-warning "Unable to determine input object type."
|
||||||
|
} # end inputobject evaluation
|
||||||
|
} #end process
|
||||||
|
|
||||||
|
end {
|
||||||
|
return $results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Function Start-SkylineInsightsApiExplorer {
|
||||||
|
<#
|
||||||
|
.NOTES
|
||||||
|
===========================================================================
|
||||||
|
Created by: Brian Wuchner
|
||||||
|
Date: February 21, 2022
|
||||||
|
Blog: www.enterpriseadmins.org
|
||||||
|
Twitter: @bwuch
|
||||||
|
===========================================================================
|
||||||
|
.SYNOPSIS
|
||||||
|
Use this function to launch the Skyline Insights API in a browser.
|
||||||
|
.DESCRIPTION
|
||||||
|
This function will open the Skyline Insights API explorer in the default web browser and populate
|
||||||
|
the clipboard with the necessary authorization header value to enable interactive queries.
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Start-SkylineInsightsApiExplorer
|
||||||
|
#>
|
||||||
|
if ( !$global:DefaultSkylineConnection ) {
|
||||||
|
Write-Error 'You are not currently connected to any servers. Please connect first using Connect-SkylineInsights.'
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
"Default web browser will launch to the Skyline Insights API explorer. In the lower left select 'Request Headers' and paste the authorization/bearer token into the text box. `nNote: this script has updated your clipboard with the required auth token."
|
||||||
|
"{`"Authorization`":`"Bearer $($global:DefaultSkylineConnection.ConnectionDetail.access_token)`"}" | Set-Clipboard
|
||||||
|
Start-Process "https://$($global:DefaultSkylineConnection.Name)/public/api/docs"
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user