Private/Collect/Get-TcAzureCollector.ps1

function Get-TcAzureCollector {
    <#
    .SYNOPSIS
    Lists the Azure collectors, in the order they run.
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param()

    @(
        @{
            Id       = 'AZR-01'
            Workload = 'Azure'
            Title    = 'Subscriptions, resources, RBAC and Azure Virtual Desktop'
            Service  = 'Azure'
            Collect  = {
                param($Role)
                # A separate process: Az.Accounts and Microsoft.Graph load different versions of MSAL.
                $tenant = $script:TcTenants[$Role]
                $code = @'
param($TenantId, $OutFile, $ClientId, $Thumbprint)
$ErrorActionPreference = 'Stop'
Import-Module Az.Accounts, Az.Resources
if ($ClientId) { Connect-AzAccount -ServicePrincipal -ApplicationId $ClientId -CertificateThumbprint $Thumbprint -Tenant $TenantId -WarningAction SilentlyContinue | Out-Null }
else { Connect-AzAccount -Tenant $TenantId -WarningAction SilentlyContinue | Out-Null }
$result = [ordered]@{ subscriptions = @(); resources = @(); errors = @() }
foreach ($subscription in @(Get-AzSubscription -TenantId $TenantId -WarningAction SilentlyContinue)) {
    $assignments = $null
    try {
        Set-AzContext -SubscriptionId $subscription.Id -Tenant $TenantId -WarningAction SilentlyContinue | Out-Null
        foreach ($group in @(Get-AzResource) | Group-Object ResourceType) {
            $result.resources += [ordered]@{ Subscription = $subscription.Name; Type = $group.Name; Count = $group.Count; Regions = ((@($group.Group | ForEach-Object Location) | Sort-Object -Unique) -join ', ') }
        }
        $assignments = @(Get-AzRoleAssignment -Scope "/subscriptions/$($subscription.Id)" -WarningAction SilentlyContinue).Count
    }
    catch { $result.errors += "Subscription $($subscription.Name): $($_.Exception.Message)" }
    $result.subscriptions += [ordered]@{ Name = $subscription.Name; Id = $subscription.Id; State = [string]$subscription.State; Assignments = $assignments }
}
$result | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $OutFile -Encoding UTF8
'@

                $file = Join-Path ([System.IO.Path]::GetTempPath()) ('tc-azure-{0}.ps1' -f [guid]::NewGuid())
                $json = "$file.json"
                $auth = Get-TcApplicationAuth -Tenant $tenant
                try {
                    Set-Content -LiteralPath $file -Value $code -Encoding UTF8
                    $arguments = @('-NoProfile', '-File', $file, '-TenantId', $tenant['tenantId'], '-OutFile', $json)
                    # With the application there is never a window: without the Reader role it just sees no subscription.
                    if ($auth) {
                        $arguments += @('-ClientId', $auth['clientId'], '-Thumbprint', $auth['thumbprint'])
                        $how = 'read-only application, no window'
                    }
                    else {
                        Assert-TcInteractiveAllowed -Service 'Azure' -Domain $tenant['primaryDomain']
                        $how = 'sign-in window'
                    }
                    Write-TcMessage -Message "Azure connects in a separate process ($how)."
                    & pwsh @arguments | Out-Null
                    if (-not (Test-Path -LiteralPath $json)) { throw 'No Azure result: the sign-in was cancelled or there is no access to subscriptions.' }
                    $result = Read-TcJsonFile -Path $json
                }
                finally {
                    Remove-Item -LiteralPath $file, $json -ErrorAction SilentlyContinue
                }

                foreach ($problem in @(Get-TcProperty $result 'errors' @())) { Add-TcCollectorWarning 'Azure' $problem }

                $subscriptions = @(Get-TcProperty $result 'subscriptions' @())
                if ($subscriptions.Count -eq 0) {
                    # No subscription visible cannot be told from no subscription, so this never reads as a service not used.
                    $reason = if (Test-TcApplicationGrant -Tenant $tenant -Grant 'Azure') { 'The read-only application sees no subscription, although it received the Reader role on the visible ones.' }
                    elseif ($auth) { 'The read-only application sees no subscription: it has no Azure role.' }
                    else { 'No subscription is visible: the tenant has none, or the account has no Azure role.' }
                    throw "NOTCONFIGURED: $reason"
                }

                Add-TcTable 'azureSubscriptions' @(foreach ($subscription in $subscriptions) {
                        [ordered]@{ 'Name' = $subscription['Name']; 'Id' = $subscription['Id']; 'State' = $subscription['State']; 'RBAC assignments' = $subscription['Assignments'] }
                    })
                Add-TcTable 'azureResources' @(foreach ($resource in @(Get-TcProperty $result 'resources' @())) {
                        [ordered]@{ 'Subscription' = $resource['Subscription']; 'Type' = $resource['Type']; 'Count' = $resource['Count']; 'Regions' = $resource['Regions'] }
                    })
            }
        }
    )
}