How to: Using managed identities to access Cosmos DB data via RBAC and disabling authentication via keys

Historically, authenticating against Microsoft’s Cosmos DB service has only been possible in one of two ways: Either by using keys – long passwords that grant you either full read or read-write access to all data in the Cosmos DB account – or by building a custom user management solution that generates short-lived tokens to specific Cosmos DB containers or items. Generally speaking, the first option has been the go-to choice with all server and daemon applications and other Azure products with their own Cosmos DB-connectors, while the second option has been a way to give client applications such as mobile phone apps a way to connect to Cosmos DB without storing keys on the end-user’s device.

After all, the keys are very sensitive pieces of information that give you access to not just a lot of data, but to all of it. Personally, I haven’t been a big fan of key-based authentication, and fortunately Microsoft updated Cosmos DB with role-based access control (or RBAC) at Build 2021. With the new RBAC capabilities it is now possible to give any Azure AD principals – users, security groups, service principals and managed identities – either read-only or read-write access to Cosmos DB data. The access can also be scoped to the entire Cosmos DB account, specific databases, or even specific containers. What’s also exciting is that with this update it became possible to also entirely disable authentication via keys, so not only is it possible to perform authentication in a smarter way, you can also clamp down on potentially vulnerable authentication methods. Because otherwise if a malicious person would get a hold of your Cosmos DB keys, they would be able to get access to all of your data!

NOTE! If you read on Cosmos DB security from Microsoft Docs you might come upon an older article “Use system-assigned managed identities to access Azure Cosmos DB data.” This article describes how to use managed identities and RBAC to retrieve Cosmos DB keys programmatically to avoid storing them in Key Vault or configuration files. However, this authentication method still, at the end of the day, relied on keys and wasn’t based on RBAC alone.

In this blog post I’ll describe how to update your existing .NET Azure-applications to use managed identities for accessing Cosmos DB. The same principles will apply to other programming languages as well, and you can also apply these concepts to e.g., Data Factory or Azure VM managed identities. The overall steps are:

  1. Creating a managed identity for your Azure App
  2. Creating a Cosmos DB role assignment for your managed identity
  3. Updating your application code to use the new authentication method
  4. Optional: Disabling Cosmos DB authentication using keys

I’ve left the last part optional, simply because while it’s a good practice to disable key-based authentication, currently the Cosmos DB Data Explorer in Azure Portal uses key-based authentication, and disabling the authentication also disables the Data Explorer itself. Hopefully, the Data Explorer will be updated to use RBAC as well, but for the time being I’ll leave it up to you whether to disable key-based auth or not.

With that said, let’s get started!

1. Creating a managed identity for your Azure App

In case your Azure App does not have a managed identity yet, creating one is super simple:

  1. Open the Azure App (App Service, Function App or even Logic App) in Azure Portal and select “Identity” from the Settings
     
  1. Toggle the Status-setting “On” and click Save
     
  1. And that’s all, just copy the managed identity’s Object ID somewhere because you’ll need it in the next step
     

NOTE! If you are using Data Factory or some other Azure service with its own built-in managed identities, you can easily use those to authenticate against Cosmos DB as well. All you need to do is retrieve the Object ID associated with that managed identity and move on to the next step.

2. Creating a Cosmos DB role assignment for your managed identity

Before your managed identity can access Cosmos DB data you need to provide it with a role assignment. These role assignments can be either read-only or read-write, and you can scope them to the entire Cosmos DB account, a specific database within the account, or a specific container within a specific database. At the time being these role assignments cannot be done via Azure Portal, so I will be showing how to do them with PowerShell. For the script you will also need the name of the resource group your Cosmos DB account is in, the name of the Cosmos DB account itself and the managed identity’s object ID from the previous chapter.

NOTE! The New-AzCosmosDBSqlRoleAssignment -commandlet is part of the Azure Az PowerShell module. If you don’t have it installed yet, I recommend checking out Microsoft’s official installation docs!

Below is a template script for assigning permissions for a managed identity into Cosmos DB:

$resourceGroupName = "<myResourceGroup>"
$accountName = "<myCosmosAccount>"
$roleDefinitionId = "<roleDefinitionId>"
$principalId = "<aadPrincipalId>"
New-AzCosmosDBSqlRoleAssignment -AccountName $accountName `
    -ResourceGroupName $resourceGroupName `
    -RoleDefinitionId $roleDefinitionId `
    -Scope "/" `
    -PrincipalId $principalId

Before you can run the script, perform the following edits to it:

  1. Add the name of your Cosmos DB resource group on line 1 of the script
  2. Add the name of your Cosmos DB account on line 2 of the script
  3. Add the GUID of your role definition on line 3 of the script. This value is going to be one of the following:
    Read-only permissions: 00000000-0000-0000-0000-000000000001
    Read-write permissions: 00000000-0000-0000-0000-000000000002
  4. Add the object ID of your managed identity on line 4 of the script

In addition, if you want to specify the scope of the role assignment to a specific database or container, you can edit the Scope -parameter (line 8) as follows:

/ (account-level)
/dbs/<database-name> (database-level)
/dbs/<database-name>/colls/<container-name> (container-level)

After running the edited script your managed identity will now have either read-only or read-write access to Cosmos DB data. At the time of writing these role assignments are not yet visible in Azure Portal, so the only way to verify the assignment is by either testing the connection or by using the Get-AzCosmosDBSqlRoleAssignment -commandlet. But because I’m pretty sure you did everything correctly let’s skip that and move straight on to updating the application code! 🙂

3. Updating your application code to use the new authentication method

The code changes required to use managed identities with Cosmos DB are very small. However, first things first, if you are using a version older than 3.19.0 of the Microsoft.Azure.Cosmos nuget package you must update it, since earlier versions of the class library do not support RBAC authentication.

Once you have the nuget package updated you can edit your code. All that you actually need to do is change how you create new objects of the CosmosClient-class. Change the first parameter (which previously was your Cosmos DB connection string) to be the URL of your Cosmos DB account and then provide an instance of DefaultAzureCredentials-class (or ManagedIdentityCredential-class) from the Azure.Identity nuget package as the second parameter. Here’s an example:

string cosmosUrl = "https://<Cosmos DB Account>.documents.azure.com:443/";
CosmosClient client = new CosmosClient(cosmosUrl, new DefaultAzureCredentials());

And that’s it! Really! Just with that small change you can update your app for testing (or, if you’re brave, straight to production).

4. Optional: Disabling Cosmos DB authentication using keys

As mentioned above, with the release of RBAC for Cosmos DB Microsoft also made it possible to disable authentication using keys. In terms of security this is a smart thing to do, however at the time being disabling auth keys also breaks using Cosmos DB’s Data Explorer in Azure Portal, so until Microsoft gets the Data Explorer working on RBAC you’ll have to make a choice yourself on whether to proceed with disabling keys or not. Also, if you do decide on disabling key authentication doing so is slightly more complicated since it can currently be done only via ARM templates. Here’s the steps for how to get it done:

  1. First you need to get an ARM template for your Cosmos DB account. Open the Cosmos DB resource in Azure Portal and select Export template.
     
  1. From the ARM template JSON copy the first object of the resources-array into Notepad (with type “Microsoft.DocumentDB/databaseAccounts”). The object should start on line 12 and is about 50 lines long.
     
  1. In the copied template perform the following edits:
    1. On line 4 (the name-attribute) update the value to your Cosmos DB account’s name
    2. On line 15 (inside the properties-attribute) add a new attribute “disableLocalAuth” with the value “true”
    3. If the apiVersion (line 3) is older than “2021-06-15” update that to “2021-06-15”

Below is an example of an editet ARM template:

 {
	            "type": "Microsoft.DocumentDB/databaseAccounts",
	            "apiVersion": "2021-06-15",
	            "name": "jodcosmos",
	            "location": "West Europe",
	            "tags": {
	                "defaultExperience": "Core (SQL)",
	                "hidden-cosmos-mmspecial": ""
	            },
	            "kind": "GlobalDocumentDB",
	            "identity": {
	                "type": "None"
	            },
	            "properties": {
	                "disableLocalAuth": true,
	                "publicNetworkAccess": "Enabled",
	                "enableAutomaticFailover": false,
	                "enableMultipleWriteLocations": false,
	                "isVirtualNetworkFilterEnabled": false,
	                "virtualNetworkRules": [],
	                "disableKeyBasedMetadataWriteAccess": false,
	                "enableFreeTier": true,
	                "enableAnalyticalStorage": false,
	                "analyticalStorageConfiguration": {},
	                "databaseAccountOfferType": "Standard",
	                "defaultIdentity": "FirstPartyIdentity",
	                "networkAclBypass": "None",
	                "consistencyPolicy": {
	                    "defaultConsistencyLevel": "Session",
	                    "maxIntervalInSeconds": 5,
	                    "maxStalenessPrefix": 100
	                },
	                "locations": [
	                    {
	                        "locationName": "West Europe",
	                        "provisioningState": "Succeeded",
	                        "failoverPriority": 0,
	                        "isZoneRedundant": false
	                    }
	                ],
	                "cors": [],
	                "capabilities": [],
	                "ipRules": [],
	                "backupPolicy": {
	                    "type": "Periodic",
	                    "periodicModeProperties": {
	                        "backupIntervalInMinutes": 240,
	                        "backupRetentionIntervalInHours": 8
	                    }
	                },
	                "networkAclBypassResourceIds": []
	            }
        }
  1. Next, copy the following ARM template to a different Notepad. Add the previously edited Cosmos DB template to line 19 of this template, replacing the text “<COSMOS DB RESOURCE GOES HERE>.” This parent template tells Azure that it should update your existing Cosmos DB account instead of provisioning a new one with the mode-attribute’s “Incremental”-value on line 11.
	{
	  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
	  "contentVersion": "1.0.0.0",
	  "parameters": {},
	  "resources": [
	    {
	    "apiVersion": "2015-01-01",
	    "type": "Microsoft.Resources/deployments",
	    "name": "updateCosmos",
	    "properties": {
	      "mode": "Incremental",
	      "parameters": {},
	      "template": {
	        "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
	        "contentVersion": "1.0.0.0",
	        "parameters": {},
	        "variables": {},   
		    "resources": [
		      <COSMOS DB RESOURCE GOES HERE>
		    ],
	        "outputs": {}
	        }
	      }
	    }
	  ],
	  "outputs": {}
}
  1. With that your ARM template to update the Cosmos DB account to disable key based auth is done, and all that’s left is to apply the template. You can do this easily in Azure Portal by creating a new “Template deployment” resource:
     
  1. With the Template deployment resource click the “Build your own template in the editor” -link and copy-paste the ARM template from step #4. Click Save.
     
  1. Next, select the resource group of your Cosmos DB account and “Create” the custom deployment

And once the template deployment is done key based authentication should be disabled! Note that since there is currently no visible setting for this in Azure Portal and Azure PowerShell nor Azure CLI yet support this option, the only way to verify that the update worked is by testing it out. The easiest way to do this is by opening your Cosmos DB database in Data Explorer and trying to see items in your containers: If the update succeeded, you’ll be greeted with an error message:

That’s all for now! This new RBAC authentication method for Cosmos DB is definitely a step in the right direction for Microsoft, although as you can see, there’s still definitely plenty of improvements to be done on the side of Azure Portal to fully support it. But I’m pretty sure those features will be improved given a little bit of time. Until then we’ll just have to keep our PowerShell commandlets and ARM templates at hand. 😀


One thought on “How to: Using managed identities to access Cosmos DB data via RBAC and disabling authentication via keys

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s