Private/New-AzureUtilsInsecureConfigQuery.ps1

function New-AzureUtilsInsecureConfigQuery {
    <#
        .SYNOPSIS
            Builds the Resource Graph (KQL) query for insecure resource configuration.
        .DESCRIPTION
            Pure, side-effect-free. Returns a union of the selected hardening checks,
            each projecting the same columns plus a 'finding' explaining the weakness.
            Focuses on configuration weaknesses (weak TLS, no HTTPS enforcement, no
            purge protection) rather than public exposure, which is covered by
            Find-AzureUtilsPublicResource.
        .PARAMETER Type
            Which insecure-config checks to include. Defaults to all.
        .OUTPUTS
            System.String (the KQL query)
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [ValidateSet('StorageInsecureTransfer', 'StorageWeakTls', 'WebAppHttpsOnly', 'KeyVaultNoPurgeProtection')]
        [string[]] $Type = @('StorageInsecureTransfer', 'StorageWeakTls', 'WebAppHttpsOnly', 'KeyVaultNoPurgeProtection')
    )

    $cols = 'id, name, type, resourceGroup, location, subscriptionId, tags, finding'
    $subQueries = [System.Collections.Generic.List[string]]::new()

    if ($Type -contains 'StorageInsecureTransfer') {
        $subQueries.Add(@"
resources
| where type =~ 'microsoft.storage/storageaccounts' and properties.supportsHttpsTrafficOnly == false
| extend finding = 'Storage allows non-HTTPS traffic (supportsHttpsTrafficOnly = false)'
| project $cols
"@
)
    }

    if ($Type -contains 'StorageWeakTls') {
        $subQueries.Add(@"
resources
| where type =~ 'microsoft.storage/storageaccounts' and tostring(properties.minimumTlsVersion) in~ ('TLS1_0', 'TLS1_1')
| extend finding = strcat('Storage minimum TLS version is ', tostring(properties.minimumTlsVersion))
| project $cols
"@
)
    }

    if ($Type -contains 'WebAppHttpsOnly') {
        $subQueries.Add(@"
resources
| where type =~ 'microsoft.web/sites' and properties.httpsOnly == false
| extend finding = 'Web app does not enforce HTTPS-only'
| project $cols
"@
)
    }

    if ($Type -contains 'KeyVaultNoPurgeProtection') {
        # enablePurgeProtection is absent (null) or false when protection is off.
        $subQueries.Add(@"
resources
| where type =~ 'microsoft.keyvault/vaults' and properties.enablePurgeProtection != true
| extend finding = 'Key Vault purge protection is disabled'
| project $cols
"@
)
    }

    if ($subQueries.Count -eq 1) {
        return $subQueries[0]
    }

    # Azure Resource Graph requires the query to start with a table, then chain
    # '| union (subquery)' for the remaining checks.
    $query = $subQueries[0]
    for ($i = 1; $i -lt $subQueries.Count; $i++) {
        $query += "`n| union (`n$($subQueries[$i])`n)"
    }
    return $query
}