Azure Functions can restrict access using IP address filtering, but it’s not as simple as adding a firewall rule; the filtering happens at the Application Gateway or Azure Front Door level, and requires careful configuration of both the frontend and backend.
Let’s see this in action. Imagine you have a public Azure Function my-function-app.azurewebsites.net that you want to expose only to your office network, say 203.0.113.0/24.
First, you’d provision an Azure Application Gateway. This acts as your public-facing endpoint.
{
"name": "my-appgw",
"location": "West US",
"sku": {
"name": "Standard_v2",
"tier": "Standard_v2"
},
"gatewayIpConfigurations": [
{
"name": "appGatewayIpConfig",
"subnet": {
"id": "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/appgwsubnet"
}
}
],
"frontendIpConfigurations": [
{
"name": "appGatewayFrontendIp",
"publicIpAddress": {
"id": "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/my-rg/providers/Microsoft.Network/publicIPAddresses/my-appgw-pip"
}
}
],
"backendAddressPools": [
{
"name": "functionAppBackendPool",
"backendAddresses": [
{
"fqdn": "my-function-app.azurewebsites.net"
}
]
}
],
"httpListeners": [
{
"name": "functionAppHttpListener",
"frontendIpConfiguration": {
"id": "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/my-rg/providers/Microsoft.Network/applicationGateways/my-appgw/frontendIPConfigurations/appGatewayFrontendIp"
},
"frontendPort": {
"id": "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/my-rg/providers/Microsoft.Network/applicationGateways/my-appgw/frontendPorts/httpPort"
},
"protocol": "Http"
}
],
"requestRoutingRules": [
{
"name": "functionAppRoutingRule",
"ruleType": "Basic",
"httpListener": {
"id": "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/my-rg/providers/Microsoft.Network/applicationGateways/my-appgw/httpListeners/functionAppHttpListener"
},
"backendAddressPool": {
"id": "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/my-rg/providers/Microsoft.Network/applicationGateways/my-appgw/backendAddressPools/functionAppBackendPool"
},
"backendHttpSettings": {
"id": "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/my-rg/providers/Microsoft.Network/applicationGateways/my-appgw/backendHttpSettingsCollection/defaultBackendHttpSettings"
}
}
],
"backendHttpSettingsCollection": [
{
"name": "defaultBackendHttpSettings",
"port": 80,
"protocol": "Http",
"cookieBasedAffinity": "Disabled",
"pickHostNameFromBackendAddress": true
}
]
}
Now, the magic happens with the Network Security Group (NSG) associated with the subnet where your Application Gateway resides. This NSG will contain the rules to restrict access.
{
"name": "my-appgw-nsg",
"location": "West US",
"properties": {
"securityRules": [
{
"name": "AllowOfficeIPs",
"properties": {
"priority": 100,
"access": "Allow",
"direction": "Inbound",
"protocol": "*",
"sourceAddressPrefix": "203.0.113.0/24",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "*",
"sourcePortList": null,
"destinationPortList": null
}
},
{
"name": "DenyAllOtherInbound",
"properties": {
"priority": 4096,
"access": "Deny",
"direction": "Inbound",
"protocol": "*",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "*",
"sourcePortList": null,
"destinationPortList": null
}
}
]
}
}
Crucially, you must also configure your Azure Function App to not be publicly accessible directly. You do this by setting publicNetworkAccess to Disabled on the Function App’s network profile. This forces all traffic through the Application Gateway.
az functionapp network show --resource-group my-rg --name my-function-app --query publicNetworkAccess
# Expected output: "Disabled"
If it’s not Disabled, you can set it:
az functionapp network update --resource-group my-rg --name my-function-app --public-network-access Disabled
The Application Gateway’s public IP address is what your users will connect to. The NSG on the Application Gateway’s subnet then enforces the IP restrictions. Traffic that isn’t from 203.0.113.0/24 is dropped by the NSG before it even reaches the Application Gateway’s request routing logic. The Application Gateway then forwards allowed traffic to your Function App.
The surprising truth is that Azure Functions themselves don’t have native IP restriction features you can configure directly. You’re always leaning on an intermediary like Application Gateway or Azure Front Door, which then uses NSGs to enforce the rules. This means you’re not just restricting access to the Function App, but to the entire subnet hosting the Application Gateway.
When you set publicNetworkAccess to Disabled on the Function App, you’re not just blocking external access; you’re also preventing access from within your Virtual Network unless it’s coming from a specific, pre-configured source like a Private Endpoint or, in this scenario, traffic that has been allowed through your Application Gateway. The Application Gateway’s health probes also need to be able to reach the Function App.
The next hurdle you’ll likely face is configuring HTTPS for your Application Gateway and ensuring proper certificate management.