Private/Read-AACApplicationGatewayMap.ps1

function Read-AACApplicationGatewayMap {
    <#
    .SYNOPSIS
        Reads the Application Gateways in a scope, what they refer to and
        their backend health, and builds the gateway map's model. Shared by
        Show-AACApplicationGatewayMap and Export-AACDiagram.
    .DESCRIPTION
        Runs inside Invoke-AACProgress (it reports its steps there). Reads
        Resource Graph: the gateways, then the public IPs, WAF policies,
        networks, NICs and App Services they refer to; then backend health
        from metrics, or the live check with -IncludeHealth.
        Returns @{ Map (ConvertTo-AACApplicationGatewayMap's result); Title;
        Facts; Gateways (the gateway rows) }.
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [string[]] $SubscriptionId = @(),

        [string[]] $ResourceGroupName = @(),

        [string[]] $Name = @(),

        [switch] $IncludeHealth,

        [int] $CertificateDays = 30,

        [string] $Title
    )

    $SubscriptionId = @($SubscriptionId | Where-Object { $_ })
    $Name = @($Name | Where-Object { $_ })
    Update-AACProgress -Id 'read' -Indeterminate -Description 'Reading the Application Gateways (Resource Graph)'
    $read = Invoke-AACGraphBatch -Query (Get-AACApplicationGatewayQuery -ResourceGroupName @($ResourceGroupName | Where-Object { $_ })) -SubscriptionId $SubscriptionId
    $gateways = @($read.Rows['gateways'] | Where-Object { $n = [string]$_['name']; $null -ne $_ -and (-not $Name.Count -or @($Name | Where-Object { $n -like $_ }).Count) })
    $names = @{}
    foreach ($s in @($read.Rows['subscriptions'])) { if ($s) { $names[[string]$s['subscriptionId']] = [string]$s['name'] } }
    # What the gateways refer to: public IPs, WAF policies, networks, NICs, App Services.
    $ids = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    $hosts = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    foreach ($g in $gateways) {
        $text = ConvertTo-Json -InputObject $g['properties'] -Depth 30 -Compress
        foreach ($m in [regex]::Matches($text, '(?i)"id":"(/subscriptions/[^"]+/providers/microsoft\.network/(publicipaddresses|applicationgatewaywebapplicationfirewallpolicies|networkinterfaces)/[^/"]+)')) { [void]$ids.Add($m.Groups[1].Value) }
        foreach ($m in [regex]::Matches($text, '(?i)"id":"(/subscriptions/[^"]+/providers/microsoft\.network/virtualnetworks/[^/"]+)/subnets/')) { [void]$ids.Add($m.Groups[1].Value) }
        foreach ($m in [regex]::Matches($text, '"fqdn":"([^"]+)"')) { [void]$hosts.Add($m.Groups[1].Value) }
    }
    $related = @()
    $sites = @()
    if ($ids.Count -or $hosts.Count) {
        $relatedRead = Invoke-AACGraphBatch -Query (Get-AACApplicationGatewayQuery -Stage Related -Id @($ids) -HostName @($hosts)) -AllowFailure @('sites')
        foreach ($key in @($relatedRead.Rows.Keys)) { if ($key -eq 'sites') { $sites = @($relatedRead.Rows[$key]) } else { $related += @($relatedRead.Rows[$key]) } }
    }
    Update-AACProgress -Id 'read' -Complete -Description ('Read {0:N0} gateway(s) and {1:N0} resource(s) they use' -f $gateways.Count, (@($related).Count + @($sites).Count))

    Update-AACProgress -Id 'health' -Indeterminate -Description $(if ($IncludeHealth) { 'Running the live backend health check (30-90 seconds per gateway, all at once)' } else { 'Reading backend health metrics' })
    $health = Read-AACApplicationGatewayHealth -GatewayId @($gateways | ForEach-Object { ([string]$_['id']).ToLowerInvariant() }) -Live:$IncludeHealth
    Update-AACProgress -Id 'health' -Complete -Description $(if ($IncludeHealth) { 'Live backend health read for {0:N0} of {1:N0} gateway(s)' -f $health.Live.Count, $gateways.Count } else { 'Read backend health metrics' })

    $map = ConvertTo-AACApplicationGatewayMap -Gateway $gateways -Related $related -Site $sites -SubscriptionName $names -Health $health -CertificateDays $CertificateDays
    $facts = @(
        '{0:N0} gateway(s)' -f $map.Stats.Gateways
        '{0:N0} listener(s)' -f $map.Stats.Listeners
        '{0:N0} rule(s)' -f $map.Stats.Rules
        '{0:N0} pool(s)' -f $map.Stats.Pools
        '{0:N0} target(s)' -f $map.Stats.Targets
        if ($map.Stats.Unhealthy) { '{0:N0} unhealthy' -f $map.Stats.Unhealthy }
        '{0:N0} finding(s)' -f $map.Stats.Findings
        "health from $(if ($IncludeHealth) { 'the live check' } else { 'metrics' })"
        "drawn $((Get-Date).ToString('d MMM yyyy HH:mm'))"
    )
    @{
        Map      = $map
        Title    = $(if ($Title) { $Title } else { "Application Gateways: $(if ($gateways.Count -le 3) { (@($gateways | ForEach-Object { $_['name'] }) -join ', ') } else { "$($gateways.Count) gateways" })" })
        Facts    = $facts
        Gateways = $gateways
    }
}