Access a Cosmos DB in a virtual network from a Function app
Asked Answered
M

1

5

Apologies for the long post, but I've been working for days on this issue and I can't seem to find the solution.

Situation

I want to restrict access to a Cosmos DB in Azure by placing it in a Virtual Network (VNet) and only allow access via a Function app.

Approach

The first part is simple: Create a Cosmos DB and a VNet and configure the service endpoint of the database as described here.

Connecting a Function app to a VNet can be done in two ways.

Using a Gateway

Following this guide, I've configured my VNet with a Gateway subnet and added and configured a VPN Gateway as described.

After that I added the Gateway subnet to the Cosmos DB service endpoint configuration and setup my Function App

Choosing a subnet using the VNet (preview) functionality

This approach is easier to setup, but as described in the microsoft docs it's not available for production workloads.
Configuration is easier, but as the results are the same, I've omitted this option in the rest of this post.

Configuration

VNet

GatewaySubnet configuration

Gateway

Point-to-site configuration

Cosmos DB

Cosmos DB Configuration

Function App

Function App VNet configuration

Problem

I keep getting connection errors when trying to connect to the Cosmos DB from my function app:

{
    "Message": "An error has occurred.",
    "ExceptionMessage": "Unable to proceed with the request. Please check the authorization claims to ensure the required permissions to process the request.\r\nActivityId: f784890f-2e1a-4e36-bfb9-8f62ff32c034, Microsoft.Azure.Documents.Common/2.2.0.0, Windows/10.0.14393 documentdb-netcore-sdk/2.2.2",
    "ExceptionType": "Microsoft.Azure.Documents.DocumentClientException",
    "StackTrace": "   at Microsoft.Azure.Documents.Client.ClientExtensions.ParseResponseAsync(HttpResponseMessage responseMessage, JsonSerializerSettings serializerSettings, Boolean throwOnKnownClientErrorCodes)\r\n   at Microsoft.Azure.Documents.Client.GatewayServiceConfigurationReader.GetDatabaseAccountAsync(Uri serviceEndpoint)\r\n   at Microsoft.Azure.Documents.Routing.GlobalEndpointManager.GetDatabaseAccountFromAnyLocationsAsync(Uri defaultEndpoint, IList`1 locations, Func`2 getDatabaseAccountFn)\r\n   at Microsoft.Azure.Documents.Client.GatewayServiceConfigurationReader.InitializeReaderAsync()\r\n   at Microsoft.Azure.Documents.Client.DocumentClient.InitializeGatewayConfigurationReader()\r\n   at Microsoft.Azure.Documents.Client.DocumentClient.GetInitializationTask()\r\n   at Microsoft.Azure.Documents.Client.DocumentClient.EnsureValidClientAsync()\r\n   at Microsoft.Azure.Documents.Client.DocumentClient.ReadDatabasePrivateAsync(String databaseLink, RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance)\r\n   at Microsoft.Azure.Documents.BackoffRetryUtility`1.<>c__DisplayClass1_0.<<ExecuteAsync>b__0>d.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetryAsync(Func`1 callbackMethod, Func`3 callShouldRetry, Func`1 inBackoffAlternateCallbackMethod, TimeSpan minBackoffForInBackoffCallback, CancellationToken cancellationToken, Action`1 preRetryCallback)\r\n   at Microsoft.Azure.Documents.ShouldRetryResult.ThrowIfDoneTrying(ExceptionDispatchInfo capturedException)\r\n   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetryAsync(Func`1 callbackMethod, Func`3 callShouldRetry, Func`1 inBackoffAlternateCallbackMethod, TimeSpan minBackoffForInBackoffCallback, CancellationToken cancellationToken, Action`1 preRetryCallback)\r\n   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteAsync(Func`1 callbackMethod, IRetryPolicy retryPolicy, CancellationToken cancellationToken, Action`1 preRetryCallback)\r\n   at Microsoft.Azure.Documents.Client.DocumentClient.CreateDatabaseIfNotExistsPrivateAsync(Database database, RequestOptions options)\r\n   at VnetTestFunction.Function1.Run(HttpRequest req, ILogger log) in C:\\Development\\VnetTestFunction\\VnetTestFunction\\Function1.cs:line 39"
}

There is no difference between the approaches. In both cases there's no connection between my function app and the database.

As a test, I've followed this tutorial and added a VM and setup a proxy. This works, so the Function app has access to the VNet.

My gut feeling says that this is some configuration issue somewhere, but I'm unable to find out where. I'll keep searching, but any help is greatly appreciated.

Used online resources

Mehalek answered 27/2, 2019 at 8:29 Comment(0)
J
4

I believe your configuration is correct since both approaches should not work as you desire.

For VNet Integration feature, you could use this gateway to access securely the resources in a VNet.

VNet Integration gives your web app(or function app) access to resources in your virtual network but doesn't grant private access to your web app from the virtual network.

Also, Cosmos DB is not a resource inside Azure VNet, actually you have not restricted the access from Azure function to Cosmos DB using this method. Your Function App is connected to both the Internet and your VNET. It still works from Function App to Cosmos DB over the Internet.

Also, once you enable the Cosmos DB service endpoint in a specific subnet, this indeed limits access to an Azure Cosmos DB account with connections from this authorized subnet in a virtual network. If you only configure the authorized subnet in the firewall of Cosmos DB, I think only the request from the authorized subnet could bypass the firewall. In this case, Azure function is not a resource in the authorized subnet as well.

When you add a VM inside the Azure VNet and setup a proxy following the process of integrating a Function App with an Azure Virtual Network. In this scenario, VMs are resources deployed in an Azure VNet. So you could access it in a private VNet using VNet Integration features.

As far as I know, if you want the Azure Function App inside a VNet, you could deploy the app service in an App Service Environments, which deploys Azure App Service directly into an Azure virtual network. Sure, It's a very high scale and cost.

Jeffiejeffrey answered 28/2, 2019 at 6:38 Comment(5)
Hi Nancy, thank you for your answer. It clarified why it doesn't work, but it's unfortunate that a construction like this isn't possible without using a costly construction like an App Service Environment. I'm going to check what other possibilities are and post my findings back on this question. Regards, Geert-JanMehalek
Depending on what you actually want to do, you could also run your Function inside a container (medium.com/@santiagom.iglesias/…). Yes, you won't have the nice serverless/consumption model but you can put it inside a VNET or do whatever with it. E.g. run it in an Azure Container Instance (if you don't want to run the Function 24/7)Mirk
@Nancy: Not yet. Unfortunately it's not the only issue on our backlog. However, we're planning on planning a workshop/mini hackaton session at our office to try and find out what our options are. Depending on the result, I'll post an update here.Mehalek
@NancyXiong if you give the App Service access to the vnet with the vnet integration, wouldn't it be possible to force the function app / web app to take the route through the vnet by using the local ip address of the CosmosDB Service endpoint in the vnet? I'm not sure how that works with the DNS of the VNET though, and whether that should work automatically or not (I read something about an AppSetting on learn.microsoft.com/en-us/azure/app-service/…)Antipyrine
Reported as github.com/MicrosoftDocs/azure-docs/issues/28459 and github.com/MicrosoftDocs/azure-docs/issues/28461Antipyrine

© 2022 - 2024 — McMap. All rights reserved.