title | description | author | ms.service | ms.subservice | ms.topic | ms.date | ms.author | ms.reviewer | ms.custom |
---|---|---|---|---|---|---|---|---|---|
Use system-assigned managed identities to access Azure Cosmos DB data |
Learn how to configure an Azure Active Directory (Azure AD) system-assigned managed identity (managed service identity) to access keys from Azure Cosmos DB. |
seesharprun |
cosmos-db |
cosmosdb-sql |
how-to |
06/01/2022 |
sidandrews |
justipat |
devx-track-csharp, devx-track-azurecli, subject-rbac-steps |
[!INCLUDE appliesto-sql-api]
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.
- An Azure account with an active subscription. Create an account for free.
- An existing Azure Cosmos DB SQL API account. Create an Azure Cosmos DB SQL API 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.
-
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 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 Cosmos DB account's properties using
az cosmosdb show
.az cosmosdb show \ --resource-group $resourceGroupName \ --name $cosmosName
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
In this step, you'll query the document endpoint for the SQL API 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.
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 this solution, you'll use the Cosmos DB Built-in Data Reader role.
Tip
When you assign roles, assign only the needed access. If your service requires only reading data, then assign the Cosmos DB Built-in Data Reader role to the managed identity. 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 object with the configuration of the new custom role.
{ "RoleName": "Read Cosmos Metadata", "Type": "CustomRole", "AssignableScopes": ["/"], "Permissions": [{ "DataActions": [ "Microsoft.DocumentDB/databaseAccounts/readMetadata" ] }] }
-
Use
az role assignment create
to assign theCosmos DB Built-in Data Reader
role to the system-assigned managed identity.az cosmosdb sql role assignment create \ --resource-group $resourceGroupName \ --account-name $cosmosName \ --role-definition-name "Read Cosmos Metadata" \ --principal-id $principal \ --scope $scope
We now have a function app that has a system-assigned managed identity with the Cosmos DB Built-in Data Reader 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); } } }
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
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