0

I am following this tutorial to get started on cosmos db - https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/quickstart-dotnet?tabs=azure-portal%2Cwindows%2Cpasswordless%2Csign-in-azure-cli#create-account

In the tutorial, cosmos URI and PRIMARY KEY are exported in environment variables and used in .cs code. However, only the uri is used. They key is not used. That I believe is causing my program to fail authentication when I create an item.

The .cs code is

// See https://aka.ms/new-console-template for more information
using Microsoft.Azure.Cosmos;
using Azure.Identity;

// New instance of CosmosClient class
using CosmosClient client = new(
    accountEndpoint: Environment.GetEnvironmentVariable("COSMOS_ENDPOINT"),
    tokenCredential: new DefaultAzureCredential()
);




// Database reference with creation if it does not already exist
Database database = client.GetDatabase(id: "cosmicworks");

Console.WriteLine("Hello, World!");

Console.WriteLine($"New database:\t{database.Id}");


// Container reference with creation if it does not alredy exist
Container container = database.GetContainer(id: "products");

Console.WriteLine($"New container:\t{container.Id}");

//THE PROGRAM FAILS SOMEWHERE AFTER THE ABOVE PRINT

// Create new object and upsert (create or replace) to container
Product newItem = new(
    id: "70b63682-b93a-4c77-aad2-65501347265f",
    categoryId: "61dba35b-4f02-45c5-b648-c6badc0cbd79",
    categoryName: "gear-surf-surfboards",
    name: "Yamba Surfboard",
    quantity: 12,
    sale: false
);

Product createdItem = await container.CreateItemAsync<Product>(
    item: newItem//,
    //partitionKey: new PartitionKey("61dba35b-4f02-45c5-b648-c6badc0cbd79")
);

Console.WriteLine($"Created item:\t{createdItem.id}\t[{createdItem.categoryName}]");

// Point read item from container using the id and partitionKey
Product readItem = await container.ReadItemAsync<Product>(
    id: "70b63682-b93a-4c77-aad2-65501347265f",
    partitionKey: new PartitionKey("61dba35b-4f02-45c5-b648-c6badc0cbd79")
);

// Create query using a SQL string and parameters
var query = new QueryDefinition(
    query: "SELECT * FROM products p WHERE p.categoryId = @categoryId"
)
    .WithParameter("@categoryId", "61dba35b-4f02-45c5-b648-c6badc0cbd79");

using FeedIterator<Product> feed = container.GetItemQueryIterator<Product>(
    queryDefinition: query
);

while (feed.HasMoreResults)
{
    FeedResponse<Product> response = await feed.ReadNextAsync();
    foreach (Product item in response)
    {
        Console.WriteLine($"Found item:\t{item.name}");
    }
}


// C# record representing an item in the container
public record Product(
    string id,
    string categoryId,
    string categoryName,
    string name,
    int quantity,
    bool sale
);

The output is

PS /home/manu> dotnet run                         
Hello, World!
New database:   cosmicworks
New container:  products
Unhandled exception. Azure.Identity.AuthenticationFailedException: ManagedIdentityCredential authentication failed: Service request failed.
Status: 400 (Bad Request)

Content:


Headers:
X-Powered-By: REDACTED
ETag: W/"43d-rxaaxO4nRZ43QXDzZ9Qicz6SZeY"
Date: Tue, 28 Feb 2023 06:24:19 GMT
Connection: keep-alive
Keep-Alive: REDACTED
Content-Type: application/json; charset=utf-8
Content-Length: 1085

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:


Headers:
X-Powered-By: REDACTED
ETag: W/"43d-rxaaxO4nRZ43QXDzZ9Qicz6SZeY"
Date: Tue, 28 Feb 2023 06:24:19 GMT
Connection: keep-alive
Keep-Alive: REDACTED
Content-Type: application/json; charset=utf-8
Content-Length: 1085

   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.SendTokenRequestToProviderAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.FetchNewAccessTokenAsync(CancellationToken cancellationToken)
   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 async, CancellationToken cancellationToken)
   at Azure.Identity.MsalConfidentialClient.AcquireTokenForClientAsync(String[] scopes, String tenantId, 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.TokenCredentialCache.RefreshCachedTokenWithRetryHelperAsync(ITrace trace)
   at Microsoft.Azure.Cosmos.TokenCredentialCache.RefreshCachedTokenWithRetryHelperAsync(ITrace trace)
   at Microsoft.Azure.Cosmos.TokenCredentialCache.GetNewTokenAsync(ITrace trace)
   at Microsoft.Azure.Cosmos.TokenCredentialCache.GetTokenAsync(ITrace trace)
   at Microsoft.Azure.Cosmos.AuthorizationTokenProviderTokenCredential.AddAuthorizationHeaderAsync(INameValueCollection headersCollection, Uri requestAddress, String verb, AuthorizationTokenType tokenType)
   at Microsoft.Azure.Cosmos.GatewayAccountReader.GetDatabaseAccountAsync(Uri serviceEndpoint)
   at Microsoft.Azure.Cosmos.Routing.GlobalEndpointManager.GetAccountPropertiesHelper.GetAndUpdateAccountPropertiesAsync(Uri endpoint)
   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.Documents.BackoffRetryUtility`1.ExecuteRetryAsync[TParam,TPolicy](Func`1 callbackMethod, Func`3 callbackMethodWithParam, Func`2 callbackMethodWithPolicy, TParam param, IRetryPolicy retryPolicy, IRetryPolicy`1 retryPolicyWithArg, Func`1 inBackoffAlternateCallbackMethod, Func`2 inBackoffAlternateCallbackMethodWithPolicy, TimeSpan minBackoffForInBackoffCallback, CancellationToken cancellationToken, Action`1 preRetryCallback)
   at Microsoft.Azure.Documents.ShouldRetryResult.ThrowIfDoneTrying(ExceptionDispatchInfo capturedException)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetryAsync[TParam,TPolicy](Func`1 callbackMethod, Func`3 callbackMethodWithParam, Func`2 callbackMethodWithPolicy, TParam param, IRetryPolicy retryPolicy, IRetryPolicy`1 retryPolicyWithArg, Func`1 inBackoffAlternateCallbackMethod, Func`2 inBackoffAlternateCallbackMethodWithPolicy, TimeSpan minBackoffForInBackoffCallback, CancellationToken cancellationToken, Action`1 preRetryCallback)
   at Microsoft.Azure.Cosmos.AsyncCacheNonBlocking`2.GetAsync(TKey key, Func`2 singleValueInitFunc, Func`2 forceRefresh)
   at Microsoft.Azure.Cosmos.DocumentClient.EnsureValidClientAsync(ITrace trace)
   at Microsoft.Azure.Cosmos.DocumentClient.GetCollectionCacheAsync(ITrace trace)
   at Microsoft.Azure.Cosmos.ContainerCore.GetCachedContainerPropertiesAsync(Boolean forceRefresh, ITrace trace, CancellationToken cancellationToken)
   at Microsoft.Azure.Cosmos.ContainerCore.GetPartitionKeyDefinitionAsync(CancellationToken cancellationToken)
   at Microsoft.Azure.Cosmos.ContainerCore.ExtractPartitionKeyAndProcessItemStreamAsync[T](Nullable`1 partitionKey, String itemId, T item, OperationType operationType, ItemRequestOptions requestOptions, ITrace trace, CancellationToken cancellationToken)
   at Microsoft.Azure.Cosmos.ContainerCore.CreateItemAsync[T](T item, ITrace trace, Nullable`1 partitionKey, ItemRequestOptions requestOptions, CancellationToken cancellationToken)
   at Microsoft.Azure.Cosmos.ClientContextCore.RunWithDiagnosticsHelperAsync[TResult](String containerName, String databaseName, OperationType operationType, ITrace trace, Func`2 task, Func`2 openTelemetry, String operationName, RequestOptions requestOptions)
   at Microsoft.Azure.Cosmos.ClientContextCore.OperationHelperWithRootTraceAsync[TResult](String operationName, String containerName, String databaseName, OperationType operationType, RequestOptions requestOptions, Func`2 task, Func`2 openTelemetry, TraceComponent traceComponent, TraceLevel traceLevel)
   at Program.<Main>$(String[] args) in /home/manu/Program.cs:line 38
   at Program.<Main>(String[] args)
PS /home/manu>                       

I have two questions

  1. If I want to use the PRIMARY KEY as environment variable in the code, where shall I use it in the .cs code.

  2. If I want to use key-vault and not use environment variables, how do I point the secrets for uri and key to .cs program? I checked this tutorial but it doesn't tell me how to integrate the keyvault with the .cs program I am runnning in powershell. https://learn.microsoft.com/en-us/azure/cosmos-db/store-credentials-key-vault

David Makogon
  • 69,407
  • 21
  • 141
  • 189
Manu Chadha
  • 15,555
  • 19
  • 91
  • 184
  • fyi even if you get this demo running, it'll only work *once* - you've hardcoded an id into your doc instead of generating something unique (plus, not mandatory to provide one, since id's are auto-generated if you don't provide one) – David Makogon Feb 28 '23 at 13:57

2 Answers2

0

I know part of the answer. To use the environment variables, I need

using CosmosClient client = new(
    accountEndpoint: Environment.GetEnvironmentVariable("COSMOS_ENDPOINT")!,
    authKeyOrResourceToken: Environment.GetEnvironmentVariable("COSMOS_KEY")!
);

The answer is in https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-dotnet-get-started?tabs=azure-cli%2Cwindows#connect-to-azure-cosmos-db-sql-api

I don't know yet how to use key vault.

Manu Chadha
  • 15,555
  • 19
  • 91
  • 184
0

The failure is not related to Cosmos DB.

The failure is related to the running application not being able to obtain the MSI token.

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.SendTokenRequestToProviderAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.FetchNewAccessTokenAsync(CancellationToken cancellationToken)
   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 async, CancellationToken cancellationToken)
   at Azure.Identity.MsalConfidentialClient.AcquireTokenForClientAsync(String[] scopes, String tenantId, 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)
   

The Cosmos DB SDK attempts to call Azure Identity to obtain a token to be sent to authenticate with Cosmos DB but the call fails on Azure Identity.

You are using new DefaultAzureCredential() which is a type of token.

Your application should be running somewhere in Azure (you can't use DefaultAzureCredential locally in your machine) and depending on which environment you are running, there are different troubleshooting steps. The error message contains a URL to visit with the relevant information for each environment: https://aka.ms/azsdk/net/identity/managedidentitycredential/troubleshoot

If you are using new DefaultAzureCredential() running on your machine, that is the source of this error.

Matias Quaranta
  • 13,907
  • 1
  • 22
  • 47