Public/Find-CloudResource.ps1

function Find-CloudResource {
    <#
        .SYNOPSIS
            Searches for cloud resources by name across providers and resource kinds.

        .DESCRIPTION
            Find-CloudResource performs a cross-kind, cross-cloud search for resources by name.
            Use this when you know a resource name but not whether it's a VM, disk, storage account,
            network, or function, or when you need to search multiple clouds simultaneously.

            Wildcards are supported in the -Name parameter.

        .EXAMPLE
            Find-CloudResource -Name 'payment-svc-03'

            Searches all providers and all resource kinds for 'payment-svc-03'.

        .EXAMPLE
            Find-CloudResource -Name 'prod-*' -Provider Azure, AWS

            Searches Azure and AWS for any resource starting with 'prod-'.

        .EXAMPLE
            Find-CloudResource -Name 'web-*' -Kind Instance, Network

            Searches for instances and networks with names starting with 'web-'.

        .EXAMPLE
            Find-CloudResource -Name '*test*' -Provider GCP -Kind Storage

            Searches GCP storage resources for names containing 'test'.
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        # The resource name to search for. Wildcards are supported.
        [Parameter(Mandatory, Position = 0)]
        [SupportsWildcards()]
        [string]$Name,

        # Limit search to specific providers. If not specified, searches all connected providers.
        [ValidateSet('Azure', 'AWS', 'GCP')]
        [string[]]$Provider,

        # Limit search to specific resource kinds. If not specified, searches all kinds.
        [ValidateSet('Instance', 'Disk', 'Storage', 'Network', 'Function')]
        [string[]]$Kind
    )

    process {
        # Resolve provider list
        $providersToSearch = if ($Provider) {
            $Provider | Where-Object { $script:PSCumulusContext.Providers[$_] }
        } else {
            @('Azure', 'AWS', 'GCP') | Where-Object { $script:PSCumulusContext.Providers[$_] }
        }

        # Resolve kind list
        $kindsToSearch = if ($Kind) { $Kind } else { @('Instance', 'Disk', 'Storage', 'Network', 'Function') }

        $results = [System.Collections.Generic.List[psobject]]::new()

        $azureRgCacheLoaded = $false

        try {
            foreach ($providerName in $providersToSearch) {
                foreach ($kindName in $kindsToSearch) {
                    $commandName = "Get-Cloud$kindName"
                    $scopeParameterSets = [System.Collections.Generic.List[hashtable]]::new()

                    # Add provider-specific scope parameters from context
                    $ctx = $script:PSCumulusContext.Providers[$providerName]
                    $skipProvider = $false

                    switch ($providerName) {
                        'Azure' {
                            if (-not $azureRgCacheLoaded) {
                                $azureRgCacheLoaded = $true
                                if (Get-Command Get-AzResourceGroup -ErrorAction SilentlyContinue) {
                                    $script:__PSCumulusFcrAzureRgCache = @(Get-AzResourceGroup -ErrorAction SilentlyContinue)
                                } else {
                                    $script:__PSCumulusFcrAzureRgCache = @()
                                }
                            }

                            if ($script:__PSCumulusFcrAzureRgCache.Count -gt 0) {
                                foreach ($rg in $script:__PSCumulusFcrAzureRgCache) {
                                    if (-not [string]::IsNullOrWhiteSpace($rg.ResourceGroupName)) {
                                        $scopeParameterSets.Add(@{ ResourceGroup = $rg.ResourceGroupName })
                                    }
                                }
                            } else {
                                Write-Verbose "Find-CloudResource: no resource groups returned for Azure subscription $($ctx.SubscriptionId); skipping."
                                $skipProvider = $true
                            }
                            break
                        }
                        'AWS' {
                            if ($ctx.Region) {
                                $scopeParameterSets.Add(@{ Region = $ctx.Region })
                            } else {
                                Write-Verbose "Find-CloudResource: no region found for AWS context; skipping."
                                $skipProvider = $true
                            }
                            break
                        }
                        'GCP' {
                            if ($ctx.Project) {
                                $scopeParameterSets.Add(@{ Project = $ctx.Project })
                            } else {
                                Write-Verbose "Find-CloudResource: no project found for GCP context; skipping."
                                $skipProvider = $true
                            }
                            break
                        }
                    }

                    if ($skipProvider) {
                        continue
                    }

                    foreach ($scopeParams in $scopeParameterSets) {
                        try {
                            $commandParams = @{ Provider = $providerName }
                            foreach ($key in $scopeParams.Keys) {
                                $commandParams[$key] = $scopeParams[$key]
                            }

                            $kindResults = & $commandName @commandParams -ErrorAction SilentlyContinue

                            if ($kindResults) {
                                foreach ($result in $kindResults) {
                                    # Add Kind property if not already present
                                    if (-not $result.PSObject.Properties.Match('Kind').Count) {
                                        $result | Add-Member -MemberType NoteProperty -Name 'Kind' -Value $kindName -Force
                                    }

                                    # Filter by name
                                    if ($result.Name -like $Name) {
                                        $results.Add($result)
                                    }
                                }
                            }
                        } catch {
                            Write-Verbose "Find-CloudResource: Failed to query $providerName $kindName`: $_"
                        }
                    }
                }
            }
        } finally {
            Remove-Variable -Scope Script -Name __PSCumulusFcrAzureRgCache -ErrorAction SilentlyContinue
        }

        $results
    }
}