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 is deployed and lifecycle-managed by Aria Suite Lifecycle (VRSLCM), but VRSLCM # does not hold the Aria Operations credential either - Aria Operations manages its own # credentials via its own API. SDDC Manager's own credential vault never holds an Aria Operations # credential, so this file has no SDDC-Manager-based resolution path at all. # Get-VcfCheckEnvironmentAriaOpsEndpoints resolves the user-declared Integrations list on an # environment (Private/Environments.ps1) as the only source # of Aria Operations FQDN/credential. Connect-VcfCheckAriaOpsEndpoint connects to one of those # directly by FQDN + credential. # # 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-VcfCheckAriaOpsConnectionFailureCategory { <# .SYNOPSIS Classifies a Connect-VcfCheckAriaOpsEndpoint failure message so the caller can show an accurate, specific remediation instead of one generic "check your network" message for every failure. .DESCRIPTION A rejected credential (bad password) surfaces from Connect-VcfOpsServer as an ordinary caught exception, indistinguishable at a glance from a real network problem. Left unclassified, it was being reported as an "Error" result telling the user to verify network connectivity - misleading when the endpoint is reachable and the only problem is the entered credential. This inspects the exception message for a recognized credential/ authorization rejection signature and returns 'AuthenticationFailed', else 'Unknown'. .PARAMETER ErrorMessage The exception message from a failed Connect-VcfCheckAriaOpsEndpoint call. .OUTPUTS [String] one of 'AuthenticationFailed', 'Unknown'. .EXAMPLE Get-VcfCheckAriaOpsConnectionFailureCategory -ErrorMessage $_.Exception.Message #> [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$ErrorMessage ) if ($ErrorMessage -match '(?i)UNAUTHORIZED|not authorized|invalid credentials|incorrect user ?name or password|authentication failed|401\b') { return 'AuthenticationFailed' } return 'Unknown' } 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. When the integration has EnableGuestOsChecks set, also resolves VCenterFqdn/ VCenterUsername for the vCenter that hosts this endpoint - the integration-level AriaVCenterFqdn/AriaVCenterUsername when AriaVCenterSharedAcrossEndpoints is set, else the endpoint's own VCenterFqdn/VCenterUsername. Both are left $null when guestOS checks are not enabled, so Get-VcfCheckAllVCenterFqdns' dedup loop skips the endpoint. When guestOS checks are enabled, not shared, and the endpoint has no VCenterFqdn of its own, both are left $null and a warning is logged. .PARAMETER Environment An environment object as returned by Get-VcfCheckEnvironments. .OUTPUTS [Object[]] of PSCustomObject with Name, Fqdn, Username, VCenterFqdn, VCenterUsername, VmNames. VmNames is the user-declared, comma-delimited list of guestOS VM names for this endpoint - a single Aria appliance is not always one VM, and its VM name(s) cannot be assumed to match the FQDN shortname, so it is entered explicitly rather than derived. 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 } $vCenterFqdn = $null $vCenterUsername = $null if ($integration.EnableGuestOsChecks) { if ($integration.AriaVCenterSharedAcrossEndpoints) { $vCenterFqdn = $integration.AriaVCenterFqdn $vCenterUsername = $integration.AriaVCenterUsername } else { $vCenterFqdn = $endpoint.VCenterFqdn $vCenterUsername = $endpoint.VCenterUsername if ([String]::IsNullOrWhiteSpace($vCenterFqdn)) { Write-LogMessage -Type WARNING -Message "GuestOS checks are enabled for Aria Operations endpoint '$($endpoint.Name)' but no VCenterFqdn is configured; skipping guestOS checks for this endpoint." } } } $results.Add([PSCustomObject]@{ PSTypeName = 'VcfCheck.AriaOpsEndpoint' Name = $endpoint.Name Fqdn = $endpoint.Fqdn Username = $username VCenterFqdn = $vCenterFqdn VCenterUsername = $vCenterUsername VmNames = @(if ($endpoint.VmNames) { $endpoint.VmNames } else { @() }) }) } } 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 Connects to an Aria Operations instance declared on the environment (see Get-VcfCheckEnvironmentAriaOpsEndpoints) - the only source of Aria Operations FQDN/ credential, since SDDC Manager never holds one. Runs a 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) { $category = Get-VcfCheckAriaOpsConnectionFailureCategory -ErrorMessage $_.Exception.Message $reason = switch ($category) { 'AuthenticationFailed' { "Authentication failed for Aria Operations `"$Fqdn`". Verify the username and password entered for this endpoint are correct." } default { "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 - every standalone endpoint declared on the current environment - and connects to each. .DESCRIPTION Every Test-VcfAriaOps* check calls this once instead of Connect-VcfCheckAriaOpsEndpoint directly, so a single check fans out across every declared Aria Operations instance. SDDC Manager never holds an Aria Operations credential (it is lifecycle-managed by Aria Suite Lifecycle, not SDDC Manager), so the environment's declared Integrations endpoints are the only source of targets. A connection failure for one target (unreachable, bad credential) 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. .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) - pass it to the REST-fallback helpers (Get-VcfCheckAriaOpsResource, Get-VcfCheckAriaOpsAdapterResources) so those calls target the right instance. Empty array if the environment has no Aria Operations Integration 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() foreach ($endpoint in @($Context.AriaOpsEndpoints) | Where-Object { $_ }) { $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-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-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 from a target's CredentialInfo (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 from a target's CredentialInfo (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-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 (a target's cached credential info from Get-VcfCheckAriaOpsTargets), 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 from a target's CredentialInfo (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) 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 from a target's CredentialInfo (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-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 from a target's CredentialInfo (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 from a target's CredentialInfo (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-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-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-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-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 } |