Table of Contents
Azure Application Gateways: Private & Public Listener Query Cookbook
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.
Problem: “No listener associated to the private”
When Azure shows “No listener associated to the private”, it only matters if you *expect* traffic to reach that private front-end IP.
Explanation
- The private front-end IP exists on the gateway, but no listener is bound to it; traffic to that IP is ignored.
- A listener binds an IP/port/hostname tuple to a routing rule and backend. No listener ⇒ no traffic handling.
Impact Summary
| 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 |
Fix (if needed)
- Create a listener using the private front-end IP.
- Bind it to a request routing rule and backend pool.
- Update internal DNS (private zone) if clients will use an FQDN.
Using Azure Resource Graph Explorer
All queries below run in Azure Portal → Resource Graph Explorer. Before running, click Scope → Select all subscriptions (unless you intentionally scope smaller).
Queries
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
List Application Gateways with *Public Listeners*
// ==== 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
Troubleshooting: Public Query Returned *No Results*
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*
// ==== 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 Front-End Configurations
// ==== 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)
Listeners Bound to *Private* IPs
// ==== 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)
Gateways that have *Both* Private **and** Public Listeners
Use these when you need to identify “dual-homed” gateways.
Summary: Counts & Resource IDs
// ==== 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
Summary: Counts & *Actual* Public IP Address
// ==== 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
Tips
- In ARG Explorer, always confirm Scope includes all required subscriptions.
- Use the Download results button in ARG Explorer to export CSV for reporting.
- To filter to a single subscription, prepend:
`| where subscriptionId == “<SUBSCRIPTION-GUID>“` after the main `Resources` line.
- To filter to a region:
`| where location =~ “westeurope”` (case-insensitive).
End of document.
