This page documents how to use Azure Resource Graph Explorer (ARG) to report on Azure Application Gateways (App GWs) that have private listeners, public listeners, or both. It also explains the “No listener associated to the private” message you may see in the Azure Portal.
When Azure shows “No listener associated to the private”, it only matters if you *expect* traffic to reach that private front-end IP.
| Scenario | Impact |
|---|---|
| Only using public endpoint | No issue — safe to ignore |
| Need to serve internal clients | Traffic to private IP fails |
| Security | No new exposure (no open port without listener) |
| Management hygiene | Consider deleting unused config |
All queries below run in Azure Portal → Resource Graph Explorer. Before running, click Scope → Select all subscriptions (unless you intentionally scope smaller).
Below are reusable Kusto snippets. Each `
` block begins with a comment line naming the query so context is preserved when copying the code block alone.
----
==== List Application Gateways with *Private Listeners* ====
<code kusto>
// ==== Private Listeners ====
// Lists App GWs that have at least one listener bound to a PRIVATE frontend IP.
Resources
| where type == "microsoft.network/applicationgateways"
| extend listeners = properties.httpListeners
| mv-expand listener = listeners
| extend frontendIP = tostring(listener.properties.frontendIPConfiguration.id)
| join kind=inner (
Resources
| where type == "microsoft.network/applicationgateways/frontendipconfigurations"
| extend ipAddress = tostring(properties.privateIPAddress)
| extend frontendIPId = id
| project frontendIPId, ipAddress
) on $left.frontendIP == $right.frontendIPId
| project resourceGroup, name, listenerName=tostring(listener.name), privateIP=ipAddress
// ==== Public Listeners ====
// Lists App GWs that have at least one listener bound to a PUBLIC frontend IP (resource ID only).
Resources
| where type == "microsoft.network/applicationgateways"
| extend listeners = properties.httpListeners
| mv-expand listener = listeners
| extend frontendIP = tostring(listener.properties.frontendIPConfiguration.id)
| join kind=inner (
Resources
| where type == "microsoft.network/applicationgateways/frontendipconfigurations"
| extend publicIPId = tostring(properties.publicIPAddress.id)
| extend frontendIPId = id
| project frontendIPId, publicIPId
) on $left.frontendIP == $right.frontendIPId
| project resourceGroup, name, listenerName=tostring(listener.name), publicIPResourceId=publicIPId
If the query above returned nothing, run this quick front-end scan to confirm whether any App GWs actually reference a Public IP at all.
// ==== Public Frontends Quick Check ====
// Shows ALL App GW frontend configs that reference a Public IP (whether or not a listener uses them).
Resources
| where type =~ "microsoft.network/applicationgateways"
| extend fips = properties.frontendIPConfigurations
| mv-expand fip = fips
| extend publicIpId = tostring(fip.properties.publicIPAddress.id)
| where isnotempty(publicIpId)
| project subscriptionId, resourceGroup, gatewayName = name,
frontendName = tostring(fip.name),
publicIpResourceId = publicIpId
// ==== Public Listeners with actual IP address ====
// Resolves Public IP resource ID to its routable IPv4/IPv6 address.
Resources
| where type =~ "microsoft.network/applicationgateways"
| extend fips = properties.frontendIPConfigurations
| mv-expand fip = fips
| extend publicIpId = tostring(fip.properties.publicIPAddress.id)
| where isnotempty(publicIpId)
| project subscriptionId, resourceGroup, gatewayName = name,
frontendName = tostring(fip.name),
publicIpResourceId = publicIpId
| join kind = leftouter (
Resources
| where type =~ "microsoft.network/publicipaddresses"
| project publicIpResourceId = id,
publicIpAddress = properties.ipAddress
) on publicIpResourceId
| project subscriptionId, resourceGroup, gatewayName, frontendName, publicIpAddress, publicIpResourceId
// ==== Private Frontend Configs ====
// Lists every App GW frontend that carries a PRIVATE IP (whether or not a listener uses it).
Resources
| where type =~ "microsoft.network/applicationgateways"
| extend fips = properties.frontendIPConfigurations
| mv-expand fip = fips
| extend privateIpAddress = tostring(fip.properties.privateIPAddress)
| where isnotempty(privateIpAddress)
| project subscriptionId, resourceGroup, gatewayName = name,
frontendName = tostring(fip.name),
privateIpAddress,
frontendIPConfigId = tostring(fip.id)
// ==== Private-bound Listeners ====
// Shows which listeners on each App GW are tied to PRIVATE frontends.
Resources
| where type =~ "microsoft.network/applicationgateways"
| extend listeners = properties.httpListeners,
fips = properties.frontendIPConfigurations
| mv-expand listener = listeners
| mv-expand fip = fips
| extend listenerFipId = tostring(listener.properties.frontendIPConfiguration.id)
| where listenerFipId == fip.id
| extend privateIpAddress = tostring(fip.properties.privateIPAddress)
| where isnotempty(privateIpAddress)
| project subscriptionId, resourceGroup, gatewayName = name,
listenerName = tostring(listener.name),
privateIpAddress, frontendName = tostring(fip.name)
Use these when you need to identify “dual-homed” gateways.
// ==== Gateways with BOTH Private & Public Listeners (IDs only) ====
// Counts listeners of each type; returns sets of private IPs + Public IP *resource IDs*.
Resources
| where type =~ "microsoft.network/applicationgateways"
| extend listeners = properties.httpListeners,
fips = properties.frontendIPConfigurations
| mv-expand listener = listeners
| mv-expand fip = fips
| extend listenerFipId = tostring(listener.properties.frontendIPConfiguration.id)
| where listenerFipId == tostring(fip.id)
| extend isPrivate = iff(isnotempty(fip.properties.privateIPAddress), 1, 0),
isPublic = iff(isnotempty(fip.properties.publicIPAddress.id), 1, 0)
| summarize
privateListenerCount = countif(isPrivate == 1),
publicListenerCount = countif(isPublic == 1),
privateIPs = make_set(fip.properties.privateIPAddress, 5),
publicIPIds = make_set(fip.properties.publicIPAddress.id, 5)
by subscriptionId, resourceGroup, gatewayName = name
| where privateListenerCount > 0 and publicListenerCount > 0
| project subscriptionId, resourceGroup, gatewayName,
privateListenerCount, publicListenerCount,
privateIPs, publicIPIds
// ==== Gateways with BOTH Private & Public Listeners (actual Public IPs) ====
// Resolves public IP resource IDs to addresses and summarizes per gateway.
let publicIPs = Resources
| where type =~ "microsoft.network/publicipaddresses"
| project publicIpResourceId = id,
publicIpAddress = properties.ipAddress;
Resources
| where type =~ "microsoft.network/applicationgateways"
| extend listeners = properties.httpListeners,
fips = properties.frontendIPConfigurations
| mv-expand listener = listeners
| mv-expand fip = fips
| extend listenerFipId = tostring(listener.properties.frontendIPConfiguration.id)
| where listenerFipId == tostring(fip.id)
| extend privateIpAddress = tostring(fip.properties.privateIPAddress),
publicIpResourceId = tostring(fip.properties.publicIPAddress.id),
isPrivate = iff(isnotempty(privateIpAddress), 1, 0),
isPublic = iff(isnotempty(publicIpResourceId), 1, 0)
| join kind = leftouter publicIPs on publicIpResourceId
| summarize
privateListenerCount = countif(isPrivate == 1),
publicListenerCount = countif(isPublic == 1),
privateIPs = make_set(privateIpAddress, 5),
publicIPs = make_set(publicIpAddress, 5)
by subscriptionId, resourceGroup, gatewayName = name
| where privateListenerCount > 0 and publicListenerCount > 0
| project subscriptionId, resourceGroup, gatewayName,
privateListenerCount, publicListenerCount,
privateIPs, publicIPs
`| where subscriptionId == “<SUBSCRIPTION-GUID>“` after the main `Resources` line.
`| where location =~ “westeurope”` (case-insensitive).
End of document.