Private/Get-AACLegacyAuthenticationQuery.ps1

function Get-AACLegacyAuthenticationQuery {
    <#
    .SYNOPSIS
        The queries behind Get-AACLegacyAuthentication: Azure Resource Graph
        for the resources that still accept keys, passwords or old TLS, and
        Microsoft Graph for legacy-protocol sign-ins and the tenant settings
        that allow weak or legacy authentication.
    .DESCRIPTION
        Returns @{ Graph (Resource Graph queries, an ordered hashtable);
        Entra (Microsoft Graph URIs, name -> URI or @{ Uri; MaxItems });
        Protocols (the legacy clientAppUsed values) }.
 
        Resource Graph:
          storage shared key access, minimum TLS
          sql Entra-only authentication, minimum TLS
          postgres password authentication (flexible servers)
          localAuth disableLocalAuth on Cosmos DB, Service Bus, Event
                        Hubs, Relay, Event Grid, SignalR, Web PubSub, App
                        Configuration, Azure AI services, AI Search,
                        Automation, Log Analytics, Application Insights,
                        IoT Hub; Batch's authentication modes
          registries the admin user
          redis access keys, the non-TLS port, minimum TLS
          aks local accounts, Entra integration
          vms Linux password authentication
          keyVaults access policies instead of RBAC
          sites App Service and Functions apps (their publishing
                        credentials are read per app)
        Microsoft Graph (-SignInDays of sign-ins):
          signIns|<protocol> sign-ins per legacy protocol (up to 500)
          conditionalAccess the policies (do any block legacy auth?)
          securityDefaults on or off
          applications implicit grant, public client flows, secrets
          authMethods the authentication methods policy (SMS, voice)
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [string[]] $ResourceGroupName,

        [ValidateRange(1, 30)]
        [int] $SignInDays = 7,

        [datetime] $Now = [datetime]::UtcNow
    )

    $quote = { param([string] $Text) "'" + ($Text -replace "'", "\'") + "'" }
    $groups = @($ResourceGroupName | Where-Object { $_ })
    $in = if ($groups.Count) { " | where resourceGroup in~ ($((@($groups | ForEach-Object { & $quote $_ })) -join ', '))" } else { '' }
    $base = { param([string] $Types, [string] $Extra) "resources | where type in~ ($Types)$in | project id = tolower(id), name, type = tolower(type), resourceGroup, subscriptionId, location, $Extra" }
    $localTypes = @(
        'microsoft.documentdb/databaseaccounts', 'microsoft.servicebus/namespaces', 'microsoft.eventhub/namespaces', 'microsoft.relay/namespaces', 'microsoft.eventgrid/topics', 'microsoft.eventgrid/domains',
        'microsoft.signalrservice/signalr', 'microsoft.signalrservice/webpubsub', 'microsoft.appconfiguration/configurationstores', 'microsoft.cognitiveservices/accounts', 'microsoft.search/searchservices',
        'microsoft.automation/automationaccounts', 'microsoft.operationalinsights/workspaces', 'microsoft.insights/components', 'microsoft.devices/iothubs', 'microsoft.batch/batchaccounts'
    ) | ForEach-Object { "'$_'" }

    $graph = [ordered]@{
        storage    = & $base "'microsoft.storage/storageaccounts'" 'sharedKey = tostring(properties.allowSharedKeyAccess), tls = tostring(properties.minimumTlsVersion)'
        sql        = & $base "'microsoft.sql/servers'" 'entraOnly = tostring(properties.administrators.azureADOnlyAuthentication), tls = tostring(properties.minimalTlsVersion)'
        postgres   = & $base "'microsoft.dbforpostgresql/flexibleservers'" 'password = tostring(properties.authConfig.passwordAuth), entra = tostring(properties.authConfig.activeDirectoryAuth)'
        localAuth  = & $base ($localTypes -join ', ') 'disabled = coalesce(tostring(properties.disableLocalAuth), tostring(properties.features.disableLocalAuth), tostring(properties.DisableLocalAuth)), modes = properties.allowedAuthenticationModes, kind'
        registries = & $base "'microsoft.containerregistry/registries'" 'admin = tostring(properties.adminUserEnabled)'
        redis      = & $base "'microsoft.cache/redis'" 'nonSsl = tostring(properties.enableNonSslPort), keysOff = tostring(properties.disableAccessKeyAuthentication), tls = tostring(properties.minimumTlsVersion)'
        aks        = & $base "'microsoft.containerservice/managedclusters'" 'localOff = tostring(properties.disableLocalAccounts), entra = isnotnull(properties.aadProfile)'
        vms        = & $base "'microsoft.compute/virtualmachines'" 'passwordOff = tostring(properties.osProfile.linuxConfiguration.disablePasswordAuthentication), linux = isnotnull(properties.osProfile.linuxConfiguration)'
        keyVaults  = & $base "'microsoft.keyvault/vaults'" 'rbac = tostring(properties.enableRbacAuthorization)'
        sites      = & $base "'microsoft.web/sites'" 'kind, state = tostring(properties.state)'
    }

    $v1 = 'https://graph.microsoft.com/v1.0'
    $since = $Now.AddDays(-$SignInDays).ToString('yyyy-MM-ddTHH:mm:ssZ', [cultureinfo]::InvariantCulture)
    $protocols = @('Exchange ActiveSync', 'IMAP4', 'POP3', 'Authenticated SMTP', 'Exchange Web Services', 'MAPI Over HTTP', 'Outlook Anywhere (RPC over HTTP)', 'Autodiscover', 'Exchange Online PowerShell', 'Other clients')
    $entra = [ordered]@{}
    foreach ($p in $protocols) {
        $filter = [System.Uri]::EscapeDataString("createdDateTime ge $since and clientAppUsed eq '$p'")
        $entra["signIns|$p"] = @{ Uri = "$v1/auditLogs/signIns?`$filter=$filter&`$select=userPrincipalName,clientAppUsed,status,createdDateTime,ipAddress,appDisplayName&`$top=500"; MaxItems = 500 }
    }
    $entra['conditionalAccess'] = "$v1/identity/conditionalAccess/policies"
    $entra['securityDefaults'] = "$v1/policies/identitySecurityDefaultsEnforcementPolicy"
    $entra['applications'] = "$v1/applications?`$select=id,appId,displayName,passwordCredentials,keyCredentials,web,isFallbackPublicClient&`$top=999"
    $entra['authMethods'] = "$v1/policies/authenticationMethodsPolicy"

    @{ Graph = $graph; Entra = $entra; Protocols = $protocols }
}