Azure Functions can reach resources inside a virtual network, but they don’t live there by default.

Imagine you have a critical database or a private API running inside your Azure Virtual Network (VNet). Your Azure Function, which is a serverless compute service, needs to talk to it. By default, Functions run in a public, shared network space. To grant them access to your private VNet resources, you need to establish a secure tunnel. This is achieved through VNet integration.

Here’s how it looks in action. Let’s say we have a Function App named my-private-function-app that needs to access a SQL Server instance (my-sql-server.database.windows.net) residing within a VNet.

First, we need to provision a dedicated subnet within our VNet for the Function App to use. This subnet is not for hosting VMs; it’s specifically for the VNet integration of the Function App.

az network vnet subnet create \
  --resource-group my-vnet-rg \
  --vnet-name my-vnet \
  --name IntegrationSubnet \
  --address-prefixes 10.0.2.0/24

This IntegrationSubnet will be dedicated to the Function App. Azure will inject the Function App’s outbound traffic into this subnet.

Next, we enable VNet integration for the Function App, pointing it to this newly created subnet.

az functionapp vnet-integration add \
  --resource-group my-function-rg \
  --name my-private-function-app \
  --subnet IntegrationSubnet \
  --resource-group my-vnet-rg \
  --vnet my-vnet

Once this is set up, your my-private-function-app can now make outbound connections to resources within my-vnet. For example, your C# function might look like this:

using System;
using System.Net.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;

public static class PrivateDataFetcher
{
    [FunctionName("FetchPrivateData")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
        ILogger log)
    {
        log.LogInformation("C# HTTP trigger function processed a request.");

        var connectionString = Environment.GetEnvironmentVariable("SqlConnectionString");
        // Now you can use this connection string to connect to your private SQL Server.
        // For example:
        // using (var conn = new SqlConnection(connectionString)) { ... }

        var httpClient = new HttpClient();
        // You could also make HTTP requests to private APIs within the VNet.
        // var response = await httpClient.GetAsync("http://my-private-api.local/data");

        return new OkObjectResult("Successfully attempted to connect to private resources.");
    }
}

The key here is that the SqlConnectionString (or any other connection details for resources within the VNet) will now work because the Function App’s outbound traffic is routed through the VNet.

The problem this solves is bridging the gap between Azure’s serverless compute and your private network infrastructure. Without VNet integration, you’d be forced to expose your VNet resources to the public internet, which is a significant security risk. VNet integration allows your Functions to act as secure clients to your private services.

Internally, VNet integration works by establishing a network interface in the subnet you designate. All outbound traffic from the Function App is then routed through this interface. For premium and dedicated plans, this is achieved using a Virtual Network Gateway. For Consumption plans, it uses a different mechanism that injects your function’s network traffic into the VNet. You can also configure private endpoints for your Function App to allow inbound traffic from your VNet.

The exact levers you control are the subnet you dedicate for integration, the type of VNet integration (regional vs. gateway), and the network security rules (NSGs) applied to the integration subnet. By default, all outbound traffic from the Function App is routed through the VNet. However, you can configure "VNet route all" to ensure all outbound traffic, including to public endpoints, goes through the VNet and potentially through a NAT Gateway or firewall for centralized egress control.

When you enable VNet integration, Azure creates a new network route table that directs traffic destined for your VNet’s address space through the integration subnet. This is transparent to your function code, which can simply use private IP addresses or internal DNS names to reach resources.

The most common reason people run into trouble is misunderstanding that the integration subnet is for the Function App’s outbound traffic, not for hosting Function App instances themselves. It’s a routing mechanism.

After successfully integrating your Function App with a VNet, the next hurdle you’ll likely encounter is managing inbound access, which is typically addressed by configuring private endpoints for your Function App.

Want structured learning?

Take the full Azure-functions course →