Public/Reconnaissance/anonymous/Get-PublicStorageAccountList.ps1

function Get-PublicStorageAccountList {
    [cmdletbinding()]
    [OutputType([System.Collections.ArrayList])] # Updated OutputType
    [Alias("bl cli public storage accounts")]
    param (
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [Alias("storage-account-name")]
        [string]$StorageAccountName,

        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [ValidateSet('blob', 'file', 'queue', 'table', 'dfs', ErrorMessage = "Type must be one of the following: Blob, File, Queue, Table")]
        [Alias("storage-type")]
        [string]$Type = 'blob',

        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [Alias("word-list")]
        [string]$WordList,

        [Parameter(Mandatory = $false)]
        [Alias("throttle-limit")]
        [int]$ThrottleLimit = 1000,

        [Parameter(Mandatory = $false)]
        [Alias("include-empty")]
        [switch]$IncludeEmpty,

        [Parameter(Mandatory = $false)]
        [Alias("include-metadata")]
        [switch]$IncludeMetadata
    )

    begin {
        Write-Verbose "Starting function $($MyInvocation.MyCommand.Name)"

        # Create thread-safe collections
        $validDnsNames = [System.Collections.Concurrent.ConcurrentBag[string]]::new()
        $userAgent = ($sessionVariables.userAgents.agents | Get-Random).value
        $result = New-Object System.Collections.ArrayList
    }

    process {
        try {
            # Read word list efficiently
            if ($WordList) {
                $permutations = [System.Collections.Generic.HashSet[string]](Get-Content $WordList)
                Write-Information "$($MyInvocation.MyCommand.Name): Loaded $($permutations.Count) permutations from '$WordList'" -InformationAction Continue
            }

            $permutations += $sessionVariables.permutations
            Write-Information "$($MyInvocation.MyCommand.Name): Loaded $($permutations.Count) permutations from session"  -InformationAction Continue

            # Generate DNS names more efficiently
            $dnsNames = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
            foreach ($item in $permutations) {
                [void] $dnsNames.Add(('{0}{1}.{2}.core.windows.net' -f $StorageAccountName, $($item), $type))
                [void] $dnsNames.Add(('{1}{0}.{2}.core.windows.net' -f $StorageAccountName, $($item), $type))
            }
            [void] $dnsNames.Add(('{0}.{1}.core.windows.net' -f $StorageAccountName, $type))

            $totalDns = $dnsNames.Count
            Write-Information "$($MyInvocation.MyCommand.Name): Starting DNS resolution for $totalDns names..."  -InformationAction Continue

            # Parallel DNS resolution with improved error handling and progress
            $dnsNames | ForEach-Object -Parallel {
                try {
                    $validDnsNames = $using:validDnsNames
                    $permutations = $using:permutations

                    if ([System.Net.Dns]::GetHostEntry($_)) {
                        $validDnsNames.Add($_)
                        $permutations += $($_).split('.')[0]
                    }
                }
                catch [System.Net.Sockets.SocketException] {
                    Write-Information "$($MyInvocation.MyCommand.Name): Storage Account '$_' does not exist"  -InformationAction Continue
                }
            } -ThrottleLimit $ThrottleLimit

            # Generate and test URIs in parallel
            if ($validDnsNames.Count -gt 0) {
                Write-Information "$($MyInvocation.MyCommand.Name): Found $($validDnsNames.Count) valid DNS names"  -InformationAction Continue
                $totalContainers = $validDnsNames.Count * $permutations.Count
                Write-Information "$($MyInvocation.MyCommand.Name): Starting container checks for $totalContainers combinations..."  -InformationAction Continue

                $validDnsNames | ForEach-Object -Parallel {
                    $dns             = $_
                    $permutations    = $using:permutations
                    $result          = $using:result
                    $includeEmpty    = $using:IncludeEmpty
                    $IncludeMetadata = $using:IncludeMetadata
                    $userAgent       = $using:userAgent

                    $permutations | ForEach-Object -Parallel {
                        $dns             = $using:dns
                        $result          = $using:result
                        $includeEmpty    = $using:IncludeEmpty
                        $IncludeMetadata = $using:IncludeMetadata
                        $userAgent       = $using:userAgent

                        $uri = "https://$dns/$_/?restype=container&comp=list"
                        $response = Invoke-WebRequest -Uri $uri -Method GET -UserAgent $userAgent -UseBasicParsing -SkipHttpErrorCheck

                        if ($response.StatusCode -eq 200) {
                            if ($includeEmpty -or $response.Content -match '<Blob>') {
                                $currentItem = [PSCustomObject]@{
                                    "StorageAccountName" = $dns.split('.')[0]
                                    "Container"          = $_
                                    "FileCount" = (Select-String -InputObject $response.Content -Pattern "/Name" -AllMatches).Matches.Count
                                }
                                if ($response.Content -match '<Blob>') {
                                    $currentItem | Add-Member -NotePropertyName IsEmpty -NotePropertyValue $false
                                }
                                else {
                                    $currentItem | Add-Member -NotePropertyName IsEmpty -NotePropertyValue $true
                                }
                                $currentItem | Add-Member -NotePropertyName Uri -NotePropertyValue $uri

                            }

                            if ($IncludeMetadata) {
                                $metadataUri = "https://$dns/$_/?restype=container&comp=metadata"
                                $metaResponse = Invoke-WebRequest -Uri $metadataUri -Method GET -UserAgent $userAgent -UseBasicParsing -SkipHttpErrorCheck

                                $metaHeaders = @{}
                                $metaResponse.Headers.GetEnumerator() | Where-Object { $_.Key -like 'x-ms-meta-*' } | ForEach-Object {
                                    $metaHeaders[$_.Key] = $_.Value
                                    if ($metaHeaders) {$currentItem | Add-Member -NotePropertyName Metadata -NotePropertyValue $metaHeaders -Force}
                                }
                            }

                            [void] $result.Add($currentItem)
                        }
                    }
                }
            }
        }
        catch {
            Write-Error -Message $_.Exception.Message -ErrorAction Continue
        }
    }

    end {
        Write-Progress -Activity "Resolving DNS Names" -Completed
        Write-Progress -Activity "Checking Containers" -Completed
        Write-Verbose "Function $($MyInvocation.MyCommand.Name) completed"
        if (-not($result) -or $result.Count -eq 0) {
            Write-Information -MessageData "No public storage account containers found" -InformationAction Continue
        } else {
            return $result
        }
    }
}