Azure App Service & Function App Runtime Expiry Azure KQL

Azure App Service & Function App Runtime End-of-Support (EOS) 

Identifies Azure Web Apps and Function Apps running on deprecated, unsupported, or soon-to-expire runtimes across all subscriptions.

Description

This Azure Resource Graph (ARG) query parses microsoft.web/sites resources to extract runtime configurations (Node, Python, PHP, Java, .NET, PowerShell, and Azure Functions) from SiteConfig, LinuxFxVersion, and App Settings. It normalizes the extracted version data, compares it against a comprehensive, built-in End-of-Support (EOS) lifecycle matrix, and calculates the days until expiry. It filters for resources currently marked as "Out of Support" or "Review" and provides explicit upgrade recommendations for platform administrators.

Prerequisites

  • Target: Azure Resource Graph Explorer (ARG)
  • Required Tables: resources, resourcecontainers
  • Required Permissions: Reader access at the Subscription or Management Group level.

KQL Query


(
    resources
    | where type =~ "microsoft.web/sites"
    | extend SiteConfig = properties.siteConfig
    | extend AppType = case(kind contains "functionapp", "Function App", "Web App")
    | extend CheckType = case(AppType == "Function App", "Function Worker Runtime", "Web App Runtime")
    | extend LinuxFxVersionRaw = tostring(SiteConfig.linuxFxVersion)
    | extend PhpVersionRaw = tostring(SiteConfig.phpVersion)
    | extend PythonVersionRaw = tostring(SiteConfig.pythonVersion)
    | extend NodeVersionRaw = tostring(SiteConfig.nodeVersion)
    | extend JavaVersionRaw = tostring(SiteConfig.javaVersion)
    | extend NetFrameworkVersionRaw = tostring(SiteConfig.netFrameworkVersion)
    | extend PowerShellVersionRaw = tostring(SiteConfig.powerShellVersion)
    | extend LinuxRuntimeToken = toupper(tostring(split(LinuxFxVersionRaw, "|")[0]))
    | extend LinuxRuntimeValueRaw = tostring(split(LinuxFxVersionRaw, "|")[1])
    | extend LinuxRuntimeFamily =
        case(
            isempty(LinuxFxVersionRaw), "",
            LinuxRuntimeToken == "NODE", "Node",
            LinuxRuntimeToken == "PYTHON", "Python",
            LinuxRuntimeToken == "PHP", "PHP",
            LinuxRuntimeToken == "JAVA", "Java",
            LinuxRuntimeToken in ("DOTNET", "DOTNETCORE", "ASPNETCORE", "DOTNET-ISOLATED"), ".NET Core/.NET",
            LinuxRuntimeToken contains "DOTNET", ".NET Core/.NET",
            "Unknown"
        )
    | extend LinuxRuntimeVersionExtracted = extract(@"([0-9]+(?:\.[0-9]+){0,2})", 1, LinuxRuntimeValueRaw)
    | extend LinuxRuntimeVersion =
        case(
            LinuxRuntimeFamily == "Java" and LinuxRuntimeVersionExtracted == "1.8", "8",
            LinuxRuntimeVersionExtracted
        )
    | extend ConfigRuntimeFamily =
        case(
            isnotempty(NodeVersionRaw), "Node",
            isnotempty(PythonVersionRaw), "Python",
            isnotempty(PhpVersionRaw), "PHP",
            isnotempty(JavaVersionRaw), "Java",
            isnotempty(PowerShellVersionRaw), "PowerShell",
            isnotempty(NetFrameworkVersionRaw), ".NET Framework",
            "Unknown"
        )
    | extend ConfigRuntimeVersionRaw =
        case(
            isnotempty(NodeVersionRaw), NodeVersionRaw,
            isnotempty(PythonVersionRaw), PythonVersionRaw,
            isnotempty(PhpVersionRaw), PhpVersionRaw,
            isnotempty(JavaVersionRaw), JavaVersionRaw,
            isnotempty(PowerShellVersionRaw), PowerShellVersionRaw,
            isnotempty(NetFrameworkVersionRaw), NetFrameworkVersionRaw,
            ""
        )
    | extend ConfigRuntimeVersionClean = replace_string(ConfigRuntimeVersionRaw, "v", "")
    | extend ConfigRuntimeVersionExtracted = extract(@"([0-9]+(?:\.[0-9]+){0,2})", 1, ConfigRuntimeVersionClean)
    | extend ConfigRuntimeVersion =
        case(
            ConfigRuntimeFamily == "Java" and ConfigRuntimeVersionExtracted == "1.8", "8",
            ConfigRuntimeVersionExtracted
        )
    | extend RuntimeFamily =
        case(
            isnotempty(LinuxRuntimeFamily) and LinuxRuntimeFamily != "Unknown", LinuxRuntimeFamily,
            isnotempty(ConfigRuntimeFamily) and ConfigRuntimeFamily != "Unknown", ConfigRuntimeFamily,
            "Unknown"
        )
    | extend RuntimeVersion =
        case(
            isnotempty(LinuxRuntimeFamily) and LinuxRuntimeFamily != "Unknown", LinuxRuntimeVersion,
            isnotempty(ConfigRuntimeFamily) and ConfigRuntimeFamily != "Unknown", ConfigRuntimeVersion,
            "Unknown"
        )
    | extend RuntimeSource =
        case(
            isnotempty(LinuxRuntimeFamily) and LinuxRuntimeFamily != "Unknown", "LinuxFxVersion",
            isnotempty(ConfigRuntimeFamily) and ConfigRuntimeFamily != "Unknown", "SiteConfig",
            "Unknown"
        )
    | extend Runtime =
        case(
            RuntimeFamily == "Unknown" or isempty(RuntimeVersion) or RuntimeVersion == "Unknown", "Unknown",
            strcat(RuntimeFamily, " ", RuntimeVersion)
        )
    | project
        id,
        subscriptionId,
        resourceGroup,
        AppName = name,
        AppType,
        CheckType,
        location,
        kind,
        RuntimeFamily,
        RuntimeVersion,
        Runtime,
        RuntimeSource
)
| union
(
    resources
    | where type =~ "microsoft.web/sites"
    | where kind contains "functionapp"
    | extend SiteConfig = properties.siteConfig
    | extend AppSettings = SiteConfig.appSettings
    | mv-expand AppSetting = AppSettings
    | extend AppSettingName = tostring(AppSetting.name)
    | extend AppSettingValue = tostring(AppSetting.value)
    | where AppSettingName =~ "FUNCTIONS_EXTENSION_VERSION"
    | extend RuntimeFamily = "Azure Functions Runtime"
    | extend RuntimeVersion = extract(@"([0-9]+)", 1, AppSettingValue)
    | extend Runtime =
        case(
            isempty(AppSettingValue), "Azure Functions Runtime Unknown",
            strcat("Azure Functions Runtime ", AppSettingValue)
        )
    | project
        id,
        subscriptionId,
        resourceGroup,
        AppName = name,
        AppType = "Function App",
        CheckType = "Function Host Runtime",
        location,
        kind,
        RuntimeFamily,
        RuntimeVersion,
        Runtime,
        RuntimeSource = "FUNCTIONS_EXTENSION_VERSION"
)
| union
(
    resources
    | where type =~ "microsoft.web/sites"
    | where kind contains "functionapp"
    | extend RuntimeFamily = "Azure Functions Runtime"
    | extend RuntimeVersion = "Unknown"
    | extend Runtime = "Azure Functions Runtime Unknown"
    | project
        id,
        subscriptionId,
        resourceGroup,
        AppName = name,
        AppType = "Function App",
        CheckType = "Function Host Runtime",
        location,
        kind,
        RuntimeFamily,
        RuntimeVersion,
        Runtime,
        RuntimeSource = "ARG/AppSettings Not Exposed"
)
| extend KnownRuntimeRank = case(RuntimeVersion == "Unknown" or isempty(RuntimeVersion), 0, 1)
| summarize arg_max(KnownRuntimeRank, *) by id, CheckType, RuntimeFamily
| extend RuntimeMajor = tostring(toint(extract(@"^([0-9]+)", 1, RuntimeVersion)))
| extend RuntimeMajorMinor = extract(@"^([0-9]+\.[0-9]+)", 1, RuntimeVersion)
| extend VersionKey =
    case(
        RuntimeFamily == "Azure Functions Runtime", RuntimeMajor,
        RuntimeFamily == "Python" and RuntimeMajor == "2", "2",
        RuntimeFamily == "PHP" and RuntimeMajor == "5", "5",
        RuntimeFamily in ("Node", "Java"), RuntimeMajor,
        RuntimeFamily in ("PHP", "Python", "PowerShell"), case(isnotempty(RuntimeMajorMinor), RuntimeMajorMinor, RuntimeMajor),
        RuntimeFamily == ".NET Core/.NET" and RuntimeMajor in ("5", "6", "7", "8", "9"), RuntimeMajor,
        RuntimeFamily == ".NET Core/.NET", case(isnotempty(RuntimeMajorMinor), RuntimeMajorMinor, RuntimeMajor),
        RuntimeFamily == ".NET Framework", case(isnotempty(RuntimeMajorMinor), RuntimeMajorMinor, RuntimeMajor),
        "Unknown"
    )
| extend SupportStatus =
    case(
        RuntimeFamily == "Azure Functions Runtime" and VersionKey == "1", "Review",
        RuntimeFamily == "Azure Functions Runtime" and VersionKey in ("2", "3"), "Out of Support",
        RuntimeFamily == "Azure Functions Runtime" and VersionKey == "4", "Supported",
        RuntimeFamily == "PHP" and VersionKey in ("5", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1"), "Out of Support",
        RuntimeFamily == "PHP" and VersionKey in ("8.2", "8.3"), "Supported",
        RuntimeFamily == "Python" and VersionKey in ("2", "3.6", "3.7", "3.8", "3.9"), "Out of Support",
        RuntimeFamily == "Python" and VersionKey == "3.10", "Review",
        RuntimeFamily == "Python" and VersionKey in ("3.11", "3.12", "3.13"), "Supported",
        RuntimeFamily == "Node" and VersionKey in ("10", "12", "14", "16", "18", "20"), "Out of Support",
        RuntimeFamily == "Node" and VersionKey in ("22", "24"), "Supported",
        RuntimeFamily == "Java" and VersionKey == "8", "Review",
        RuntimeFamily == "Java" and VersionKey in ("11", "17", "21"), "Supported",
        RuntimeFamily == ".NET Framework" and VersionKey in ("2.0", "3.5", "4.0", "4.5", "4.5.1", "4.5.2", "4.6", "4.6.1", "4.6.2"), "Out of Support",
        RuntimeFamily == ".NET Framework" and VersionKey in ("4.7", "4.7.1", "4.7.2", "4.8", "4.8.1"), "Supported",
        RuntimeFamily == ".NET Core/.NET" and VersionKey in ("2.1", "3.1", "5", "6", "7"), "Out of Support",
        RuntimeFamily == ".NET Core/.NET" and VersionKey in ("8", "9"), "Supported",
        RuntimeFamily == "PowerShell" and VersionKey in ("7.0", "7.1", "7.2"), "Out of Support",
        RuntimeFamily == "PowerShell" and VersionKey in ("7.4"), "Supported",
        "Review"
    )
| extend InternalExpiryDate =
    case(
        RuntimeFamily == "Azure Functions Runtime" and VersionKey == "1", datetime(2026-09-14),
        RuntimeFamily == "Azure Functions Runtime" and VersionKey == "2", datetime(2022-12-13),
        RuntimeFamily == "Azure Functions Runtime" and VersionKey == "3", datetime(2022-12-13),
        RuntimeFamily == "PHP" and VersionKey == "5", datetime(2019-01-10),
        RuntimeFamily == "PHP" and VersionKey == "7.0", datetime(2018-12-03),
        RuntimeFamily == "PHP" and VersionKey == "7.1", datetime(2019-12-01),
        RuntimeFamily == "PHP" and VersionKey == "7.2", datetime(2020-11-30),
        RuntimeFamily == "PHP" and VersionKey == "7.3", datetime(2021-12-06),
        RuntimeFamily == "PHP" and VersionKey == "7.4", datetime(2022-11-28),
        RuntimeFamily == "PHP" and VersionKey == "8.0", datetime(2023-11-26),
        RuntimeFamily == "PHP" and VersionKey == "8.1", datetime(2025-12-31),
        RuntimeFamily == "PHP" and VersionKey == "8.2", datetime(2026-12-31),
        RuntimeFamily == "PHP" and VersionKey == "8.3", datetime(2027-12-31),
        RuntimeFamily == "Python" and VersionKey == "2", datetime(2020-01-01),
        RuntimeFamily == "Python" and VersionKey == "3.6", datetime(2021-12-23),
        RuntimeFamily == "Python" and VersionKey == "3.7", datetime(2023-06-27),
        RuntimeFamily == "Python" and VersionKey == "3.8", datetime(2024-10-07),
        RuntimeFamily == "Python" and VersionKey == "3.9", datetime(2025-10-31),
        RuntimeFamily == "Python" and VersionKey == "3.10", datetime(2026-10-31),
        RuntimeFamily == "Python" and VersionKey == "3.11", datetime(2027-10-31),
        RuntimeFamily == "Python" and VersionKey == "3.12", datetime(2028-10-31),
        RuntimeFamily == "Python" and VersionKey == "3.13", datetime(2029-10-31),
        RuntimeFamily == "Node" and VersionKey == "10", datetime(2021-04-30),
        RuntimeFamily == "Node" and VersionKey == "12", datetime(2022-04-30),
        RuntimeFamily == "Node" and VersionKey == "14", datetime(2023-04-30),
        RuntimeFamily == "Node" and VersionKey == "16", datetime(2023-09-11),
        RuntimeFamily == "Node" and VersionKey == "18", datetime(2025-04-30),
        RuntimeFamily == "Node" and VersionKey == "20", datetime(2026-04-30),
        RuntimeFamily == "Node" and VersionKey == "22", datetime(2027-04-30),
        RuntimeFamily == "Node" and VersionKey == "24", datetime(2028-04-30),
        RuntimeFamily == ".NET Framework" and VersionKey == "2.0", datetime(2011-07-12),
        RuntimeFamily == ".NET Framework" and VersionKey == "3.5", datetime(2029-01-09),
        RuntimeFamily == ".NET Framework" and VersionKey == "4.0", datetime(2016-01-12),
        RuntimeFamily == ".NET Framework" and VersionKey == "4.5", datetime(2016-01-12),
        RuntimeFamily == ".NET Framework" and VersionKey == "4.5.1", datetime(2016-01-12),
        RuntimeFamily == ".NET Framework" and VersionKey == "4.5.2", datetime(2022-04-26),
        RuntimeFamily == ".NET Framework" and VersionKey == "4.6", datetime(2022-04-26),
        RuntimeFamily == ".NET Framework" and VersionKey == "4.6.1", datetime(2022-04-26),
        RuntimeFamily == ".NET Framework" and VersionKey == "4.6.2", datetime(2027-01-12),
        RuntimeFamily == ".NET Core/.NET" and VersionKey == "2.1", datetime(2021-08-21),
        RuntimeFamily == ".NET Core/.NET" and VersionKey == "3.1", datetime(2022-12-13),
        RuntimeFamily == ".NET Core/.NET" and VersionKey == "5", datetime(2022-05-10),
        RuntimeFamily == ".NET Core/.NET" and VersionKey == "6", datetime(2024-11-12),
        RuntimeFamily == ".NET Core/.NET" and VersionKey == "7", datetime(2024-05-14),
        RuntimeFamily == ".NET Core/.NET" and VersionKey == "8", datetime(2026-11-10),
        RuntimeFamily == ".NET Core/.NET" and VersionKey == "9", datetime(2026-05-12),
        RuntimeFamily == "PowerShell" and VersionKey == "7.0", datetime(2022-12-03),
        RuntimeFamily == "PowerShell" and VersionKey == "7.1", datetime(2023-05-08),
        RuntimeFamily == "PowerShell" and VersionKey == "7.2", datetime(2024-11-08),
        RuntimeFamily == "PowerShell" and VersionKey == "7.4", datetime(2026-11-10),
        datetime(null)
    )
| extend EndOfSupport =
    case(
        isnull(InternalExpiryDate), "Unknown/Review",
        format_datetime(InternalExpiryDate, "yyyy-MM-dd")
    )
| extend DaysToExpiry =
    case(
        isnull(InternalExpiryDate), int(null),
        datetime_diff("day", InternalExpiryDate, now())
    )
| extend ExpiryCategory =
    case(
        isnull(InternalExpiryDate), "Unknown",
        DaysToExpiry < 0, "Expired",
        DaysToExpiry <= 30, "Expires Within 30 Days",
        DaysToExpiry <= 60, "Expires Within 60 Days",
        DaysToExpiry <= 90, "Expires Within 90 Days",
        DaysToExpiry <= 180, "Expires Within 180 Days",
        DaysToExpiry <= 365, "Expires Within 1 Year",
        "Valid Beyond 1 Year"
    )
| extend Recommendation =
    case(
        RuntimeFamily == "Azure Functions Runtime" and VersionKey == "1", "Migrate Azure Functions runtime to ~4 after application compatibility validation",
        RuntimeFamily == "Azure Functions Runtime" and VersionKey in ("2", "3"), "Migrate Azure Functions runtime to ~4",
        RuntimeFamily == "PHP" and VersionKey in ("5", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1"), "Upgrade to PHP 8.3",
        RuntimeFamily == "Python" and VersionKey in ("2", "3.6", "3.7", "3.8", "3.9", "3.10"), "Upgrade to Python 3.12 or 3.13",
        RuntimeFamily == "Node" and VersionKey in ("10", "12", "14", "16", "18", "20"), "Upgrade to Node 22 or 24",
        RuntimeFamily == "Java" and VersionKey == "8", "Upgrade to Java 17 or 21",
        RuntimeFamily == ".NET Framework" and VersionKey in ("2.0", "3.5", "4.0", "4.5", "4.5.1", "4.5.2", "4.6", "4.6.1", "4.6.2"), "Upgrade to .NET Framework 4.8.1 or migrate to .NET 8",
        RuntimeFamily == ".NET Core/.NET" and VersionKey in ("2.1", "3.1", "5", "6", "7"), "Upgrade to .NET 8 or .NET 9",
        RuntimeFamily == "PowerShell" and VersionKey in ("7.0", "7.1", "7.2"), "Upgrade to PowerShell 7.4 or later",
        RuntimeFamily == "Unknown" or VersionKey == "Unknown", "Runtime not detected in Azure Resource Graph; validate from Azure Portal or Azure CLI",
        SupportStatus == "Review", "Review runtime lifecycle and application compatibility with Application Owner",
        "No immediate action"
    )
| where SupportStatus in ("Out of Support", "Review")
| join kind=leftouter
(
    resourcecontainers
    | where type =~ "microsoft.resources/subscriptions"
    | project subscriptionId, SubscriptionName = name
)
on subscriptionId
| project
    Subscription = coalesce(SubscriptionName, subscriptionId),
    SubscriptionId = subscriptionId,
    ResourceGroup = resourceGroup,
    AppName,
    AppType,
    Location = location,
    CheckType,
    RuntimeFamily,
    RuntimeVersion,
    Runtime,
    RuntimeSource,
    SupportStatus,
    InternalExpiryDate,
    EndOfSupport,
    DaysToExpiry,
    ExpiryCategory,
    Recommendation
| order by
    SupportStatus asc,
    ExpiryCategory asc,
    DaysToExpiry asc,
    AppType asc,
    RuntimeFamily asc,
    RuntimeVersion asc,
    Subscription asc,
    ResourceGroup asc,
    AppName asc
Previous Post Next Post

Contact Form