Private/AriaOpsHelpers.ps1
|
# Copyright (c) 2026 Broadcom. All Rights Reserved. # Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc. # and/or its subsidiaries. # # ============================================================================= # # SOFTWARE LICENSE AGREEMENT # # Copyright (c) CA, Inc. All rights reserved. # # You are hereby granted a non-exclusive, worldwide, royalty-free license # under CA, Inc.'s copyrights to use, copy, modify, and distribute this # software in source code or binary form for use in connection with CA, Inc. # products. # # This copyright notice shall be included in all copies or substantial # portions of the software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # ============================================================================= #region AriaOpsHelpers # # Aria Operations has full PowerCLI/OpenAPI SDK coverage via VMware.Sdk.Vcf.Ops (nested inside # VCF.PowerCLI) - unlike VRSLCM, no hand-written REST helper is needed. Checks call the # Invoke-VcfOps* cmdlets directly (e.g. Invoke-VcfOpsGetAlerts), passing the connection object # this file resolves as -Server. # # Aria Operations has no per-domain FQDN lookup - like VRSLCM, Invoke-VcfGetCredentials # -ResourceType VROPS is the only source for both its FQDN and credential. Credential filtering # on CredentialType -eq 'API' is done client-side because Invoke-VcfGetCredentials lacks a # -CredentialType parameter (only -AccountType is available). # # Aria Operations can also be deployed entirely outside SDDC Manager/VRSLCM (SDDC Manager has # zero knowledge of it) - see Get-VcfCheckEnvironmentAriaOpsEndpoints, which resolves the # user-declared Integrations list on an environment (Private/Environments.ps1) instead of # Invoke-VcfGetCredentials. Connect-VcfCheckAriaOpsEndpoint connects to one of those directly by # FQDN + credential, with no SDDC Manager lookup at all. # # Invoke-VcfOpsGetResourcesOfAdapterInstance (VMware.Sdk.Vcf.Ops 13.5.0.25380678, the newest # release at time of writing) sends a malformed 'adapterId' value for this specific endpoint - a # resource-kind schema version tag (e.g. "Variant.8,Version.4") instead of the real adapter # instance GUID - so Aria Operations rejects every call with HTTP 400. Get-VcfCheckAriaOpsAdapterResources # below bypasses the SDK for this one call and hits '/suite-api/api/adapters/{id}/resources' # directly via Invoke-VcfCheckAriaOpsApi, the same REST-fallback pattern VrslcmHelpers.ps1 uses - # except unlike VRSLCM, suite-api does not accept HTTP Basic Auth on ordinary endpoints, so # Invoke-VcfCheckAriaOpsApi first exchanges the credential for a session token via # Get-VcfCheckAriaOpsApiToken ('/suite-api/api/auth/token/acquire') and sends it as an # 'Authorization: vRealizeOpsToken <token>' header instead. # # Invoke-VcfOpsGetResource sends the same malformed schema-version-tag value as its 'id', so # Get-VcfCheckAriaOpsResource below hits '/suite-api/api/resources/{id}' directly via # Invoke-VcfCheckAriaOpsApi instead. Test-VcfAriaOpsCriticalAlerts guards with [Guid]::TryParse # before calling it, since Alert.ResourceId can independently arrive non-GUID. function Get-VcfCheckAriaOpsCredential { <# .SYNOPSIS Resolves and caches Aria Operations' FQDN and API credential from SDDC Manager. .DESCRIPTION Calls Invoke-VcfGetCredentials -ResourceType VROPS once per run and caches the result on $Context. Returns $null (not an exception) when no matching credential entry comes back - this is the expected, normal shape of "Aria Operations is not deployed in this environment", which every Aria Operations check must treat as Skipped rather than Error. .PARAMETER Context The VcfCheck.Context object. Must already be connected to SDDC Manager. .OUTPUTS [PSCustomObject] with Fqdn/Credential properties, or $null if Aria Operations is not deployed. #> [CmdletBinding()] [OutputType([PSCustomObject])] Param ( [Parameter(Mandatory = $true)] [PSObject]$Context ) if ($Context.AriaOpsCredential) { return $Context.AriaOpsCredential } Write-LogMessage -Type DEBUG -Message 'Resolving Aria Operations FQDN and credential from SDDC Manager.' try { $response = Invoke-VcfGetCredentials -ResourceType VROPS -ErrorAction Stop } catch { throw [System.InvalidOperationException]::new("Failed to query SDDC Manager for Aria Operations credentials: $($_.Exception.Message)") } $match = @($response.Elements) | Where-Object { $_.CredentialType -eq 'API' } | Select-Object -First 1 if (-not $match) { Write-LogMessage -Type DEBUG -Message 'No Aria Operations credential entry returned by SDDC Manager - Aria Operations is not part of this environment.' return $null } $secure = ConvertTo-SecureStringForCredential -PlainText $match.Password $credential = [PSCredential]::new($match.Username, $secure) $resolved = [PSCustomObject]@{ PSTypeName = 'VcfCheck.AriaOpsCredential' Fqdn = $match.Resource.ResourceName Credential = $credential AllowInsecureTls = [Bool]$Context.AllowInsecureTls } Remove-Variable -Name match, response -ErrorAction SilentlyContinue $Context.AriaOpsCredential = $resolved return $resolved } function Connect-VcfCheckAriaOps { <# .SYNOPSIS Connects to Aria Operations via the VMware.Sdk.Vcf.Ops SDK, using the credential SDDC Manager has on file. .DESCRIPTION Resolves the FQDN/credential via Get-VcfCheckAriaOpsCredential, runs a TCP reachability pre-flight check (Test-VcfCheckTcpConnectivity) against <Fqdn>:443, then calls Connect-VcfOpsServer. The resulting connection is cached on $Context.AriaOpsConnection so every later check reuses it instead of reconnecting. On any failure - not deployed, unreachable, or auth failure - the reason is cached on $Context.UnreachableAriaOps so every later Aria Operations check fails fast with the same message. .PARAMETER Context The VcfCheck.Context object. Must already be connected to SDDC Manager. .PARAMETER ConnectivityTimeoutSeconds Maximum time to wait for the TCP reachability pre-flight check. Defaults to 15 seconds. .OUTPUTS The Aria Operations connection object returned by Connect-VcfOpsServer, or $null if Aria Operations is not deployed in this environment. .EXAMPLE Connect-VcfCheckAriaOps -Context $Context #> [CmdletBinding()] [OutputType([PSObject])] Param ( [Parameter(Mandatory = $true)] [PSObject]$Context, [Parameter(Mandatory = $false)] [Int]$ConnectivityTimeoutSeconds = 15 ) if ($Context.UnreachableAriaOps) { throw [System.InvalidOperationException]::new($Context.UnreachableAriaOps) } if ($Context.AriaOpsConnection) { return $Context.AriaOpsConnection } $resolved = Get-VcfCheckAriaOpsCredential -Context $Context if (-not $resolved) { return $null } if (-not (Test-VcfCheckTcpConnectivity -ComputerName $resolved.Fqdn -Port 443 -TimeoutSeconds $ConnectivityTimeoutSeconds)) { $reason = "Could not reach Aria Operations `"$($resolved.Fqdn)`" on port 443 within $ConnectivityTimeoutSeconds second(s). Check VPN/network connectivity to the environment, firewall rules, and that the FQDN resolves to the correct address, then retry." $Context.UnreachableAriaOps = $reason throw [System.InvalidOperationException]::new($reason) } Write-LogMessage -Type INFO -Message "Connecting to Aria Operations `"$($resolved.Fqdn)`"..." -NoNewline try { $connection = Connect-VcfOpsServer -Server $resolved.Fqdn -Credential $resolved.Credential -NotDefault -IgnoreInvalidCertificate:$resolved.AllowInsecureTls -ErrorAction Stop Write-Host " Connected" -ForegroundColor White } catch { Write-Host " Failed" -ForegroundColor Red $reason = Get-VcfCheckTlsTrustErrorMessage -ComponentName 'Aria Operations' -Fqdn $resolved.Fqdn -ErrorMessage $_.Exception.Message if (-not $reason) { $reason = "Failed to connect to Aria Operations `"$($resolved.Fqdn)`". Verify network connectivity, that the credential SDDC Manager has on file is still valid, and that Aria Operations is reachable." } $Context.UnreachableAriaOps = $reason throw [System.InvalidOperationException]::new($reason) } $Context.AriaOpsConnection = $connection return $connection } function Get-VcfCheckEnvironmentAriaOpsEndpoints { <# .SYNOPSIS Resolves the user-declared, standalone Aria Operations endpoints attached to an environment (Private/Environments.ps1's Integrations field) - components SDDC Manager and VRSLCM have zero knowledge of. .DESCRIPTION Filters $Environment.Integrations to entries with Type -eq 'AriaOperations' and flattens their Endpoints into one object per endpoint, applying each integration's SharedCredentials setting to resolve which Username applies (the integration-level one when shared, else the endpoint's own). Never resolves a password - see Connect-VcfCheckAriaOpsEndpoint. .PARAMETER Environment An environment object as returned by Get-VcfCheckEnvironments. .OUTPUTS [Object[]] of PSCustomObject with Name, Fqdn, Username. Empty array if the environment has no AriaOperations integration. .EXAMPLE Get-VcfCheckEnvironmentAriaOpsEndpoints -Environment $environment #> [CmdletBinding()] [OutputType([Object[]])] Param ( [Parameter(Mandatory = $true)] [PSObject]$Environment ) $results = [System.Collections.Generic.List[PSObject]]::new() $ariaIntegrations = @($Environment.Integrations) | Where-Object { $_ -and $_.Type -eq 'AriaOperations' } foreach ($integration in $ariaIntegrations) { foreach ($endpoint in @($integration.Endpoints)) { if (-not $endpoint) { continue } $username = if ($integration.SharedCredentials) { $integration.Username } else { $endpoint.Username } $results.Add([PSCustomObject]@{ PSTypeName = 'VcfCheck.AriaOpsEndpoint' Name = $endpoint.Name Fqdn = $endpoint.Fqdn Username = $username }) } } return $results.ToArray() } function Connect-VcfCheckAriaOpsEndpoint { <# .SYNOPSIS Connects directly to a user-declared, standalone Aria Operations endpoint by FQDN and credential - no SDDC Manager lookup. .DESCRIPTION Companion to Connect-VcfCheckAriaOps for Aria Operations instances SDDC Manager has zero knowledge of (see Get-VcfCheckEnvironmentAriaOpsEndpoints). Runs the same TCP reachability pre-flight and caches the resulting connection on $Context.AriaOpsEndpointConnections (keyed by Fqdn, since there can be more than one) so repeat checks against the same endpoint reuse it. Unreachable/failed endpoints are cached on $Context.UnreachableAriaOpsEndpoints (also keyed by Fqdn) for the same fail-fast reason Connect-VcfCheckVCenter caches per-vCenter failures. .PARAMETER Context The VcfCheck.Context object. .PARAMETER Fqdn The endpoint's FQDN, as entered by the user. .PARAMETER Credential The credential to connect with. The caller is responsible for resolving the password (session-only prompt or launcher parameter) - this function never persists or looks one up itself. .PARAMETER ConnectivityTimeoutSeconds Maximum time to wait for the TCP reachability pre-flight check. Defaults to 15 seconds. .OUTPUTS The Aria Operations connection object returned by Connect-VcfOpsServer. .EXAMPLE Connect-VcfCheckAriaOpsEndpoint -Context $Context -Fqdn 'vrops-standalone.example.com' -Credential $credential #> [CmdletBinding()] [OutputType([PSObject])] Param ( [Parameter(Mandatory = $true)] [PSObject]$Context, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$Fqdn, [Parameter(Mandatory = $true)] [PSCredential]$Credential, [Parameter(Mandatory = $false)] [Int]$ConnectivityTimeoutSeconds = 15 ) if (-not $Context.AriaOpsEndpointConnections) { $Context.AriaOpsEndpointConnections = @{} } if (-not $Context.UnreachableAriaOpsEndpoints) { $Context.UnreachableAriaOpsEndpoints = @{} } if ($Context.UnreachableAriaOpsEndpoints.ContainsKey($Fqdn)) { throw [System.InvalidOperationException]::new($Context.UnreachableAriaOpsEndpoints[$Fqdn]) } if ($Context.AriaOpsEndpointConnections.ContainsKey($Fqdn)) { return $Context.AriaOpsEndpointConnections[$Fqdn] } if (-not (Test-VcfCheckTcpConnectivity -ComputerName $Fqdn -Port 443 -TimeoutSeconds $ConnectivityTimeoutSeconds)) { $reason = "Could not reach Aria Operations `"$Fqdn`" on port 443 within $ConnectivityTimeoutSeconds second(s). Check VPN/network connectivity to the environment, firewall rules, and that the FQDN resolves to the correct address, then retry." $Context.UnreachableAriaOpsEndpoints[$Fqdn] = $reason throw [System.InvalidOperationException]::new($reason) } Write-LogMessage -Type INFO -Message "Connecting to Aria Operations `"$Fqdn`"..." -NoNewline try { $connection = Connect-VcfOpsServer -Server $Fqdn -Credential $Credential -NotDefault -IgnoreInvalidCertificate:$Context.AllowInsecureTls -ErrorAction Stop Write-Host " Connected" -ForegroundColor White } catch { Write-Host " Failed" -ForegroundColor Red $reason = Get-VcfCheckTlsTrustErrorMessage -ComponentName 'Aria Operations' -Fqdn $Fqdn -ErrorMessage $_.Exception.Message if (-not $reason) { $reason = "Failed to connect to Aria Operations `"$Fqdn`". Verify network connectivity and the credential entered for this endpoint." } $Context.UnreachableAriaOpsEndpoints[$Fqdn] = $reason throw [System.InvalidOperationException]::new($reason) } $Context.AriaOpsEndpointConnections[$Fqdn] = $connection return $connection } function Resolve-VcfCheckAriaOpsEndpointCredentials { <# .SYNOPSIS Resolves and caches a [PSCredential] per standalone Aria Operations endpoint's Username. .DESCRIPTION For each endpoint returned by Get-VcfCheckEnvironmentAriaOpsEndpoints, uses a matching entry (by Fqdn) in PreSuppliedCredentials (the launcher/browser session-only password path - see Tools/Invoke-VcfCheckLauncher.ps1) when present, otherwise prompts once via Read-Host -AsSecureString (the CLI path, matching Get-VcfCheckSddcManagerRootCredential's prompt style). Results are cached on $Context.AriaOpsEndpointCredentials, keyed by Fqdn, so a second run within the same $Context (e.g. re-invoking with a different -CheckId filter) never re-prompts. .PARAMETER Context The VcfCheck.Context object. .PARAMETER Endpoints Endpoint objects as returned by Get-VcfCheckEnvironmentAriaOpsEndpoints (Name/Fqdn/Username). .PARAMETER PreSuppliedCredentials Optional array of PSCustomObject/Hashtable with Fqdn/Username/Password (SecureString), e.g. resolved from the browser's per-endpoint session-only password field. Endpoints not matched here fall back to an interactive prompt. .EXAMPLE Resolve-VcfCheckAriaOpsEndpointCredentials -Context $Context -Endpoints $endpoints -PreSuppliedCredentials $AriaOpsEndpointCredentials #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', 'PreSuppliedCredentials', Justification = 'Object[] of Fqdn/Username/Password triplets, not a password itself - each element''s Password field is already a SecureString.')] Param ( [Parameter(Mandatory = $true)] [PSObject]$Context, [Parameter(Mandatory = $true)] [Object[]]$Endpoints, [Parameter(Mandatory = $false)] [Object[]]$PreSuppliedCredentials = @() ) foreach ($endpoint in @($Endpoints)) { if (-not $endpoint -or $Context.AriaOpsEndpointCredentials.ContainsKey($endpoint.Fqdn)) { continue } $preSupplied = @($PreSuppliedCredentials) | Where-Object { $_ -and $_.Fqdn -eq $endpoint.Fqdn } | Select-Object -First 1 if ($preSupplied) { $Context.AriaOpsEndpointCredentials[$endpoint.Fqdn] = [PSCredential]::new($endpoint.Username, $preSupplied.Password) continue } $securePassword = Read-Host -Prompt "Enter the password for $($endpoint.Username)@$($endpoint.Fqdn) (Aria Operations - $($endpoint.Name))" -AsSecureString if ($securePassword.Length -eq 0) { throw [System.InvalidOperationException]::new("Password for Aria Operations endpoint `"$($endpoint.Fqdn)`" must not be empty.") } $Context.AriaOpsEndpointCredentials[$endpoint.Fqdn] = [PSCredential]::new($endpoint.Username, $securePassword) } } function Get-VcfCheckAriaOpsTargets { <# .SYNOPSIS Resolves every Aria Operations instance a check should evaluate - the SDDC-Manager-known instance (if deployed) plus every standalone endpoint declared on the current environment - and connects to each. .DESCRIPTION Every Test-VcfAriaOps* check calls this once instead of Connect-VcfCheckAriaOps directly, so a single check fans out across every known Aria Operations instance rather than only the SDDC-Manager-known one. A connection failure for one target (unreachable, bad credential, not deployed) never blocks evaluation of the others - it is surfaced on that target's ConnectError instead of throwing, so the caller can emit one Error result per failed target and keep evaluating the rest. A standalone endpoint whose FQDN matches the SDDC-Manager-known instance's FQDN is skipped, since that is the same physical appliance registered twice - once via SDDC Manager and once as a manually declared endpoint - and would otherwise double every check result. .PARAMETER Context The VcfCheck.Context object. $Context.AriaOpsEndpoints and $Context.AriaOpsEndpointCredentials must already be populated by Invoke-VcfCheck (via Resolve-VcfCheckAriaOpsEndpointCredentials) for standalone endpoints to be included. .PARAMETER ConnectivityTimeoutSeconds Maximum time to wait for each target's TCP reachability pre-flight check. Defaults to 15 seconds. .OUTPUTS [Object[]] of PSCustomObject with Name, Fqdn, Connection, CredentialInfo, ConnectError. CredentialInfo (Fqdn/Credential) is the same shape Get-VcfCheckAriaOpsCredential returns - pass it to the REST-fallback helpers (Get-VcfCheckAriaOpsResource, Get-VcfCheckAriaOpsAdapterResources) instead of $Context.AriaOpsCredential so those calls target the right instance for a standalone endpoint too. Empty array if Aria Operations is not deployed via SDDC Manager and the environment has no standalone endpoints declared - callers must treat that as Skipped. .EXAMPLE Get-VcfCheckAriaOpsTargets -Context $Context #> [CmdletBinding()] [OutputType([Object[]])] Param ( [Parameter(Mandatory = $true)] [PSObject]$Context, [Parameter(Mandatory = $false)] [Int]$ConnectivityTimeoutSeconds = 15 ) $targets = [System.Collections.Generic.List[PSObject]]::new() try { $connection = Connect-VcfCheckAriaOps -Context $Context -ConnectivityTimeoutSeconds $ConnectivityTimeoutSeconds if ($connection) { $targets.Add([PSCustomObject]@{ Name = 'SDDC Manager'; Fqdn = $Context.AriaOpsCredential.Fqdn; Connection = $connection; CredentialInfo = $Context.AriaOpsCredential; ConnectError = $null }) } } catch { $targets.Add([PSCustomObject]@{ Name = 'SDDC Manager'; Fqdn = $Context.AriaOpsCredential.Fqdn; Connection = $null; CredentialInfo = $null; ConnectError = $_.Exception.Message }) } $knownFqdns = @($targets | Where-Object { $_.Fqdn } | ForEach-Object { $_.Fqdn }) foreach ($endpoint in @($Context.AriaOpsEndpoints) | Where-Object { $_ }) { if ($endpoint.Fqdn -and ($knownFqdns -icontains $endpoint.Fqdn)) { continue } $credential = $Context.AriaOpsEndpointCredentials[$endpoint.Fqdn] if (-not $credential) { $targets.Add([PSCustomObject]@{ Name = $endpoint.Name; Fqdn = $endpoint.Fqdn; Connection = $null; CredentialInfo = $null; ConnectError = "No password was supplied for Aria Operations endpoint `"$($endpoint.Fqdn)`"." }) continue } $credentialInfo = [PSCustomObject]@{ PSTypeName = 'VcfCheck.AriaOpsCredential'; Fqdn = $endpoint.Fqdn; Credential = $credential } try { $connection = Connect-VcfCheckAriaOpsEndpoint -Context $Context -Fqdn $endpoint.Fqdn -Credential $credential -ConnectivityTimeoutSeconds $ConnectivityTimeoutSeconds $targets.Add([PSCustomObject]@{ Name = $endpoint.Name; Fqdn = $endpoint.Fqdn; Connection = $connection; CredentialInfo = $credentialInfo; ConnectError = $null }) } catch { $targets.Add([PSCustomObject]@{ Name = $endpoint.Name; Fqdn = $endpoint.Fqdn; Connection = $null; CredentialInfo = $credentialInfo; ConnectError = $_.Exception.Message }) } } return $targets.ToArray() } function Get-VcfCheckAriaOpsCollectors { <# .SYNOPSIS Thin, mockable wrapper around Invoke-VcfOpsGetCollectors (see AriaOpsHelpers.ps1's file header for why this wrapper exists). .PARAMETER Connection The Aria Operations connection object returned by Connect-VcfCheckAriaOps or Connect-VcfCheckAriaOpsEndpoint. .OUTPUTS The Collectors response object returned by Invoke-VcfOpsGetCollectors. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSObject]$Connection ) return Invoke-VcfOpsGetCollectors -Server $Connection -ErrorAction Stop } function Get-VcfCheckAriaOpsCriticalAlerts { <# .SYNOPSIS Thin, mockable wrapper around Invoke-VcfOpsQueryAlert (see AriaOpsHelpers.ps1's file header for why this wrapper exists), pre-built for active Critical/Immediate alerts. .DESCRIPTION Builds an AlertQuery via Initialize-VcfOpsAlertQuery filtered to ActiveOnly with AlertCriticality 'CRITICAL'/'IMMEDIATE', then calls Invoke-VcfOpsQueryAlert. The response object's alert collection is exposed as its '_Alerts' property - a naming quirk of the generated SDK model, not a typo. .PARAMETER Connection The Aria Operations connection object returned by Connect-VcfCheckAriaOps or Connect-VcfCheckAriaOpsEndpoint. .OUTPUTS The Alerts response object returned by Invoke-VcfOpsQueryAlert. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSObject]$Connection ) $alertQuery = Initialize-VcfOpsAlertQuery -ActiveOnly $true -AlertCriticality @('CRITICAL', 'IMMEDIATE') return Invoke-VcfOpsQueryAlert -Server $Connection -AlertQuery $alertQuery -ErrorAction Stop } function Get-VcfCheckAriaOpsResource { <# .SYNOPSIS Thin, mockable wrapper around Aria Operations' '/suite-api/api/resources/{id}' REST endpoint - see AriaOpsHelpers.ps1's file header for why this bypasses the SDK. .DESCRIPTION Resolves a resource id (as returned on an Alert's ResourceId property) to its Resource object, whose ResourceKey carries the human-readable Name and AdapterKindKey/ ResourceKindKey - fields the Alert model itself does not expose. Invoke-VcfOpsGetResource (the SDK equivalent) hits the same "Variant,<n>,Version,4" schema-tag defect as Invoke-VcfOpsGetResourcesOfAdapterInstance - it malforms its own request path regardless of the id passed in - so this call goes straight to suite-api instead, the same REST-fallback pattern Get-VcfCheckAriaOpsAdapterResources uses. .PARAMETER CredentialInfo The AriaOpsCredential object returned by Get-VcfCheckAriaOpsCredential (Fqdn + Credential). .PARAMETER ResourceId The resource id to resolve. .OUTPUTS The Resource object returned by the suite-api. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSObject]$CredentialInfo, [Parameter(Mandatory = $true)] [Guid]$ResourceId ) return Invoke-VcfCheckAriaOpsApi -CredentialInfo $CredentialInfo -Path "/suite-api/api/resources/$($ResourceId.ToString())" -ErrorAction Stop } function Test-VcfCheckAriaOpsValueLooksLikeFqdn { <# .SYNOPSIS Returns whether a string value is shaped like an FQDN rather than a bare IP address. .PARAMETER Value The candidate string. .OUTPUTS [Bool] #> [CmdletBinding()] [OutputType([Bool])] Param ( [Parameter(Mandatory = $false)] [String]$Value ) if ([String]::IsNullOrWhiteSpace($Value)) { return $false } $parsedIp = $null if ([System.Net.IPAddress]::TryParse($Value, [ref]$parsedIp)) { return $false } return $Value -match '^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)+$' } function Resolve-VcfCheckAriaOpsCollectorFqdn { <# .SYNOPSIS Best-effort resolution of an Aria Operations collector's FQDN from its node resource. .DESCRIPTION A Collector's HostName property is documented by Aria Operations as "host name or IP address" and in practice is usually an IP address - the Collector object itself has no FQDN field. This resolves the collector's NodeIdentifier to its Resource object via Get-VcfCheckAriaOpsResource and scans ResourceKey.ResourceIdentifiers for a network-address-type identifier whose value is shaped like an FQDN (see Test-VcfCheckAriaOpsValueLooksLikeFqdn). Falls back to the collector's own HostName - logged via Write-LogMessage - when the resource lookup fails, the NodeIdentifier is not a GUID, or no FQDN-shaped identifier is found. This is best-effort enrichment only; it never throws. .PARAMETER CredentialInfo The AriaOpsCredential object returned by Get-VcfCheckAriaOpsCredential (Fqdn + Credential). .PARAMETER Collector A Collector object as returned by Get-VcfCheckAriaOpsCollectors. .OUTPUTS [String] The resolved FQDN, or the collector's HostName if none could be resolved. #> [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true)] [PSObject]$CredentialInfo, [Parameter(Mandatory = $true)] [PSObject]$Collector ) $nodeIdentifier = [Guid]::Empty if (-not [Guid]::TryParse($Collector.NodeIdentifier, [ref]$nodeIdentifier)) { return $Collector.HostName } try { $resource = Get-VcfCheckAriaOpsResource -CredentialInfo $CredentialInfo -ResourceId $nodeIdentifier } catch { Write-LogMessage -Type DEBUG -Message "Could not resolve FQDN for Aria Operations collector `"$($Collector.Name)`" - falling back to its reported HostName `"$($Collector.HostName)`": $($_.Exception.Message)" return $Collector.HostName } $identifiers = @($resource.ResourceKey.ResourceIdentifiers | Where-Object { $_.IdentifierType.Name -match '(?i)network|address|dns|host' }) $fqdn = $identifiers | ForEach-Object { $_.Value } | Where-Object { Test-VcfCheckAriaOpsValueLooksLikeFqdn -Value $_ } | Select-Object -First 1 if (-not $fqdn) { Write-LogMessage -Type DEBUG -Message "No FQDN-shaped network identifier found for Aria Operations collector `"$($Collector.Name)`" - falling back to its reported HostName `"$($Collector.HostName)`"." return $Collector.HostName } return $fqdn } function Get-VcfCheckAriaOpsCertificates { <# .SYNOPSIS Thin, mockable wrapper around Invoke-VcfOpsGetAllCertificates (see AriaOpsHelpers.ps1's file header for why this wrapper exists). .DESCRIPTION The response object's certificate collection is exposed as its '_Certificates' property - a naming quirk of the generated SDK model, not a typo. .PARAMETER Connection The Aria Operations connection object returned by Connect-VcfCheckAriaOps or Connect-VcfCheckAriaOpsEndpoint. .OUTPUTS The Certificates response object returned by Invoke-VcfOpsGetAllCertificates. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSObject]$Connection ) return Invoke-VcfOpsGetAllCertificates -Server $Connection -ErrorAction Stop } function Get-VcfCheckAriaOpsApiToken { <# .SYNOPSIS Acquires (and caches) an Aria Operations suite-api session token. .DESCRIPTION Aria Operations' suite-api does not accept HTTP Basic Auth on ordinary endpoints (unlike VRSLCM) - it requires a short-lived token acquired via POST '/suite-api/api/auth/token/acquire' with the username/password/authSource, then presented on every later call as 'Authorization: vRealizeOpsToken <token>'. The token is cached as an 'ApiToken' member added directly onto the CredentialInfo object (the same object callers already cache on $Context.AriaOpsCredential), so every call within a run re-uses it instead of re-authenticating. Aria Operations accounts are not always local - the credential's username determines the authSource to authenticate against: 'DOMAIN\user' or 'user@domain' is treated as a directory-backed account (authSource 'domain', username 'user' with the domain stripped, matching how Aria Operations' own login screen splits the same formats); a bare username with neither separator is treated as a local account (authSource 'LOCAL'). .PARAMETER CredentialInfo The AriaOpsCredential object returned by Get-VcfCheckAriaOpsCredential (Fqdn + Credential). .OUTPUTS [String] the session token. #> [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true)] [PSObject]$CredentialInfo ) if ($CredentialInfo.PSObject.Properties['ApiToken'] -and $CredentialInfo.ApiToken) { return $CredentialInfo.ApiToken } $rawUsername = $CredentialInfo.Credential.UserName if ($rawUsername -match '^(?<domain>[^\\]+)\\(?<user>.+)$') { $username = $Matches.user $authSource = $Matches.domain } elseif ($rawUsername -match '^(?<user>[^@]+)@(?<domain>.+)$') { $username = $Matches.user $authSource = $Matches.domain } else { $username = $rawUsername $authSource = 'LOCAL' } $body = @{ username = $username password = $CredentialInfo.Credential.GetNetworkCredential().Password authSource = $authSource } | ConvertTo-Json -Depth 2 try { $response = Invoke-RestMethod -Uri "https://$($CredentialInfo.Fqdn)/suite-api/api/auth/token/acquire" ` -Method POST -Body $body -ContentType 'application/json' -Headers @{ Accept = 'application/json' } ` -SkipCertificateCheck:$CredentialInfo.AllowInsecureTls -ErrorAction Stop } catch { $message = Get-VcfCheckTlsTrustErrorMessage -ComponentName 'Aria Operations' -Fqdn $CredentialInfo.Fqdn -ErrorMessage $_.Exception.Message if (-not $message) { throw } throw [System.InvalidOperationException]::new($message) } if ($CredentialInfo.PSObject.Properties['ApiToken']) { $CredentialInfo.ApiToken = $response.token } else { $CredentialInfo | Add-Member -NotePropertyName 'ApiToken' -NotePropertyValue $response.token } return $response.token } function Invoke-VcfCheckAriaOpsApi { <# .SYNOPSIS Calls Aria Operations' suite-api REST API directly with a token acquired via Get-VcfCheckAriaOpsApiToken, bypassing the VMware.Sdk.Vcf.Ops SDK. .DESCRIPTION Thin Invoke-RestMethod wrapper for the handful of suite-api endpoints where the VMware.Sdk.Vcf.Ops SDK cannot be used - see AriaOpsHelpers.ps1's file header. -SkipCertificateCheck is only passed when CredentialInfo.AllowInsecureTls is $true (the run's resolved AllowInsecureTls value, derived from PowerCLI's InvalidCertificateAction setting (see Invoke-VcfCheck in Orchestrator.ps1 and Get-VcfCheckAriaOpsCredential) rather than being unconditional; an untrusted certificate encountered while that is $false surfaces as a clear, actionable error via Get-VcfCheckTlsTrustErrorMessage instead of being silently accepted. Retries up to 3 times with a 10-second delay on transient network failures (timeouts, connection resets). .PARAMETER CredentialInfo The AriaOpsCredential object returned by Get-VcfCheckAriaOpsCredential (Fqdn + Credential). .PARAMETER Method HTTP method. Defaults to GET. .PARAMETER Path The suite-api path, e.g. '/suite-api/api/adapters/<guid>/resources'. .OUTPUTS The parsed JSON response. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSObject]$CredentialInfo, [Parameter(Mandatory = $false)] [ValidateSet('GET', 'POST')] [String]$Method = 'GET', [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$Path ) $token = Get-VcfCheckAriaOpsApiToken -CredentialInfo $CredentialInfo $uri = "https://$($CredentialInfo.Fqdn)$Path" $parameters = @{ Uri = $uri Method = $Method Headers = @{ Accept = 'application/json'; Authorization = "vRealizeOpsToken $token" } SkipCertificateCheck = $CredentialInfo.AllowInsecureTls ErrorAction = 'Stop' } $maxAttempts = 3 $retryDelaySeconds = 10 for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { try { return Invoke-RestMethod @parameters } catch { $tlsMessage = Get-VcfCheckTlsTrustErrorMessage -ComponentName 'Aria Operations' -Fqdn $CredentialInfo.Fqdn -ErrorMessage $_.Exception.Message if ($tlsMessage) { throw [System.InvalidOperationException]::new($tlsMessage) } $isTransient = $_.Exception.Message -match '(?i)timed out|timeout|actively refused|connection refused|reset by peer|handshake' if (-not $isTransient -or $attempt -eq $maxAttempts) { throw } Write-LogMessage -Type DEBUG -Message "Transient error calling Aria Operations suite-api '$Path' (attempt $attempt of $maxAttempts): $($_.Exception.Message). Retrying in $retryDelaySeconds seconds." Start-Sleep -Seconds $retryDelaySeconds } } } function Get-VcfCheckAriaOpsAdapterInstances { <# .SYNOPSIS Thin, mockable wrapper around Invoke-VcfOpsEnumerateAdapterInstances (see AriaOpsHelpers.ps1's file header for why this wrapper exists). .DESCRIPTION The response object's adapter instance collection is exposed as its 'AdapterInstancesInfoDto' property, per the generated SDK model. .PARAMETER Connection The Aria Operations connection object returned by Connect-VcfCheckAriaOps or Connect-VcfCheckAriaOpsEndpoint. .OUTPUTS The AdapterInstances response object returned by Invoke-VcfOpsEnumerateAdapterInstances. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSObject]$Connection ) return Invoke-VcfOpsEnumerateAdapterInstances -Server $Connection -ErrorAction Stop } function Get-VcfCheckAriaOpsAdapterResources { <# .SYNOPSIS Thin, mockable wrapper around Aria Operations' '/suite-api/api/adapters/{id}/resources' REST endpoint - see AriaOpsHelpers.ps1's file header for why this bypasses the SDK. .DESCRIPTION The response object's resource collection is exposed as its 'ResourceList' property (PowerShell's property access on a JSON-deserialized PSCustomObject is case-insensitive, so this matches the suite-api response's camelCase 'resourceList' as well as the generated SDK model's PascalCase naming callers were already written against). Each resource's ResourceStatusStates carries the collection status (e.g. DATA_RECEIVING, COLLECTOR_DOWN) per adapter instance monitoring it. .PARAMETER CredentialInfo The AriaOpsCredential object returned by Get-VcfCheckAriaOpsCredential (Fqdn + Credential). .PARAMETER AdapterId The identifier of the adapter instance to enumerate resources for. .OUTPUTS The Resources response object returned by the suite-api. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSObject]$CredentialInfo, [Parameter(Mandatory = $true)] [Guid]$AdapterId ) return Invoke-VcfCheckAriaOpsApi -CredentialInfo $CredentialInfo -Path "/suite-api/api/adapters/$($AdapterId.ToString())/resources" -ErrorAction Stop } function Get-VcfCheckAriaOpsLicenseEntitlement { <# .SYNOPSIS Thin, mockable wrapper around Aria Operations' '/suite-api/api/product/licensing/entitlement' REST endpoint - see AriaOpsHelpers.ps1's file header for why this bypasses the SDK. .DESCRIPTION No VMware.Sdk.Vcf.Ops cmdlet covers this endpoint (only Invoke-VcfOpsGetLicenseInfo exists, and it 404s on every real instance tested - see Docs/CheckConfidence.md's aria_ops_license entry), so this goes straight through Invoke-VcfCheckAriaOpsApi. The response object's license collection is exposed as its 'solutionLicenses' property, each entry carrying id/licenseKey/expirationDate (epoch milliseconds)/capacity/usage/edition/ licenseType/statuses per the 8.18 swagger spec's 'solution-licenses' schema. .PARAMETER CredentialInfo The AriaOpsCredential object returned by Get-VcfCheckAriaOpsCredential (Fqdn + Credential). .OUTPUTS The solution-licenses response object returned by the suite-api. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSObject]$CredentialInfo ) return Invoke-VcfCheckAriaOpsApi -CredentialInfo $CredentialInfo -Path '/suite-api/api/product/licensing/entitlement' -ErrorAction Stop } function Get-VcfCheckAriaOpsVersion { <# .SYNOPSIS Thin, mockable wrapper around Invoke-VcfOpsGetCurrentVersionOfServer, returning Aria Operations' own reported marketing version as a dotted string. .DESCRIPTION The SDK's Version model (VMware.Bindings.Vcf.Ops.Model.Version) exposes Major/Minor/ MinorMinor/Patch as separate integers, but those are the internal build-numbering scheme (e.g. 1.70.0), not the marketing product version (e.g. 8.18.0) that the shipped Interop Matrix snapshot (Data/Interoperability/Vrops.json) uses. The marketing version is embedded as free text in the model's ReleaseName property (e.g. 'VMware Aria Operations 8.18.0'), so this extracts the trailing dotted-number group from it. .PARAMETER Connection The Aria Operations connection object returned by Connect-VcfCheckAriaOps or Connect-VcfCheckAriaOpsEndpoint. .OUTPUTS [String] the dotted marketing version string, or $null if it could not be determined. #> [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true)] [PSObject]$Connection ) $version = Invoke-VcfOpsGetCurrentVersionOfServer -Server $Connection -ErrorAction Stop if ($null -eq $version -or [String]::IsNullOrWhiteSpace($version.ReleaseName)) { return $null } $match = [Regex]::Match($version.ReleaseName, '\d+(?:\.\d+){1,3}$') if (-not $match.Success) { return $null } return $match.Value } function Get-VcfCheckAriaOpsSelfMonitoringResources { <# .SYNOPSIS Thin, mockable wrapper around Invoke-VcfOpsGetResourcesWithAdapterKind (see AriaOpsHelpers.ps1's file header for why this wrapper exists), pre-built for Aria Operations' own self-monitoring resources (its cluster and node(s)). .DESCRIPTION Aria Operations monitors itself under a built-in adapter kind - the same resources the product's own "vRealize Operations Manager Cluster" dashboards chart. The exact adapter kind key varies in shape by version and is resolved by the caller (see Get-VcfCheckAriaOpsSelfMonitoringAdapterKind) rather than hardcoded here, since a hardcoded guess ('VCOPS_VCOPS_ADAPTER') was confirmed live to return zero resources against a real instance - see ARIA_OPS_CONNECTOR_PLAN.md. The response object's resource collection is exposed as its 'ResourceList' property, per the generated SDK model. .PARAMETER Connection The Aria Operations connection object returned by Connect-VcfCheckAriaOps or Connect-VcfCheckAriaOpsEndpoint. .PARAMETER AdapterKindKey The adapter kind key to query, as resolved by Get-VcfCheckAriaOpsSelfMonitoringAdapterKind. .OUTPUTS The Resources response object returned by Invoke-VcfOpsGetResourcesWithAdapterKind. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSObject]$Connection, [Parameter(Mandatory = $true)] [String]$AdapterKindKey ) return Invoke-VcfOpsGetResourcesWithAdapterKind -Server $Connection -AdapterKindKey $AdapterKindKey -ErrorAction Stop } function Get-VcfCheckAriaOpsSelfMonitoringAdapterKind { <# .SYNOPSIS Resolves the adapter kind key of Aria Operations' own self-monitoring adapter instance by matching on name/kind rather than a hardcoded key. .DESCRIPTION Calls Get-VcfCheckAriaOpsAdapterInstances and returns the AdapterKindKey of the first configured adapter instance whose AdapterKindKey or resource name matches the self-monitoring adapter's known naming pattern ('VCOPS', 'vRealize Operations Manager', or 'vRealize Operations Adapter' - a real instance's self-monitoring adapters are named 'vRealize Operations Adapter - <node>', one per node, confirmed live against xint-ops01.rainpole.io). A hardcoded key ('VCOPS_VCOPS_ADAPTER') was tried previously and confirmed live to return zero resources - see ARIA_OPS_CONNECTOR_PLAN.md - so this discovers the key from the instance's own adapter inventory instead of guessing. .PARAMETER Connection The Aria Operations connection object returned by Connect-VcfCheckAriaOps or Connect-VcfCheckAriaOpsEndpoint. .OUTPUTS [String] The discovered adapter kind key, or $null if no self-monitoring adapter instance is configured. #> [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true)] [PSObject]$Connection ) $response = Get-VcfCheckAriaOpsAdapterInstances -Connection $Connection $adapters = @($response.AdapterInstancesInfoDto) | Where-Object { $_ -and $_.ResourceKey } $selfMonitoring = $adapters | Where-Object { $_.ResourceKey.AdapterKindKey -match '(?i)vcops' -or $_.ResourceKey.Name -match '(?i)vRealize Operations Manager|vRealize Operations Adapter|Aria Operations' } | Select-Object -First 1 if (-not $selfMonitoring) { return $null } return $selfMonitoring.ResourceKey.AdapterKindKey } function Get-VcfCheckAriaOpsResourceStats { <# .SYNOPSIS Thin, mockable wrapper around Invoke-VcfOpsGetLatestStatsOfResources (see AriaOpsHelpers.ps1's file header for why this wrapper exists). .DESCRIPTION Returns every stat Aria Operations currently reports for the given resource id(s) - no -StatKey filter is passed, since the exact stat key names for CPU/memory/disk/network/ object-count self-monitoring metrics have not been confirmed live against a real instance (see ARIA_OPS_CONNECTOR_PLAN.md). Callers filter the returned stats by name pattern instead of relying on hardcoded key strings. The response object's per-resource stat collection is exposed as its 'Values' property, each entry's 'StatList' property being a wrapper object (not itself enumerable) whose 'Stat' property holds one Stats object per reported metric (StatKey.Key/Data/Timestamps), per the generated SDK model. .PARAMETER Connection The Aria Operations connection object returned by Connect-VcfCheckAriaOps or Connect-VcfCheckAriaOpsEndpoint. .PARAMETER ResourceId Array of resource ids to fetch the latest stats for. .OUTPUTS The StatsOfResources response object returned by Invoke-VcfOpsGetLatestStatsOfResources. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSObject]$Connection, [Parameter(Mandatory = $true)] [Guid[]]$ResourceId ) return Invoke-VcfOpsGetLatestStatsOfResources -Server $Connection -ResourceId $ResourceId -CurrentOnly $true -ErrorAction Stop } |