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
Gateway
Cosmos DB
Function App
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.