Modules/Private/Main/Get-AZTICollectedValue.ps1

<#
.Synopsis
Reads one named value from every element of a collection, safely under StrictMode.

.DESCRIPTION
PowerShell's member enumeration ($collection.Name) is unsafe under
Set-StrictMode -Version Latest: when the enumeration yields nothing at all, PowerShell
reports the property as missing and throws

    The property 'Name' cannot be found on this object. Verify that the property exists.

A $null value on every element is fine - enumeration still yields one $null per element.
An EMPTY COLLECTION on every element is not: those flatten away, the overall result is
empty, and the read throws even though every element carries the key.

That distinction matters because an empty collection is a perfectly normal Azure API
response. A subscription with no reservation recommendations returns { "value": [] }, so a
healthy tenant was crashing the inventory run (AB#5633).

This function reads each element explicitly instead, and emits nothing when there is
nothing to emit - so `$Resources += Get-AZSCCollectedValue ...` adds nothing rather than
throwing. It handles both dictionary elements (the API result hashtables) and objects.

.PARAMETER InputObject
The collection to read from. $null and empty collections are accepted and yield nothing.

.PARAMETER Name
The key or property name to read from each element.

.EXAMPLE
$Resources += Get-AZSCCollectedValue -InputObject $APIResults -Name 'ReservationRecomen'

.COMPONENT
This PowerShell Module is part of Azure Scout (AZSC).
#>

function Get-AZSCCollectedValue {
    [CmdletBinding()]
    Param(
        [Parameter(Position = 0)]
        [AllowNull()]
        $InputObject,

        [Parameter(Mandatory, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    if ($null -eq $InputObject) { return }

    foreach ($Element in @($InputObject)) {
        if ($null -eq $Element) { continue }

        if ($Element -is [System.Collections.IDictionary]) {
            # .Contains rather than .ContainsKey: IDictionary guarantees the former.
            if ($Element.Contains($Name)) { $Element[$Name] }
            continue
        }

        # PSObject.Properties lookup never throws for an absent member, unlike $Element.$Name.
        $Property = $Element.PSObject.Properties[$Name]
        if ($Property) { $Property.Value }
    }
}