Functions/Private/Search-ObjectProperty.ps1

function Search-ObjectProperty {
    <#
        .SYNOPSIS
            Searches PSCustomObject(s) for a specified search pattern, returning a list of matching properties.
 
        .DESCRIPTION
            Search-ObjectProperty searches PSCustomObject(s) for a specified search pattern, returning a list of matching properties.
 
        .PARAMETER InputObject
            The object(s) to search.
 
        .PARAMETER Pattern
            The pattern to use when searching. Regular expressions are supported.
 
        .PARAMETER Depth
            The number of layers (sub-objects/properties) in every object to search. Using a search depth that is too deep can significantly increase search time.
 
        .PARAMETER CurrentDepth
            Do not use. This parameter is used internally when searching recursively to keep track of the current depth in the object.
 
        .PARAMETER ParentProperty
            Do not use. This parameter is used internally when searching recursively to keep track of the full path to a matching property.
 
        .LINK
            https://github.com/AutomatePS/AutomatePS
    #>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        $InputObject,

        [string]$Pattern,

        [switch]$Regex = $false,

        [ValidateScript({$_ -gt 0})]
        [int]$Depth = 3,

        [int]$CurrentDepth = 0,

        [ValidateNotNullOrEmpty()]
        [string]$ParentProperty = ""
    )
    BEGIN {
        $result = @()
        $index = 0
        if ($Regex.ToBool()) {
            try {
                [regex]::new($Pattern)
            } catch {
                throw "$Pattern is not a valid regular expression!"
            }
        } elseif ([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($Pattern)) {
            try   { "" -like $Pattern | Out-Null } # Test wildcard string
            catch { throw }                        # Throw error if wildcard invalid
        }
    }

    PROCESS {
        foreach ($obj in $InputObject) {
            $found = $false
            $matchedProps = @{}

            foreach ($property in ($obj | Get-Member -MemberType Property | Where-Object {$_.Name -notin "MatchedProperty"}).Name) {
                if ($ParentProperty -eq "") {
                    $propertyChain = ".$property"
                } else {
                    $propertyChain = "$ParentProperty[$index].$property"
                }
                if ($obj."$property" -is [System.Collections.IEnumerable] -and $obj."$property" -isnot [string]) {
                    if ($CurrentDepth -le $Depth) {
                        #Write-Verbose "Searching sub-object $ParentProperty.$property"
                        $subProperties = $obj."$property" | Search-ObjectProperty -Pattern $Pattern -ParentProperty $propertyChain -Regex:$Regex -Depth $Depth -CurrentDepth ($CurrentDepth + 1)
                        foreach ($subProperty in $subProperties) {
                            $matchedProps += $subProperty.MatchedProperty
                            $found = $true
                        }
                    }
                } else {
                    $matched = $false
                    if ($Regex.ToBool()) {
                        $matched = $obj."$property" -match $Pattern
                    } else {
                        $matched = $obj."$property" -like $Pattern
                    }
                    if ($matched) {
                        Write-Verbose "Found '$Pattern' in $propertyChain"
                        $matchedProps.Add("$propertyChain", $obj."$property") | Out-Null
                        $found = $true
                    }
                }
            }
            if ($found) {
                if ($null -eq ($obj | Get-Member -Name "MatchedProperty")) {
                    $obj | Add-Member -NotePropertyName "MatchedProperty" -NotePropertyValue $matchedProps
                } else {
                    $obj.MatchedProperty = $matchedProps
                }
                $result += $obj
            }
            $index += 1
        }
    }

    END {
        return $result
    }
}