Support for User Assigned Managed identity - CosmosDB NoSQL / Container Apps
What happened?
A bug happened!
Version
Microsoft.DataApiBuilder 0.9.7+e560142426d1c080b9fd7b7fabff51a276f6bf61
What database are you using?
CosmosDB NoSQL
What hosting model are you using?
Container Apps
Which API approach are you accessing DAB through?
GraphQL
Relevant log output
Please support RBAC Authentication for CosmosDB via a ManagedIdentity. My company's security policies do not allow local authentication via keys.
I get the following error when trying to connect with the master key
{
"errors": [
{
"message": "Response status code does not indicate success: Unauthorized (401); Substatus: 5202; ActivityId: ccfad820-3641-48e2-8808-8b0c3993f8f8; Reason: (Local Authorization is disabled. Use an AAD token to authorize all requests.\r\nActivityId: ccfad820-3641-48e2-8808-8b0c3993f8f8, Microsoft.Azure.Documents.Common/2.14.0, Linux/2.0 cosmos-netstandard-sdk/3.19.3);",
"locations": [
{
"line": 2,
"column": 5
}
],
"path": [
"books"
]
}
]
}
For more info see...
https://joonasaijala.com/2021/07/01/how-to-using-managed-identities-to-access-cosmos-db-data-via-rbac-and-disabling-authentication-via-keys/
Code of Conduct
- [X] I agree to follow this project's Code of Conduct
I realized that if I didn't specify the "AccountKey" parameter in the connection string that the code would drop into trying to connect with a managed identity. However, I am using a user-assigned managed identity and now have this issue:
fail: Azure.DataApiBuilder.Service.Startup[0] A GraphQL request execution error occurred. Azure.Identity.AuthenticationFailedException: ManagedIdentityCredential authentication failed: Service request failed. Status: 400 (Bad Request)
Content:
{"statusCode":400,"message":"Unable to load the proper Managed Identity.","correlationId":"c4d8cdec-5a73-4a90-88f9-8b6fcef97dac"}
Headers:
Date: Wed, 20 Dec 2023 21:58:57 GMT
Server: Kestrel
Transfer-Encoding: chunked
X-CORRELATION-ID: REDACTED
Content-Type: application/json; charset=utf-8
See the troubleshooting guide for more information. https://aka.ms/azsdk/net/identity/managedidentitycredential/troubleshoot
---> Azure.RequestFailedException: Service request failed.
Status: 400 (Bad Request)
Content:
{"statusCode":400,"message":"Unable to load the proper Managed Identity.","correlationId":"c4d8cdec-5a73-4a90-88f9-8b6fcef97dac"}
Headers:
Date: Wed, 20 Dec 2023 21:58:57 GMT
Server: Kestrel
Transfer-Encoding: chunked
X-CORRELATION-ID: REDACTED
Content-Type: application/json; charset=utf-8
at Azure.Identity.ManagedIdentitySource.HandleResponseAsync(Boolean async, TokenRequestContext context, Response response, CancellationToken cancellationToken)
at Azure.Identity.ManagedIdentitySource.AuthenticateAsync(Boolean async, TokenRequestContext context, CancellationToken cancellationToken)
at Azure.Identity.ManagedIdentityClient.AuthenticateCoreAsync(Boolean async, TokenRequestContext context, CancellationToken cancellationToken)
at Azure.Identity.ManagedIdentityClient.AppTokenProviderImpl(AppTokenProviderParameters parameters)
at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.SendTokenRequestToAppTokenProviderAsync(ILoggerAdapter logger, CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.GetAccessTokenAsync(CancellationToken cancellationToken, ILoggerAdapter logger)
at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.ExecuteAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.ApiConfig.Executors.ConfidentialClientExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenForClientParameters clientParameters, CancellationToken cancellationToken)
at Azure.Identity.AbstractAcquireTokenParameterBuilderExtensions.ExecuteAsync[T](AbstractAcquireTokenParameterBuilder`1 builder, Boolean async, CancellationToken cancellationToken)
at Azure.Identity.MsalConfidentialClient.AcquireTokenForClientCoreAsync(String[] scopes, String tenantId, Boolean enableCae, Boolean async, CancellationToken cancellationToken)
at Azure.Identity.MsalConfidentialClient.AcquireTokenForClientAsync(String[] scopes, String tenantId, Boolean enableCae, Boolean async, CancellationToken cancellationToken)
at Azure.Identity.ManagedIdentityClient.AuthenticateAsync(Boolean async, TokenRequestContext context, CancellationToken cancellationToken)
at Azure.Identity.ManagedIdentityCredential.GetTokenImplAsync(Boolean async, TokenRequestContext requestContext, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at Microsoft.Azure.Cosmos.Routing.GlobalEndpointManager.GetAccountPropertiesHelper.GetAccountPropertiesAsync()
at Microsoft.Azure.Cosmos.GatewayAccountReader.InitializeReaderAsync()
at Microsoft.Azure.Cosmos.CosmosAccountServiceConfiguration.InitializeAsync()
at Microsoft.Azure.Cosmos.DocumentClient.InitializeGatewayConfigurationReaderAsync()
at Microsoft.Azure.Cosmos.DocumentClient.GetInitializationTaskAsync(IStoreClientFactory storeClientFactory)
at Microsoft.Azure.Cosmos.DocumentClient.EnsureValidClientAsync(ITrace trace)
at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.EnsureValidClientAsync(RequestMessage request, ITrace trace)
at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.SendAsync(RequestMessage request, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.SendAsync(String resourceUriString, ResourceType resourceType, OperationType operationType, RequestOptions requestOptions, ContainerInternal cosmosContainerCore, FeedRange feedRange, Stream streamPayload, Action`1 requestEnricher, ITrace trace, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.ContainerCore.ReadContainerAsync(ITrace trace, ContainerRequestOptions requestOptions, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.ClientContextCore.RunWithDiagnosticsHelperAsync[TResult](ITrace trace, Func`2 task)
at Microsoft.Azure.Cosmos.ClientContextCore.OperationHelperWithRootTraceAsync[TResult](String operationName, RequestOptions requestOptions, Func`2 task, TraceComponent traceComponent, TraceLevel traceLevel)
at Azure.DataApiBuilder.Core.Resolvers.CosmosQueryEngine.GetPartitionKeyPath(Container container, ISqlMetadataProvider metadataStoreProvider) in /src/src/Core/Resolvers/CosmosQueryEngine.cs:line 267
at Azure.DataApiBuilder.Core.Resolvers.CosmosQueryEngine.GetIdAndPartitionKey(IDictionary`2 parameters, Container container, CosmosQueryStructure structure, ISqlMetadataProvider metadataStoreProvider) in /src/src/Core/Resolvers/CosmosQueryEngine.cs:line 278
at Azure.DataApiBuilder.Core.Resolvers.CosmosQueryEngine.ExecuteAsync(IMiddlewareContext context, IDictionary`2 parameters, String dataSourceName) in /src/src/Core/Resolvers/CosmosQueryEngine.cs:line 72
at Azure.DataApiBuilder.Core.Services.ResolverMiddleware.InvokeAsync(IMiddlewareContext context) in /src/src/Core/Services/ResolverMiddleware.cs:line 106
at HotChocolate.Utilities.MiddlewareCompiler`1.ExpressionHelper.AwaitTaskHelper(Task task)
at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)
at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] Executed endpoint 'Hot Chocolate GraphQL Pipeline' info: Microsoft.AspNetCore.Hosting.Diagnostics[2] Request finished HTTP/1.1 POST http://graphqlapp.nicesky-e371e381.eastus2.azurecontainerapps.io/graphql application/json 117 - 500 - application/json;+charset=utf-8 68.6274ms
It appears this is a known issue with SDK's in Azure Container Apps as referenced here:
https://github.com/microsoft/azure-container-apps/issues/442
You may need to modify how you are getting the credential to allow for the config file to specify the AZURE_CLIENT_ID
Thank you for raising this issue. We are tracking this ask via #1944. Currently, only system assigned managed identities are supported.
we are actually using a user assigened managed identity and this does work. you need to specify the clientid in an environment variable called AZURE_CLIENT_ID in the container apps app.
Specifying AZURE_CLIENT_ID is the typical behavior for DefaultAzureCredential and this "trick" should work on many different Azure hosts where you want to use a user-assigned managed identity instead of system-assigned.
Thanks @dgcaron for sharing!
@dgcaron, @jmkelljr
You can see it implemented in https://github.com/Azure-Samples/dab-azure-cosmos-db-nosql-quickstart
Here's some examples:
- Bicep: https://github.com/Azure-Samples/dab-azure-cosmos-db-nosql-quickstart/blob/main/infra/main.bicep#L216-L219
- Config: https://github.com/Azure-Samples/dab-azure-cosmos-db-nosql-quickstart/blob/main/src/api/dab-config.json#L5
To make it happen, the connection string should be of syntax: AccountEndpoint=<cosmos-db-nosql-account-endpoint>; and you must specify the AZURE_CLIENT_ID environment variable or specify it in the TokenCredential in code.
CC: @seantleonard, @sajeetharan