How to connect & deploy to an Azure Function inside a vNet (that is using private endpoints) using Azure VPN Gateway

In order to deploy my Azure Function code to an Azure Function deployed inside a vNet (using private endpoints), I needed to connect my local machine to the vNet running in Azure using Azure VPN Gateway.

My Azure Functions were deployed inside a vNet (not directly publicly exposed). I deployed them inside the vNet using private endpoints to ensure that they were only accessed via my reverse proxy (Azure Application Gateway).

This is great from a security standpoint, but it makes it hard to do local development & deploy my code to the Azure Function to test it.

If I try to deploy my Function code from my local computer without some way into the vNet, I get the following error.

az functionapp deployment source config-zip -g rg-apimTrafficMgr-ussc-dev -n func-apimTrafficMgr-ussc-dev --src ./app.zip


Getting scm site credentials for zip deployment
Starting zip deployment. This operation can take a while to complete ...
The command failed with an unexpected error. Here is the traceback:
HTTPSConnectionPool(host='func-apimtrafficmgr-ussc-dev.scm.azurewebsites.net', port=443): Max retries exceeded with url: /api/zipdeploy?isAsync=true (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x075B37F0>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed'))
Traceback (most recent call last):
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\urllib3/connection.py", line 174, in _new_conn
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\urllib3/util/connection.py", line 73, in create_connection
  File "socket.py", line 955, in getaddrinfo
socket.gaierror: [Errno 11001] getaddrinfo failed

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\urllib3/connectionpool.py", line 699, in urlopen
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\urllib3/connectionpool.py", line 382, in _make_request
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\urllib3/connectionpool.py", line 1010, in _validate_conn
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\urllib3/connection.py", line 358, in connect
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\urllib3/connection.py", line 186, in _new_conn
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x075B37F0>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed

During handling of the above exception, another exception occurred:

  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/__init__.py", line 697, in _run_job
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/__init__.py", line 333, in __call__
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/command_operation.py", line 121, in handler
  File "C:\Users\jordanbean\.azure\cliextensions\appservice-kube\azext_appservice_kube\custom.py", line 1303, in enable_zip_deploy_functionapp
    return enable_zip_deploy(cmd, resource_group_name, name, src, timeout, slot)  File "C:\Users\jordanbean\.azure\cliextensions\appservice-kube\azext_appservice_kube\custom.py", line 1338, in enable_zip_deploy
    res = requests.post(zip_url, data=zip_content, headers=headers, verify=not should_disable_connection_verify())
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\requests/api.py", line 117, in post
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\requests/api.py", line 61, in request
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\requests/sessions.py", line 542, in request
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\requests/sessions.py", line 655, in send
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\requests/adapters.py", line 516, in send
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='func-apimtrafficmgr-ussc-dev.scm.azurewebsites.net', port=443): Max retries exceeded with url: /api/zipdeploy?isAsync=true (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x075B37F0>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed'))

In order to overcome this, I need to connect my local computer to the vNet so it can communicate with the Azure Function. Normally, this would be accomplished by me being on a machine connected to the same vNet (such as over ExpressRoute).

In my case, I decided to stand up a point-to-site VPN from my local computer to my Azure vNet using Azure VPN Gateway. This is because I am testing locally, and I don’t have an ExpressRoute from my local home computer to an Azure vNet.

First, I need to allocate a new subnet GatewaySubnet for the VPN Gateway to be attached to. In the Azure portal, open your vNet, navigate to the Subnets blade & click on Gateway subnet. Choose an appropriate subnet address range and click Save.

Next, go back to your Resource Group and click Create and select Virtual network gateway. Give it a Name, pick a Region, select VPN as the Gateway type, select Route-based as the VPN type, pick the VpnGw1 SKU (since I don’t need much), select Generation 1, pick my vNet and subnet, select Standard as the Public IP Address type, create a new Public IP address and click Review + create.

Once the VPN Gateway has been created (this may take a while), you can select it in the Azure portal. Select the Point-to-site configuration blade. Add an Address Pool that works for your local home network, select OpenVPN (SSL) as the Tunnel type, set the Authentication type to Azure Active Directory. Set the following values for the AAD authentication (check the Azure docs for more specific guidance depending on your use case). You will need to get the unique identifier of your AAD tenant (go to the Azure Active Directory page in the Azure portal and look in the Overview page).

  • Tenant – https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/
  • Audience – 41b23e61-6c1e-4545-b367-cd054e0ed4b4
  • Issuer – https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/

Click Save. This will take a few minutes to set up. You may need to navigate away from this page and navigate back after the Save to enable the Download VPN client button.

Look in your Downloads directory to see the zip file that contains the configuration XML files. In this example, I will use the Azure VPN client, so navigate to the AzureVPN directory.

Now I need a VPN client. I can use the Azure VPN client from the Microsoft store.

Open the Azure VPN Client on your local machine. Click the + button, select Import and select the azurevpnconfig.xml file you downloaded.

Now you can click the Connect button to sign-in to AAD & connect to your vNet. We can see it connected to both our local network (using the Address space specified earlier) and the Azure network.

This connects you to the vNet running in Azure, but the Function deployment still fails! What gives?

We have connected our networks, but our local computer does not know how to resolve the FQDN of the Azure Function to the correct IP address in my Azure vNet. It is still trying to go out to the Internet to resolve it but will be unable to do so because we have enabled private endpoints on our Azure Function.

ping func-apimtrafficmgr-ussc-dev.azurewebsites.net

Ping request could not find host func-apimtrafficmgr-ussc-dev.azurewebsites.net. Please check the name and try again.

We need to tell our local computer how to resolve this FQDN. Since I do not have a DNS forwarder setup, I can modify the local hosts file to give it an A record to resolve to.

We need to find the “local” IP address of the private endpoint deployed to Azure. Navigate to the private endpoint resource and click on the DNS configuration blade. We can see the IP address that has been assigned to it and the equivalent FQDNs.

Open the C:\Windows\System32\drivers\etc\hosts file using Notepad running in Administrator mode. Add records for both the Function API endpoint & the SCM endpoint (which lets us deploy our code).

10.0.1.5 func-apimtrafficmgr-ussc-dev.azurewebsites.net
10.0.1.5 func-apimtrafficmgr-ussc-dev.scm.azurewebsites.net

Save the hosts file.

We can now see that ping is able to resolve the correct 10. IP address (even though it won’t get a response from the Function App, we know the IP address is now correct).

ping func-apimtrafficmgr-ussc-dev.azurewebsites.net

Pinging func-apimtrafficmgr-ussc-dev.azurewebsites.net [10.0.1.5] with 32 bytes of data:
Request timed out.

Try the deployment again and it will succeed.

az functionapp deployment source config-zip -g rg-apimTrafficMgr-ussc-dev -n func-apimTrafficMgr-ussc-dev --src ./app.zip
The behavior of this command has been altered by the following extension: appservice-kube
Getting scm site credentials for zip deployment
Starting zip deployment. This operation can take a while to complete ...
Deployment endpoint responded with status code 202
{
  "active": true,
  "author": "N/A",
  "author_email": "N/A",
  "complete": true,
  "deployer": "ZipDeploy",
  "end_time": "2022-05-27T21:22:44.8618148Z",
  "id": "60ed3ee9cc5947648e9cb0b94b65f7ce",
  "is_readonly": true,
  "is_temp": false,
  "last_success_end_time": "2022-05-27T21:22:44.8618148Z",
  "log_url": "https://func-apimtrafficmgr-ussc-dev.scm.azurewebsites.net/api/deployments/latest/log",
  "message": "Created via a push deployment",
  "progress": "",
  "provisioningState": "Succeeded",
  "received_time": "2022-05-27T21:22:38.3617789Z",
  "site_name": "func-apimTrafficMgr-ussc-dev",
  "start_time": "2022-05-27T21:22:38.6742432Z",
  "status": 4,
  "status_text": "",
  "url": "https://func-apimtrafficmgr-ussc-dev.scm.azurewebsites.net/api/deployments/latest"
}

Leave a Reply

Your email address will not be published. Required fields are marked *