Use system-assigned managed identities to access Azure Cosmos DB data
APPLIES TO: NoSQL
In this article, you'll set up a robust, key rotation agnostic solution to access Azure Cosmos DB keys by using managed identities and data plane role-based access control. The example in this article uses Azure Functions, but you can use any service that supports managed identities.
You'll learn how to create a function app that can access Azure Cosmos DB data without needing to copy any Azure Cosmos DB keys. The function app will trigger when an HTTP request is made and then list all of the existing databases.
Prerequisites
- An Azure account with an active subscription. Create an account for free.
- An existing Azure Cosmos DB API for NoSQL account. Create an Azure Cosmos DB API for NoSQL account
- An existing Azure Functions function app. Create your first function in the Azure portal
- A system-assigned managed identity for the function app. Add a system-assigned identity
- Azure Functions Core Tools
- To perform the steps in this article, install the Azure CLI and sign in to Azure.
Prerequisite check
In a terminal or command window, store the names of your Azure Functions function app, Azure Cosmos DB account and resource group as shell variables named
functionName
,cosmosName
, andresourceGroupName
.# Variable for function app name functionName="msdocs-function-app" # Variable for Azure Cosmos DB account name cosmosName="msdocs-cosmos-app" # Variable for resource group name resourceGroupName="msdocs-cosmos-functions-dotnet-identity"
Note
These variables will be re-used in later steps. This example assumes your Azure Cosmos DB account name is
msdocs-cosmos-app
, your function app name ismsdocs-function-app
and your resource group name ismsdocs-cosmos-functions-dotnet-identity
.View the function app's properties using the
az functionapp show
command.az functionapp show \ --resource-group $resourceGroupName \ --name $functionName
View the properties of the system-assigned managed identity for your function app using
az webapp identity show
.az webapp identity show \ --resource-group $resourceGroupName \ --name $functionName
View the Azure Cosmos DB account's properties using
az cosmosdb show
.az cosmosdb show \ --resource-group $resourceGroupName \ --name $cosmosName
Create Azure Cosmos DB API for NoSQL databases
In this step, you'll create two databases.
In a terminal or command window, create a new
products
database usingaz cosmosdb sql database create
.az cosmosdb sql database create \ --resource-group $resourceGroupName \ --name products \ --account-name $cosmosName
Create a new
customers
database.az cosmosdb sql database create \ --resource-group $resourceGroupName \ --name customers \ --account-name $cosmosName
Get Azure Cosmos DB API for NoSQL endpoint
In this step, you'll query the document endpoint for the API for NoSQL account.
Use
az cosmosdb show
with the query parameter set todocumentEndpoint
. Record the result. You'll use this value in a later step.az cosmosdb show \ --resource-group $resourceGroupName \ --name $cosmosName \ --query documentEndpoint cosmosEndpoint=$( az cosmosdb show \ --resource-group $resourceGroupName \ --name $cosmosName \ --query documentEndpoint \ --output tsv ) echo $cosmosEndpoint
Note
This variable will be re-used in a later step.
Grant access to your Azure Cosmos DB account
In this step, you'll assign a role to the function app's system-assigned managed identity. Azure Cosmos DB has multiple built-in roles that you can assign to the managed identity for control-plane access. For data-plane access, you'll create a new custom role with access to read metadata.
Tip
For more information about the importance of least privilege access, see the Lower exposure of privileged accounts article.
Use
az cosmosdb show
with the query parameter set toid
. Store the result in a shell variable namedscope
.scope=$( az cosmosdb show \ --resource-group $resourceGroupName \ --name $cosmosName \ --query id \ --output tsv ) echo $scope
Note
This variable will be re-used in a later step.
Use
az webapp identity show
with the query parameter set toprincipalId
. Store the result in a shell variable namedprincipal
.principal=$( az webapp identity show \ --resource-group $resourceGroupName \ --name $functionName \ --query principalId \ --output tsv ) echo $principal
Create a new JSON file with the configuration of the new custom role.
{ "RoleName": "Read Azure Cosmos DB Metadata", "Type": "CustomRole", "AssignableScopes": ["/"], "Permissions": [{ "DataActions": [ "Microsoft.DocumentDB/databaseAccounts/readMetadata" ] }] }
Tip
You can create a file in the Azure Cloud Shell using either
touch <filename>
or the built-in editor (code .
). For more information, see Azure Cloud Shell editorUse
az cosmosdb sql role definition create
to create a new role definition namedRead Azure Cosmos DB Metadata
using the custom JSON object.az cosmosdb sql role definition create \ --resource-group $resourceGroupName \ --account-name $cosmosName \ --body @definition.json
Note
In this example, the role definition is defined in a file named definition.json.
Use
az role assignment create
to assign theRead Azure Cosmos DB Metadata
role to the system-assigned managed identity.az cosmosdb sql role assignment create \ --resource-group $resourceGroupName \ --account-name $cosmosName \ --role-definition-name "Read Azure Cosmos DB Metadata" \ --principal-id $principal \ --scope $scope
Programmatically access the Azure Cosmos DB keys
We now have a function app that has a system-assigned managed identity with the custom role. The following function app will query the Azure Cosmos DB account for a list of databases.
Create a local function project with the
--dotnet
parameter in a folder namedcsmsfunc
. Change your shell's directoryfunc init csmsfunc --dotnet cd csmsfunc
Create a new function with the template parameter set to
httptrigger
and the name set toreaddatabases
.func new --template httptrigger --name readdatabases
Add the
Azure.Identity
andMicrosoft.Azure.Cosmos
NuGet package to the .NET project. Build the project usingdotnet build
.dotnet add package Azure.Identity dotnet add package Microsoft.Azure.Cosmos dotnet build
Open the function code in an integrated developer environment (IDE).
Tip
If you are using the Azure CLI locally or in the Azure Cloud Shell, you can open Visual Studio Code.
code .
Replace the code in the readdatabases.cs file with this sample function implementation. Save the updated file.
using System; using System.Collections.Generic; using System.Threading.Tasks; using Azure.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Cosmos; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace csmsfunc { public static class readdatabases { [FunctionName("readdatabases")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req, ILogger log) { log.LogTrace("Start function"); CosmosClient client = new CosmosClient( accountEndpoint: Environment.GetEnvironmentVariable("COSMOS_ENDPOINT", EnvironmentVariableTarget.Process), new DefaultAzureCredential() ); using FeedIterator<DatabaseProperties> iterator = client.GetDatabaseQueryIterator<DatabaseProperties>(); List<(string name, string uri)> databases = new(); while(iterator.HasMoreResults) { foreach(DatabaseProperties database in await iterator.ReadNextAsync()) { log.LogTrace($"[Database Found]\t{database.Id}"); databases.Add((database.Id, database.SelfLink)); } } return new OkObjectResult(databases); } } }
(Optional) Run the function locally
In a local environment, the DefaultAzureCredential
class will use various local credentials to determine the current identity. While running locally isn't required for the how-to, you can develop locally using your own identity or a service principal.
In the local.settings.json file, add a new setting named
COSMOS_ENDPOINT
in the Values object. The value of the setting should be the document endpoint you recorded earlier in this how-to guide.... "Values": { ... "COSMOS_ENDPOINT": "https://msdocs-cosmos-app.documents.azure.com:443/", ... } ...
Note
This JSON object has been shortened for brevity. This JSON object also includes a sample value that assumes your account name is
msdocs-cosmos-app
.Run the function app
func start
Deploy to Azure
Once published, the DefaultAzureCredential
class will use credentials from the environment or a managed identity. For this guide, the system-assigned managed identity will be used as a credential for the CosmosClient
constructor.
Set the
COSMOS_ENDPOINT
setting on the function app already deployed in Azure.az functionapp config appsettings set \ --resource-group $resourceGroupName \ --name $functionName \ --settings "COSMOS_ENDPOINT=$cosmosEndpoint"
Deploy your function app to Azure by reusing the
functionName
shell variable:func azure functionapp publish $functionName
Next steps
Feedback
https://aka.ms/ContentUserFeedback.
Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see:Submit and view feedback for