Connect to Azure Cosmos DB for Table using role-based access control and Microsoft Entra ID

Role-based access control refers to a method to manage access to resources in Azure. This method is based on specific identities being assigned roles that manage what level of access they have to one or more resources. Role-based access control provides a flexible system of fine-grained access management that ensures identities only have the least privileged level of access they need to perform their task.

For more information, see role-based access control.

Prerequisites

  • An Azure account with an active subscription. Create an account.

  • An existing Azure Cosmos DB for Table account.

  • One or more existing identities in Microsoft Entra ID.

You can use the local Azure CLI.

Disable key-based authentication

Disabling key-based authorization prevents your account from being used without the more secure Microsoft Entra ID authentication method. This procedure is a step that should be performed on new accounts in secure workloads. Alternatively, perform this procedure on existing accounts being migrated to a secure workload pattern.

First, disable key-based authentication to your existing account so that applications are required to use Microsoft Entra ID authentication. Use az resource update to modify properties.disableLocalAuth of the existing account.

az resource update \
    --resource-group "<name-of-existing-resource-group>" \
    --name "<name-of-existing-account>" \
    --resource-type "Microsoft.DocumentDB/databaseAccounts" \
    --set properties.disableLocalAuth=true

First, create a new account with key-based authentication disabled so that applications are required to use Microsoft Entra authentication.

  1. Create a new Bicep file to deploy your new account with key-based authentication disabled. Name the file deploy-new-account.bicep.

    metadata description = 'Deploys a new Azure Cosmos DB account with key-based auth disabled.'
    
    @description('Name of the Azure Cosmos DB account.')
    param name string = 'csms-${uniqueString(resourceGroup().id)}'
    
    @description('Primary location for the Azure Cosmos DB account.')
    param location string = resourceGroup().location
    
    resource account 'Microsoft.DocumentDB/databaseAccounts@2024-05-15' = {
      name: name
      location: location
      kind: 'GlobalDocumentDB'
      properties: {
        databaseAccountOfferType: 'Standard'
        locations: [
          {
            locationName: location
          }
        ]
        disableLocalAuth: true
      }
    }
    
  2. Use az deployment group create to deploy the Bicep file with the new account.

    az deployment group create \
        --resource-group "<name-of-existing-resource-group>" \
        --template-file deploy-new-account.bicep
    

First, disable key-based authentication to your existing account so that applications are required to use Microsoft Entra authentication. Use Get-AzResource and Set-AzResource to respectively read and update the existing account.

$parameters = @{
    ResourceGroupName = "<name-of-existing-resource-group>"
    ResourceName = "<name-of-existing-account>"
    ResourceType = "Microsoft.DocumentDB/databaseAccounts"
}
$resource = Get-AzResource @parameters

$resource.Properties.DisableLocalAuth = $true

$resource | Set-AzResource -Force

Use these steps to create a new Azure Cosmos DB for NoSQL account with key-based authentication disabled so that applications are required to only use Microsoft Entra authentication.

  1. When setting up a new Azure Cosmos DB for NoSQL account, navigate to the Security section of the account creation process.

  2. Then, select Disable for the Key-based authentication option.

    Screenshot of the option to disable key-based authentication when creating a new account in the Azure portal.

Important

Modifying an Azure Cosmos DB account requires an Azure role with at least the Microsoft.DocumentDb/databaseAccounts/*/write permission. For more information, see permissions for Azure Cosmos DB.

Validate that key-based authentication is disabled

To validate that key-based access is disabled, attempt to use the Azure SDK to connect to Azure Cosmos DB for Table using a resource-owner password credential (ROPC). This attempt should fail. If necessary, code samples for common programming languages are provided here.

using Azure.Data.Tables;
using Azure.Core;

string connectionString = "AccountEndpoint=<table-endpoint>;AccountKey=<key>;";

TableServiceClient client = new(connectionString);
const { TableServiceClient } = require('@azure/data-tables');

const connectionString = 'AccountEndpoint=<table-endpoint>;AccountKey=<key>;';

const client = new TableServiceClient(connectionString);
import { TableServiceClient } from '@azure/data-tables';

let connectionString: string = 'AccountEndpoint=<table-endpoint>;AccountKey=<key>;';

const client: TableServiceClient = new TableServiceClient(connectionString);
from azure.data.tables import TableServiceClient

connection_string = "AccountEndpoint=<table-endpoint>;AccountKey=<key>;"

client = TableServiceClient(endpoint, connection_string)
package main

import (
    "github.com/Azure/azure-sdk-for-go/sdk/data/aztables"
)

const connectionString = "AccountEndpoint=<table-endpoint>;AccountKey=<key>;"

func main() {
  client, _ := aztables.NewServiceClientFromConnectionString(connectionString, nil)
}
import com.azure.data.tables.TableServiceClient;
import com.azure.data.tables.TableServiceClientBuilder;

public class Table{
    public static void main(String[] args){
        TableServiceClient tableServiceClient = new TableServiceClientBuilder()
            .connectionString("AccountEndpoint=<table-endpoint>;AccountKey=<key>;")
            .buildClient();
    }
}

Grant control plane role-based access

Control plane access refers to the ability to manage resources for an Azure service without managing data. For example, Azure Cosmos DB control plane access could include the ability to:

  • Read all account and resource metadata
  • Read and regenerate account keys and connection strings
  • Perform account backups and restore
  • Start and track data transfer jobs
  • Manage databases and containers
  • Modify account properties

Important

In Azure Cosmos DB, you need control plane access to manage native data-plane role-based access control definitions and assignments. Since Azure Cosmos DB's data plane role-based access control mechanism is native, you need control plane access to create definitions and assignments and store them as resources within an Azure Cosmos DB account.

First, you must prepare a role definition with a list of actions to grant access to manage account resources in Azure Cosmos DB. In this guide, you prepare a built-in and custom role. Then, assign the newly defined role[s] to an identity so that your applications can access resources in Azure Cosmos DB.

  1. List all of the role definitions associated with your Azure Cosmos DB account using az role definition list.

    az role definition list \
        --name "Cosmos DB Operator"
    
  2. Review the output and locate the role definition named Cosmos DB Operator. The output contains the unique identifier of the role definition in the id property. Record this value as it is required to use in the assignment step later in this guide.

    [
      {
        "assignableScopes": [
          "/"
        ],
        "description": "Lets you manage Azure Cosmos DB accounts, but not access data in them. Prevents access to account keys and connection strings.",
        "id": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa",
        "name": "230815da-be43-4aae-9cb4-875f7bd000aa",
        "permissions": [
          {
            "actions": [
              "Microsoft.DocumentDb/databaseAccounts/*",
              "Microsoft.Insights/alertRules/*",
              "Microsoft.Authorization/*/read",
              "Microsoft.ResourceHealth/availabilityStatuses/read",
              "Microsoft.Resources/deployments/*",
              "Microsoft.Resources/subscriptions/resourceGroups/read",
              "Microsoft.Support/*",
              "Microsoft.Network/virtualNetworks/subnets/joinViaServiceEndpoint/action"
            ],
            "condition": null,
            "conditionVersion": null,
            "dataActions": [],
            "notActions": [
              "Microsoft.DocumentDB/databaseAccounts/dataTransferJobs/*",
              "Microsoft.DocumentDB/databaseAccounts/readonlyKeys/*",
              "Microsoft.DocumentDB/databaseAccounts/regenerateKey/*",
              "Microsoft.DocumentDB/databaseAccounts/listKeys/*",
              "Microsoft.DocumentDB/databaseAccounts/listConnectionStrings/*",
              "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions/write",
              "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions/delete",
              "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments/write",
              "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments/delete",
              "Microsoft.DocumentDB/databaseAccounts/mongodbRoleDefinitions/write",
              "Microsoft.DocumentDB/databaseAccounts/mongodbRoleDefinitions/delete",
              "Microsoft.DocumentDB/databaseAccounts/mongodbUserDefinitions/write",
              "Microsoft.DocumentDB/databaseAccounts/mongodbUserDefinitions/delete"
            ],
            "notDataActions": []
          }
        ],
        "roleName": "Cosmos DB Operator",
        "roleType": "BuiltInRole",
        "type": "Microsoft.Authorization/roleDefinitions",
      }
    ]
    

    Note

    In this example, the id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa. This example uses fictitious data and your identifier would be distinct from this example. However, the identifier (230815da-be43-4aae-9cb4-875f7bd000aa) is globally unique across all role definitions in Azure.

  3. Use az group show to get the metadata for your current resource group.

    az group show \
        --name "<name-of-existing-resource-group>"
    
  4. Observe the output of the previous command. Record the value of the id property for this resource group as it is required to use in the next step.

    {
      "id": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example",
      "location": "chinanorth3",
      "name": "msdocs-identity-example",
      "type": "Microsoft.Resources/resourceGroups"
    }
    

    Note

    In this example, the id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example. This example uses fictitious data and your identifier would be distinct from this example. This string is a truncated example of the output.

  5. Create a new JSON file named role-definition.json. In the file, create this resource definition specifying the values listed here. For the AssignableScopes list, add the id property of the resource group recorded in the previous step.

    {
      "Name": "Azure Cosmos DB Control Plane Owner",
      "IsCustom": true,
      "Description": "Can perform all control plane actions for an Azure Cosmos DB account.",
      "Actions": [
        "Microsoft.DocumentDb/*"
      ],
      "AssignableScopes": [
        "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example"
      ]
    }
    

    Note

    This example uses the /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example value recorded from the previous step. Your actual resource identifier could be different.

  6. Create a new role definition using az role definition create. Use the role-definition.json file as the input for the --role-definition argument.

    az role definition create \
        --role-definition role-definition.json
    
  7. Review the output from the definition creation command. The output contains the unique identifier of the role definition in the id property. Record this value as it is required to use in the assignment step later in this guide.

    {
      "assignableScopes": [
        "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example"
      ],
      "description": "Can perform all control plane actions for an Azure Cosmos DB account.",
      "id": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleDefinitions/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1",
      "name": "e4e4e4e4-ffff-aaaa-bbbb-c5c5c5c5c5c5",
      "permissions": [
        {
          "actions": [
            "Microsoft.DocumentDb/*"
          ]
        }
      ],
      "roleName": "Azure Cosmos DB Control Plane Owner",
      "roleType": "CustomRole"
    }
    

    Note

    In this example, the id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleDefinitions/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1. This example uses fictitious data and your identifier would be distinct from this example. This example is a subset of the typical JSON outputted from the deployment for clarity.

  8. Use az group show to get the metadata for your current resource group again.

    az group show \
        --name "<name-of-existing-resource-group>"
    
  9. Observe the output of the previous command. Record the value of the id property for this resource group as it is required to use in the next step.

    {
      "id": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example",
      "location": "westus",
      "name": "msdocs-identity-example",
      "type": "Microsoft.Resources/resourceGroups"
    }
    

    Note

    In this example, the id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example. This example uses fictitious data and your identifier would be distinct from this example. This string is a truncated example of the output.

  10. Assign the new role using az role assignment create. Use your resource group's identifier for the --scope argument, the role's identifier for the -role argument, and the unique identifier for your identity to the --assignee argument.

    az role assignment create \
        --assignee "<your-principal-identifier>" \
        --role "subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleDefinitions/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1" \
        --scope "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example"
    

    Note

    In this example command, the scope was set to the fictitious example /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example from the previous step's example. Your resource group's identifier would be distinct from this example. The role was also set to the fictitious /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleDefinitions/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1. Again, your role identifier would be distinct.

  11. Observe the output from the command. The output includes a unique identifier for the assignment in the id property.

    {
      "id": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleAssignments/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1",
      "name": "ffffffff-5555-6666-7777-aaaaaaaaaaaa",
      "principalId": "aaaaaaaa-bbbb-cccc-1111-222222222222",
      "resourceGroup": "msdocs-identity-example",
      "roleDefinitionId": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleDefinitions/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1",
      "scope": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example",
      "type": "Microsoft.Authorization/roleAssignments"
    }
    

    Note

    In this example, the id property is /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleAssignments/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1, which is another fictitious example.

  12. Repeat these steps to grant access to the account from any other identities you would like to use.

    Tip

    You can repeat these steps for as many identities as you'd like. Typically, these steps are at least repeated to allow developers access to an account using their human identity and to allow applications access to the data using a managed identity.

  1. List all of the role definitions associated with your Azure Cosmos DB account using az role definition list.

    az role definition list \
        --name "Cosmos DB Operator"
    
  2. Review the output and locate the role definition named Cosmos DB Operator. The output contains the unique identifier of the role definition in the id property. Record this value as it is required to use in the assignment step later in this guide.

    [
      {
        "assignableScopes": [
          "/"
        ],
        "description": "Lets you manage Azure Cosmos DB accounts, but not access data in them. Prevents access to account keys and connection strings.",
        "id": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa",
        "name": "230815da-be43-4aae-9cb4-875f7bd000aa",
        "permissions": [
          {
            "actions": [
              "Microsoft.DocumentDb/databaseAccounts/*",
              "Microsoft.Insights/alertRules/*",
              "Microsoft.Authorization/*/read",
              "Microsoft.ResourceHealth/availabilityStatuses/read",
              "Microsoft.Resources/deployments/*",
              "Microsoft.Resources/subscriptions/resourceGroups/read",
              "Microsoft.Support/*",
              "Microsoft.Network/virtualNetworks/subnets/joinViaServiceEndpoint/action"
            ],
            "condition": null,
            "conditionVersion": null,
            "dataActions": [],
            "notActions": [
              "Microsoft.DocumentDB/databaseAccounts/dataTransferJobs/*",
              "Microsoft.DocumentDB/databaseAccounts/readonlyKeys/*",
              "Microsoft.DocumentDB/databaseAccounts/regenerateKey/*",
              "Microsoft.DocumentDB/databaseAccounts/listKeys/*",
              "Microsoft.DocumentDB/databaseAccounts/listConnectionStrings/*",
              "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions/write",
              "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions/delete",
              "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments/write",
              "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments/delete",
              "Microsoft.DocumentDB/databaseAccounts/mongodbRoleDefinitions/write",
              "Microsoft.DocumentDB/databaseAccounts/mongodbRoleDefinitions/delete",
              "Microsoft.DocumentDB/databaseAccounts/mongodbUserDefinitions/write",
              "Microsoft.DocumentDB/databaseAccounts/mongodbUserDefinitions/delete"
            ],
            "notDataActions": []
          }
        ],
        "roleName": "Cosmos DB Operator",
        "roleType": "BuiltInRole",
        "type": "Microsoft.Authorization/roleDefinitions",
      }
    ]
    

    Note

    In this example, the id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa. This example uses fictitious data and your identifier would be distinct from this example. However, the identifier (230815da-be43-4aae-9cb4-875f7bd000aa) is globally unique across all role definitions in Azure.

  3. Create a new Bicep file to define your role definition. Name the file control-plane-role-definition.bicep. Add these actions to the definition:

    Description
    Microsoft.DocumentDb/* Enables all possible actions.
    metadata description = 'Create RBAC definition for control plane access to Azure Cosmos DB.'
    
    @description('Name of the role definition.')
    param roleDefinitionName string = 'Azure Cosmos DB Control Plane Owner'
    
    @description('Description of the role definition.')
    param roleDefinitionDescription string = 'Can perform all control plane actions for an Azure Cosmos DB account.'
    
    resource definition 'Microsoft.Authorization/roleDefinitions@2022-04-01' = {
      name: guid(subscription().id, resourceGroup().id, roleDefinitionName)
      scope: resourceGroup()
      properties: {
        roleName: roleDefinitionName
        description: roleDefinitionDescription
        type: 'CustomRole'
        permissions: [
          {
            actions: [
              'Microsoft.DocumentDb/*'
            ]
          }
        ]
        assignableScopes: [
          resourceGroup().id
        ]
      }
    }
    
    output definitionId string = definition.id
    
  4. Deploy the Bicep template using az deployment group create. Specify the name of the Bicep template and Azure resource group.

    az deployment group create \
        --resource-group "<name-of-existing-resource-group>" \
        --template-file control-plane-role-definition.bicep
    
  5. Review the output from the deployment. The output contains the unique identifier of the role definition in the properties.outputs.definitionId.value property. Record this value as it is required to use in the assignment step later in this guide.

    {
      "properties": {
        "outputs": {
          "definitionId": {
            "type": "String",
            "value": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleDefinitions/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1"
          }
        }
      }
    }
    

    Note

    In this example, the id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleDefinitions/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1. This example uses fictitious data and your identifier would be distinct from this example. This example is a subset of the typical JSON outputted from the deployment for clarity.

  6. Create a new Bicep file to define your role assignment. Name the file control-plane-role-assignment.bicep.

    metadata description = 'Assign RBAC role for control plane access to Azure Cosmos DB.'
    
    @description('Id of the role definition to assign to the targeted principal in the context of the account.')
    param roleDefinitionId string
    
    @description('Id of the identity/principal to assign this role in the context of the account.')
    param identityId string
    
    resource assignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
      name: guid(subscription().id, resourceGroup().id, roleDefinitionId, identityId)
      scope: resourceGroup()
      properties: {
        roleDefinitionId: roleDefinitionId
        principalId: identityId
      }
    }
    
  7. Create a new Bicep parameters file named control-plane-role-assignment.bicepparam. In this parameters file; assign the previously recorded role definition identifiers to the roleDefinitionId parameter, and the unique identifier for your identity to the identityId parameter.

    using './control-plane-role-assignment.bicep'
    
    param roleDefinitionId = '<id-of-new-role-definition>'
    param identityId = '<id-of-existing-identity>'
    
  8. Deploy this Bicep template using az deployment group create.

    az deployment group create \
        --resource-group "<name-of-existing-resource-group>" \
        --parameters control-plane-role-assignment.bicepparam \
        --template-file control-plane-role-assignment.bicep
    
  9. Repeat these steps to grant access to the account from any other identities you would like to use.

    Tip

    You can repeat these steps for as many identities as you'd like. Typically, these steps are at least repeated to allow developers access to an account using their human identity and to allow applications access to the data using a managed identity.

  1. Sign in to the Azure portal (https://portal.azure.cn).

  2. Enter Resource group in the global search bar.

    Screenshot of the global search bar in the Azure portal.

  3. Within Services, select Resource groups.

    Screenshot of the 'Resource groups' option selected in the search menu.

  4. In the Resource groups pane, select your existing resource group.

    Screenshot of an existing resource group in the list of resource groups for the subscription.

    Note

    This example screenshot includes the msdocs-identity-example resource group. Your actual resource group name could be different.

  5. Within the pane for the resource group, select Access control (IAM) in the service menu.

    Screenshot of the 'Access Control (IAM)' option in the service menu for a resource group.

  6. In the Access control (IAM) pane, select Roles.

    Screenshot of the 'Roles' option in the 'Access Control (IAM)' pane.

  7. In the Roles section, use the search phrase Cosmos DB and locate the Cosmos DB Operator role definition. Then, select the View option associated with that definition.

    Screenshot of a list of role definitions at the current assignable scope filtered to include only definitions with 'Cosmos DB' in the title.

  8. In the Cosmos DB Operator role definition dialog, observe the actions assigned as part of this role definition.

    Screenshot of the 'Cosmos DB Operator' dialog with details about the built-in role definition.

  9. Close the Cosmos DB Operator role definition dialog.

  10. Back in the Access control (IAM) pane, select Add. Then select Add custom role.

    Screenshot of the 'Add custom role' option in the 'Access Control (IAM)' menu for the 'Add' option.

  11. Within the Basics pane, configure the following options, and then select Next:

    Value
    Custom role name Azure Cosmos DB Control Plane Owner
    Description Can perform all control plane actions for an Azure Cosmos DB account.
    Baseline permissions Start from scratch

    Screenshot of the 'Basics' pane for adding a custom role.

  12. In the Permissions pane, select Add permissions. Then, search for DocumentDB in the permissions dialog. Finally, select the Microsoft.DocumentDB option.

    Screenshot of the 'Permissions' pane for adding a custom role.

    Screenshot of the 'Add permissions' dialog filtered to permissions related to 'DocumentDB' for adding a custom role.

  13. In the permissions dialog, select all Actions for Microsoft.DocumentDB. Then, select Add to return to the *Permissions pane.

    Screenshot of all permissions selected for 'DocumentDB' in a dialog for a custom role.

  14. Back in the Permissions pane, observe the list of permissions. Then, select Review + create.

    Screenshot of the 'Permissions' pane with multiple permissions added to the list for a custom role.

  15. In the Review + create pane, review the specified options for the new role definition. Finally, select Create.

    Screenshot of the 'Review + create' pane for adding a custom role.

  16. Wait for the portal to finish creating the role definition.

  17. In the Access control (IAM) pane, select Add and then Add role assignment.

    Screenshot of the 'Add role assignment' option in the 'Access Control (IAM)' menu for the 'Add' option.

  18. In the Role pane, search for Azure Cosmos DB and then select the Azure Cosmos DB Control Plane Owner role created earlier in this guide. Then, select Next.

    Screenshot of the 'Role' pane for adding a role assignment.

    Tip

    You can optionally filter the list of roles to only include custom roles.

  19. In the Members pane, select the Select members option. In the members dialog, select the identity you wish to grant this level of access for your Azure Cosmos DB account and then use the Select option to confirm your choice.

    Screenshot of the 'Members' pane for adding a role assignment.

    Screenshot of the identity selection dialog for adding a role assignment.

    Note

    This screenshot illustrates an example user named "Kai Carter" with a principal of kai@adventure-works.com.

  20. Back in the Members pane, review the selected member[s] and then select Review + assign.

    Screenshot of the 'Members' pane with a selected identity for a role assignment.

  21. In the Review + assign pane, review the specified options for the new role assignment. Finally, select Review + assign.

    Screenshot of the 'Review + create' pane for a role assignment.

  22. Wait for the portal to finish creating the role assignment.

  1. Use Get-AzRoleDefinition to list all of the role definitions associated with your Azure Cosmos DB account.

    $parameters = @{
        Name = "Cosmos DB Operator"
    }
    Get-AzRoleDefinition @parameters
    
  2. Review the output and locate the role definition named Cosmos DB Built-in Data Contributor. The output contains the unique identifier of the role definition in the Id property. Record this value as it is required to use in the assignment step later in this guide.

    Name             : Cosmos DB Operator
    Id               : 230815da-be43-4aae-9cb4-875f7bd000aa
    IsCustom         : False
    Description      : Lets you manage Azure Cosmos DB accounts, but not access data in them. Prevents access to account keys and connection strings.
    Actions          : {Microsoft.DocumentDb/databaseAccounts/*, Microsoft.Insights/alertRules/*, Microsoft.Authorization/*/read, Microsoft.ResourceHealth/availabilityStatuses/read…}
    NotActions       : {Microsoft.DocumentDB/databaseAccounts/dataTransferJobs/*, Microsoft.DocumentDB/databaseAccounts/readonlyKeys/*, Microsoft.DocumentDB/databaseAccounts/regenerateKey/*, Microsoft.DocumentDB/databaseAccounts/listKeys/*…}
    DataActions      : {}
    NotDataActions   : {}
    AssignableScopes : {/}
    

    Note

    In this example, the Id value would be 230815da-be43-4aae-9cb4-875f7bd000aa. The identifier is globally unique across all role definitions in Azure.

  3. Use Get-AzResourceGroup to get the metadata for your current resource group.

    $parameters = @{
        Name = "<name-of-existing-resource-group>"
    }
    Get-AzResourceGroup @parameters
    
  4. Observe the output of the previous command. Record the value of the ResourceId property for this resource group as it is required to use in the next step.

    ResourceGroupName : msdocs-identity-example
    Location          : westus
    ProvisioningState : Succeeded
    ResourceId        : /subscriptions/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1/resourcegroups/msdocs-identity-example
    

    Note

    In this example, the ResourceId value would be /subscriptions/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1/resourcegroups/msdocs-identity-example. This example uses fictitious data and your identifier would be distinct from this example. This string is a truncated example of the typical output.

  5. First, import the Az.Resources module. Then, Create a new Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleDefinition object. In the object, create this resource definition specifying the values listed here. For the AssignableScopes list, add the ResourceId property of the resource group recorded in the previous step. Finally, use the role definition object as the input for the -Role parameter of New-AzRoleDefinition.

    Import-Module Az.Resources
    
    $parameters = @{
        TypeName = "Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleDefinition"
        Property = @{
            Name = "Azure Cosmos DB Control Plane Owner"
            Description = "Can perform all control plane actions for an Azure Cosmos DB account."
            IsCustom = $true
            Actions = @(
                "Microsoft.DocumentDb/*"
            )
            AssignableScopes = @(
                "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example"
            )
        }
    }
    $role = New-Object @parameters
    
    New-AzRoleDefinition -Role $role
    

    Note

    This example uses the /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example value recorded from the previous step. Your actual resource identifier could be different.

  6. Review the output from the definition creation command. The output contains the unique identifier of the role definition in the Name property. Record this value as it is required to use in the assignment step later in this guide.

    Name             : Azure Cosmos DB Control Plane Owner
    Id               : e4e4e4e4-ffff-aaaa-bbbb-c5c5c5c5c5c5
    IsCustom         : True
    Description      : Can perform all control plane actions for an Azure Cosmos DB account.
    Actions          : {Microsoft.DocumentDb/*}
    AssignableScopes : {/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example}
    

    Note

    In this example, the Name value would be Azure Cosmos DB Control Plane Owner. This example is a subset of the typical output of the deployment for clarity.

  7. Assign the new role using New-AzRoleAssignment. Use the role's name for the RoleDefinitionName parameter and the unique identifier for your identity to the ObjectId parameter.

    $parameters = @{
        ResourceGroupName = "<name-of-existing-resource-group>"
        ObjectId = "<your-principal-identifier>"
        RoleDefinitionName = "Azure Cosmos DB Control Plane Owner"
    }
    New-AzRoleAssignment @parameters
    
  8. Observe the output from the command. The output includes a unique identifier for the assignment in the RoleAssignmentId property.

    RoleAssignmentName : ffffffff-5555-6666-7777-aaaaaaaaaaaa
    RoleAssignmentId   : /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleAssignments/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1
    Scope              : /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example
    DisplayName        : Kai Carter
    SignInName         : <kai@adventure-works.com>
    RoleDefinitionName : Azure Cosmos DB Control Plane Owner
    RoleDefinitionId   : e4e4e4e4-ffff-aaaa-bbbb-c5c5c5c5c5c5
    

    Note

    In this example, the RoleAssignmentId property is /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.Authorization/roleAssignments/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1, which is another fictitious example. This example is a subset of the typical output of the deployment for clarity.

  9. Repeat these steps to grant access to the account from any other identities you would like to use.

    Tip

    You can repeat these steps for as many identities as you'd like. Typically, these steps are at least repeated to allow developers access to an account using their human identity and to allow applications access to the data using a managed identity.

Important

Assigning a role definition requires you to already have the unique identifier of any identity you want to grant role-based access control permissions.

Validate control plane role-based access in code

Validate that you correctly granted access using application code and the Azure Management SDK.

using Azure.Identity;
using Azure.ResourceManager;

DefaultAzureCredential credential = new();

ArmClient client = new(credential);
const { CosmosDBManagementClient } = require('@azure/arm-cosmosdb');
const { DefaultAzureCredential } = require('@azure/identity');

const subscriptionId = "<subscription-id>";

const credential = new DefaultAzureCredential();

const client = new CosmosDBManagementClient(credential, subscriptionId);
import { CosmosDBManagementClient } from '@azure/arm-cosmosdb';
import { TokenCredential, DefaultAzureCredential } from '@azure/identity';

let subscriptionId: string = "<subscription-id>";

let credential: TokenCredential = new DefaultAzureCredential();

const client: CosmosDBManagementClient = new CosmosDBManagementClient(credential, subscriptionId);
from azure.mgmt.cosmosdb import CosmosDBManagementClient
from azure.identity import DefaultAzureCredential

subscription_id = "<subscription-id>"

credential = DefaultAzureCredential()

client = CosmosDBManagementClient(credential=credential, subscription=subscription_id)
package main

import (
    "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
    "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cosmos/armcosmos"
)

const subscriptionId = "<subscription-id>"

func main() {
    credential, _ := azidentity.NewDefaultAzureCredential(nil)
    
    client, _ := armcosmos.NewDatabaseClient(subscriptionId, credential, nil)
}
package com.example;

import com.azure.core.management.profile.AzureProfile;
import com.azure.core.management.AzureEnvironment;
import com.azure.identity.DefaultAzureCredential;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.resourcemanager.cosmos.CosmosManager;

public class CosmosDB {
    public static void main(String[] args) {
        AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE);
        DefaultAzureCredential credential = new DefaultAzureCredentialBuilder()
          .build();

        CosmosManager manager = CosmosManager.authenticate(credential, profile);
    }
}

Grant data plane role-based access

Data plane access refers to the ability to read and write data within an Azure service without the ability to manage resources in the account. For example, Azure Cosmos DB data plane access could include the ability to:

  • Read some account and resource metadata
  • Create, read, update, patch, and delete items
  • Execute Table queries
  • Read from a container's change feed
  • Execute stored procedures
  • Manage conflicts in the conflict feed

First, you must prepare a role definition with a list of dataActions to grant access to read, query, and manage data in Azure Cosmos DB for Table. In this guide, you prepare a built-in and custom role. Then, assign the newly defined role[s] to an identity so that your applications can access data in Azure Cosmos DB for Table.

  1. First, get the resource identifier of the existing Azure Cosmos DB for Table account using az cosmsodb show and store it in a variable.

    resourceId=$( \
        az cosmosdb show \
            --resource-group "<name-of-existing-resource-group>" \
            --name "<name-of-existing-table-account>" \
            --query "id" \
            --output tsv \
    )
    
    az rest \
        --method "GET" \
        --url $resourceId/tableRoleDefinitions?api-version=2023-04-15
    
  2. Then, list all of the role definitions associated with your Azure Cosmos DB for Table account using az rest. Finally, review the output and locate the role definition named Cosmos DB Built-in Data Contributor. The output contains the unique identifier of the role definition in the id property. Record this value as it is required to use in the assignment step later in this guide.

    [
      ...,
      {
        "id": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table/tableRoleDefinitions/00000000-0000-0000-0000-000000000002",
        "name": "00000000-0000-0000-0000-000000000002",
        "properties": {
          "assignableScopes": [
            "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table"
          ],
          "permissions": [
            {
              "dataActions": [
                "Microsoft.DocumentDB/databaseAccounts/readMetadata",
                "Microsoft.DocumentDB/databaseAccounts/tables/*",
                "Microsoft.DocumentDB/databaseAccounts/tables/containers/entities/*"
              ],
              "notDataActions": []
            }
          ],
          "roleName": "Cosmos DB Built-in Data Contributor",
          "type": "BuiltInRole"
        },
        "type": "Microsoft.DocumentDB/databaseAccounts/tableRoleDefinitions"
      }
      ...
    ]
    

    Note

    In this example, the id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table/tableRoleDefinitions/00000000-0000-0000-0000-000000000002. This example uses fictitious data and your identifier would be distinct from this example. This example output is truncated.

  3. Create a new JSON file named role-definition.json. In this file, create a resource definition specifying the data actions listed here:

    Description
    Microsoft.DocumentDB/databaseAccounts/readMetadata Can read account-level metadata
    Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/* Can perform any container-level data operations
    Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/* Can perform any operation on items with containers
    {
      "properties": {
        "roleName": "Azure Cosmos DB for Table Data Plane Owner",
        "type": "CustomRole",
        "assignableScopes": [
          "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/sidandrews-rbac/providers/Microsoft.DocumentDB/databaseAccounts/sidandrews-rbac-table/"
        ],
        "permissions": [
          {
            "dataActions": [
              "Microsoft.DocumentDB/databaseAccounts/readMetadata",
              "Microsoft.DocumentDB/databaseAccounts/tables/*",
              "Microsoft.DocumentDB/databaseAccounts/tables/containers/entities/*"
            ]
          }
        ]
      }
    }
    
  4. Now create or update a role definition using az cosmosdb show and az rest together to issue an HTTP PUT request. As part of this request, specify a unique GUID for your role definition.

    resourceId=$( \
        az cosmosdb show \
            --resource-group "<name-of-existing-resource-group>" \
            --name "<name-of-existing-table-account>" \
            --query "id" \
            --output tsv \
    )
    
    az rest \
        --method "PUT" \
        --url $resourceId/tableRoleDefinitions/d3d3d3d3-eeee-ffff-aaaa-b4b4b4b4b4b4?api-version=2023-04-15 \
        --body @role-definition.json
    

    Note

    In this example, the unique GUID specified was d3d3d3d3-eeee-ffff-aaaa-b4b4b4b4b4b4. You can specify any unique GUID for your own role definition.

  5. The output should now indicate that the request is queued. Now, wait for the enqueued role definition deployment to finish. This task can take a few minutes.

    {
      "status": "Enqueued"
    }
    
  6. Finally, check the list of role definitions using az rest again.

    resourceId=$( \
        az cosmosdb show \
            --resource-group "<name-of-existing-resource-group>" \
            --name "<name-of-existing-table-account>" \
            --query "id" \
            --output tsv \
    )
    
    az rest \
        --method "GET" \
        --url $resourceId/tableRoleDefinitions?api-version=2023-04-15
    
  7. Use az cosmosdb show to get the unique identifier for your current account.

    az cosmosdb show \
        --resource-group "<name-of-existing-resource-group>" \
        --name "<name-of-existing-resource-group>" \
        --query "{id:id}"
    
  8. Observe the output of the previous command. Record the value of the id property for this account as it is required to use in the next step.

    {
      "id": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table"
    }
    

    Note

    In this example, the id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table. This example uses fictitious data and your identifier would be distinct from this example.

  9. Create a new JSON file named role-assignment.json. In the JSON file, add the unique identifier for your identity and unique identifier for the account resource.

    {
      "properties": {
        "roleDefinitionId": "<account-resource-id>/tableRoleDefinitions/d3d3d3d3-eeee-ffff-aaaa-b4b4b4b4b4b4",
        "scope": "<account-resource-id>",
        "principalId": "<id-of-existing-identity>"
      }
    }
    

    Note

    In this example, the unique GUID specified was d3d3d3d3-eeee-ffff-aaaa-b4b4b4b4b4b4. You can use the unique GUID you used previously for your own role definition.

    Tip

    In Azure Cosmos DB's native implementation of role-based access control, scope refers to the granularity of resources within an account for which you want permission applied. At the highest level, you can scope a data plane role-based access control assignment to the entire account using the largest scope. This scope includes all databases and containers within the account:

    /subscriptions/<subscription-id>/resourcegroups/<resource-group-name>/providers/Microsoft.DocumentDB/databaseAccounts/<account-name>/
    

    Or, you can scope your data plane role assignment to a specific database:

    /subscriptions/<subscription-id>/resourcegroups/<resource-group-name>/providers/Microsoft.DocumentDB/databaseAccounts/<account-name>/dbs/<database-name>
    

    Finally, you can scope the assignment to a single container, the most granular scope:

    /subscriptions/<subscription-id>/resourcegroups/<resource-group-name>/providers/Microsoft.DocumentDB/databaseAccounts/<account-name>/dbs/<database-name>/colls/<container-name>
    

    In many cases, you can use the relative scope instead of the fully qualified scope. For example, you can use this relative scope to grant data plane role-based access control permissions to a specific database and container from an Azure CLI command:

    /dbs/<database-name>/colls/<container-name>
    

    You can also grant universal access to all databases and containers using the relative scope:

    /
    
  10. Now create or update a role assignment using az cosmosdb show and az rest together to issue an HTTP PUT request. As part of this request, specify a unique GUID for your role assignment.

    resourceId=$( \
        az cosmosdb show \
            --resource-group "<name-of-existing-resource-group>" \
            --name "<name-of-existing-table-account>" \
            --query "id" \
            --output tsv \
    )
    
    az rest \
        --method "PUT" \
        --url $resourceId/tableRoleAssignments/e4e4e4e4-ffff-aaaa-bbbb-c5c5c5c5c5c5?api-version=2023-04-15 \
        --body @role-assignment.json
    

    Note

    In this example, the unique GUID specified was e4e4e4e4-ffff-aaaa-bbbb-c5c5c5c5c5c5. You can specify any unique GUID for your own role assignment.

  1. First, get the resource identifier of the existing Azure Cosmos DB for Table account using az cosmsodb show and store it in a variable.

    resourceId=$( \
        az cosmosdb show \
            --resource-group "<name-of-existing-resource-group>" \
            --name "<name-of-existing-table-account>" \
            --query "id" \
            --output tsv \
    )
    
    az rest \
        --method "GET" \
        --url $resourceId/tableRoleDefinitions?api-version=2023-04-15
    
  2. Then, list all of the role definitions associated with your Azure Cosmos DB for Table account using az rest. Finally, review the output and locate the role definition named Cosmos DB Built-in Data Contributor. The output contains the unique identifier of the role definition in the id property. Record this value as it is required to use in the assignment step later in this guide.

    [
      ...,
      {
        "id": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table/tableRoleDefinitions/00000000-0000-0000-0000-000000000002",
        "name": "00000000-0000-0000-0000-000000000002",
        "properties": {
          "assignableScopes": [
            "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table"
          ],
          "permissions": [
            {
              "dataActions": [
                "Microsoft.DocumentDB/databaseAccounts/readMetadata",
                "Microsoft.DocumentDB/databaseAccounts/tables/*",
                "Microsoft.DocumentDB/databaseAccounts/tables/containers/entities/*"
              ],
              "notDataActions": []
            }
          ],
          "roleName": "Cosmos DB Built-in Data Contributor",
          "type": "BuiltInRole"
        },
        "type": "Microsoft.DocumentDB/databaseAccounts/tableRoleDefinitions"
      }
      ...
    ]
    

    Note

    In this example, the id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table/tableRoleDefinitions/00000000-0000-0000-0000-000000000002. This example uses fictitious data and your identifier would be distinct from this example. This example output is truncated.

  3. Create a new Bicep file to define your role definition. Name the file data-plane-role-definition.bicep. Add these dataActions to the definition:

    Description
    Microsoft.DocumentDB/databaseAccounts/readMetadata
    Microsoft.DocumentDB/databaseAccounts/tables/*
    Microsoft.DocumentDB/databaseAccounts/tables/containers/entities/*
    metadata description = 'Create RBAC definition for data plane access to Azure Cosmos DB for Table.'
    
    @description('Name of the Azure Cosmos DB for Table account.')
    param accountName string
    
    @description('Name of the role definition.')
    param roleDefinitionName string = 'API for Table Data Plane Owner'
    
    resource account 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' existing = {
      name: accountName
    }
    
    resource definition 'Microsoft.DocumentDB/databaseAccounts/tableRoleDefinitions@2023-04-15' = {
      name: guid('table-role-definition', account.id)
      parent: account
      properties: {
        roleName: roleDefinitionName
        type: 'CustomRole'
        assignableScopes: [
          account.id
        ]
        permissions: [
          {
            dataActions: [
              'Microsoft.DocumentDB/databaseAccounts/readMetadata'
              'Microsoft.DocumentDB/databaseAccounts/tables/*'
              'Microsoft.DocumentDB/databaseAccounts/tables/containers/entities/*'
            ]
          }
        ]
      }
    }
    
    output definitionId string = definition.id
    

    Tip

    In Azure Cosmos DB's native implementation of role-based access control, scope refers to the granularity of resources within an account for which you want permission applied. At the highest level, you can scope a data plane role-based access control assignment to the entire account using the largest scope. This scope includes all databases and containers within the account:

    /subscriptions/<subscription-id>/resourcegroups/<resource-group-name>/providers/Microsoft.DocumentDB/databaseAccounts/<account-name>/
    

    Or, you can scope your data plane role assignment to a specific database:

    /subscriptions/<subscription-id>/resourcegroups/<resource-group-name>/providers/Microsoft.DocumentDB/databaseAccounts/<account-name>/dbs/<database-name>
    

    Finally, you can scope the assignment to a single container, the most granular scope:

    /subscriptions/<subscription-id>/resourcegroups/<resource-group-name>/providers/Microsoft.DocumentDB/databaseAccounts/<account-name>/dbs/<database-name>/colls/<container-name>
    

    In many cases, you can use the relative scope instead of the fully qualified scope. For example, you can use this relative scope to grant data plane role-based access control permissions to a specific database and container from an Azure CLI command:

    /dbs/<database-name>/colls/<container-name>
    

    You can also grant universal access to all databases and containers using the relative scope:

    /
    
  4. Create a new Bicep parameters file named data-plane-role-definition.bicepparam. In this parameters file, assign the name of your existing Azure Cosmos DB for Table account to the accountName parameter.

    using './data-plane-role-definition.bicep'
    
    param accountName = '<name-of-existing-table-account>'
    
  5. Deploy the Bicep template using az deployment group create. Specify the name of the Bicep template, parameters file, and Azure resource group.

    az deployment group create \
        --resource-group "<name-of-existing-resource-group>" \
        --parameters data-plane-role-definition.bicepparam \
        --template-file data-plane-role-definition.bicep
    
  6. Review the output from the deployment. The output contains the unique identifier of the role definition in the properties.outputs.definitionId.value property. Record this value as it is required to use in the assignment step later in this guide.

    {
      "properties": {
        "outputs": {
          "definitionId": {
            "type": "String",
            "value": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table-account/tableRoleDefinitions/dddddddd-9999-0000-1111-eeeeeeeeeeee"
          }
        }
      }
    }
    

    Note

    In this example, the id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourcegroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table-account/tableRoleDefinitions/dddddddd-9999-0000-1111-eeeeeeeeeeee. This example uses fictitious data and your identifier would be distinct from this example. This example is a subset of the typical JSON outputted from the deployment for clarity.

  7. Create another Bicep file to assign a role to an identity. Name this file data-plane-role-assignment.bicep.

    metadata description = 'Assign RBAC role for data plane access to Azure Cosmos DB for Table.'
    
    @description('Name of the Azure Cosmos DB for Table account.')
    param accountName string
    
    @description('Id of the role definition to assign to the targeted principal in the context of the account.')
    param roleDefinitionId string
    
    @description('Id of the identity/principal to assign this role in the context of the account.')
    param identityId string
    
    resource account 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' existing = {
      name: accountName
    }
    
    resource assignment 'Microsoft.DocumentDB/databaseAccounts/tableRoleAssignments@2023-04-15' = {
      name: guid(roleDefinitionId, identityId, account.id)
      parent: account
      properties: {
        principalId: identityId
        roleDefinitionId: roleDefinitionId
        scope: account.id
      }
    }
    
    output id string = assignment.id
    
  8. Create a new Bicep parameters file named data-plane-role-assignment.bicepparam. In this parameters file; assign the name of your existing Azure Cosmos DB for Table account to the accountName parameter, the previously recorded role definition identifiers to the roleDefinitionId parameter, and the unique identifier for your identity to the identityId parameter.

    using './data-plane-role-assignment.bicep'
    
    param accountName = '<name-of-existing-table-account>'
    param roleDefinitionId = '<id-of-new-role-definition>'
    param identityId = '<id-of-existing-identity>'
    
  9. Deploy this Bicep template using az deployment group create.

    az deployment group create \
        --resource-group "<name-of-existing-resource-group>" \
        --parameters data-plane-role-assignment.bicepparam \
        --template-file data-plane-role-assignment.bicep
    
  10. Repeat these steps to grant access to the account from any other identities you would like to use.

    Tip

    You can repeat these steps for as many identities as you'd like. Typically, these steps are at least repeated to allow developers access to an account using their human identity and to allow applications access to data using a managed identity.

  1. Use Get-AzCosmosDBAccount to get the resource identifier of the existing Azure Cosmos DB for Table account and store it in a variable.

    $parameters = @{
        ResourceGroupName = "<name-of-existing-resource-group>"
        Name = "<name-of-existing-table-account>"
    }
    $resourceId = (
        Get-AzCosmosDBAccount @parameters |
            Select-Object -Property Id -First 1
    ).Id
    
    $parameters = @{
      Path = "$resourceId/tableRoleDefinitions?api-version=2023-04-15"
      Method = "GET"
    }
    Invoke-AzRestMethod @parameters
    
  2. Then, use Invoke-AzRestMethod to list all of the role definitions associated with your Azure Cosmos DB for Table account. Review the output and locate the role definition named Cosmos DB Built-in Data Contributor. The output contains the unique identifier of the role definition in the Id property. Record this value as it is required to use in the assignment step later in this guide.

    StatusCode : 200
    Content    : {
        "value": [
          ...,
          {
            "id": "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table/tableRoleDefinitions/00000000-0000-0000-0000-000000000002",
            "name": "00000000-0000-0000-0000-000000000002",
            "properties": {
              "assignableScopes": [
                "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table"
              ],
              "permissions": [
                {
                  "dataActions": [
                    "Microsoft.DocumentDB/databaseAccounts/readMetadata",
                    "Microsoft.DocumentDB/databaseAccounts/tables/*",
                    "Microsoft.DocumentDB/databaseAccounts/tables/containers/entities/*"
                  ],
                  "notDataActions": []
                }
              ],
              "roleName": "Cosmos DB Built-in Data Contributor",
              "type": "BuiltInRole"
            },
            "type": "Microsoft.DocumentDB/databaseAccounts/tableRoleDefinitions"
          }
          ...
          ]
        },
        ...
      ]
    }
    

    Note

    In this example, the Id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table/tableRoleDefinitions/00000000-0000-0000-0000-000000000002. This example uses fictitious data and your identifier would be distinct from this example. This example output is truncated.

  3. Create or update your role definition using Get-AzCosmosDBAccount and Invoke-AzRestMethod together to issue an HTTP PUT request. Also, as part of this request, specify a unique GUID for your role definition. Finally, create a resource definition payload specifying the data actions listed here:

    Description
    Microsoft.DocumentDB/databaseAccounts/readMetadata Can read account-level metadata
    Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/* Can perform any container-level data operations
    Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/* Can perform any operation on items with containers
    $parameters = @{
        ResourceGroupName = "<name-of-existing-resource-group>"
        Name = "<name-of-existing-table-account>"
    }
    $resourceId = (
        Get-AzCosmosDBAccount @parameters |
            Select-Object -Property Id -First 1
    ).Id
    
    $payload = @{
      properties = @{
        roleName = "Azure Cosmos DB for Table Data Plane Owner"
        type = "CustomRole"
        assignableScopes = @(
          "/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/sidandrews-rbac/providers/Microsoft.DocumentDB/databaseAccounts/sidandrews-rbac-table/"
        )
        permissions = @(
          @{
            dataActions = @(
              "Microsoft.DocumentDB/databaseAccounts/readMetadata",
              "Microsoft.DocumentDB/databaseAccounts/tables/*",
              "Microsoft.DocumentDB/databaseAccounts/tables/containers/entities/*"
            )
          }
        )
      }
    }
    
    $parameters = @{
      Path = "$resourceId/tableRoleDefinitions/d3d3d3d3-eeee-ffff-aaaa-b4b4b4b4b4b4?api-version=2023-04-15"
      Method = "PUT"
      Payload = $payload | ConvertTo-Json -Depth 4
    }
    Invoke-AzRestMethod @parameters
    

    Note

    In this example, the unique GUID specified was d3d3d3d3-eeee-ffff-aaaa-b4b4b4b4b4b4. You can specify any unique GUID for your own role definition.

    Tip

    In Azure Cosmos DB's native implementation of role-based access control, scope refers to the granularity of resources within an account for which you want permission applied. At the highest level, you can scope a data plane role-based access control assignment to the entire account using the largest scope. This scope includes all databases and containers within the account:

    /subscriptions/<subscription-id>/resourcegroups/<resource-group-name>/providers/Microsoft.DocumentDB/databaseAccounts/<account-name>/
    

    Or, you can scope your data plane role assignment to a specific database:

    /subscriptions/<subscription-id>/resourcegroups/<resource-group-name>/providers/Microsoft.DocumentDB/databaseAccounts/<account-name>/dbs/<database-name>
    

    Finally, you can scope the assignment to a single container, the most granular scope:

    /subscriptions/<subscription-id>/resourcegroups/<resource-group-name>/providers/Microsoft.DocumentDB/databaseAccounts/<account-name>/dbs/<database-name>/colls/<container-name>
    

    In many cases, you can use the relative scope instead of the fully qualified scope. For example, you can use this relative scope to grant data plane role-based access control permissions to a specific database and container from an Azure CLI command:

    /dbs/<database-name>/colls/<container-name>
    

    You can also grant universal access to all databases and containers using the relative scope:

    /
    
  4. The output should return with a status code of 200. Now, wait for the enqueued role definition deployment to finish. This task can take a few minutes.

    StatusCode : 200
    ...
    
  5. Finally, check the list of role definitions using Invoke-AzRestMethod again.

    $parameters = @{
        ResourceGroupName = "<name-of-existing-resource-group>"
        Name = "<name-of-existing-table-account>"
    }
    $resourceId = (
        Get-AzCosmosDBAccount @parameters |
            Select-Object -Property Id -First 1
    ).Id
    
    $parameters = @{
      Path = "$resourceId/tableRoleDefinitions?api-version=2023-04-15"
      Method = "GET"
    }
    Invoke-AzRestMethod @parameters
    
  6. Use `Get-AzCosmosDBAccount to get the unique identifier for your current account.

    $parameters = @{
        ResourceGroupName = "<name-of-existing-resource-group>"
        Name = "<name-of-existing-table-account>"
    }
    Get-AzCosmosDBAccount @parameters | Select -Property Id
    
  7. Observe the output of the previous command. Record the value of the Id property for this account as it is required to use in the next step.

    Id
    --    
    /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table
    

    Note

    In this example, the Id value would be /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-identity-example/providers/Microsoft.DocumentDB/databaseAccounts/msdocs-identity-example-table. This example uses fictitious data and your identifier would be distinct from this example.

  8. Now create or update a role assignment using Get-AzCosmosDBAccount and Invoke-AzRestMethod together to issue an HTTP PUT request. As part of this request, specify a unique GUID for your role assignment. Finally, create a resource assignment payload specifying the unique identifier for your identity.

    $parameters = @{
        ResourceGroupName = "<name-of-existing-resource-group>"
        Name = "<name-of-existing-table-account>"
    }
    $resourceId = (
        Get-AzCosmosDBAccount @parameters |
            Select-Object -Property Id -First 1
    ).Id    
    
    $payload = @{
      properties = @{
        roleDefinitionId = "$resourceId/tableRoleDefinitions/00000000-0000-0000-0000-000000000002"
        scope = "$resourceId"
        principalId = "<id-of-existing-identity>"
      }
    }
    
    $parameters = @{
      Path = "$resourceId/tableRoleAssignments/e4e4e4e4-ffff-aaaa-bbbb-c5c5c5c5c5c5?api-version=2023-04-15"
      Method = "PUT"
      Payload = $payload | ConvertTo-Json -Depth 2
    }
    Invoke-AzRestMethod @parameters
    

    Note

    In this example, the unique GUID specified was e4e4e4e4-ffff-aaaa-bbbb-c5c5c5c5c5c5. You can specify any unique GUID for your own role assignment.

Warning

Managing data plane role-based access control isn't supported in the Azure portal.

Validate data plane role-based access in code

Validate that you correctly granted access using application code and the Azure SDK.

using Azure.Identity;
using Azure.Data.Tables;

string endpoint = "<account-endpoint>";

DefaultAzureCredential credential = new();

TableServiceClient client = new(
    endpoint: new Uri(endpoint),
    tokenCredential: credential
);

TableClient table = client.GetTableClient(
    tableName: "<name-of-table>"
);

await table.GetEntityAsync<TableEntity>(
    partitionKey: "<partition-key>",
    rowKey: "<row-key>"
);
const { TableServiceClient, TableClient } = require('@azure/data-tables');
const { DefaultAzureCredential } = require('@azure/identity');

const endpoint = '<account-endpoint>';

let credential = new DefaultAzureCredential();

let client = new TableServiceClient(endpoint, credential);

let table = new TableClient(endpoint, "<table-name>", credential);

await table.getEntity("<partition-key>", "<row-key>");
import { TableServiceClient, TableClient } from '@azure/data-tables';
import { TokenCredential, DefaultAzureCredential } from '@azure/identity';

const endpoint: string = '<account-endpoint>';

let credential: TokenCredential = new DefaultAzureCredential();

let client: TableServiceClient = new TableServiceClient(endpoint, credential);

let table: TableClient = new TableClient(endpoint, "<table-name>", credential);

await table.getEntity("<partition-key>", "<row-key>");
from azure.data.tables import TableServiceClient
from azure.identity import DefaultAzureCredential

endpoint = "<account-endpoint>"

credential = DefaultAzureCredential()

client = TableServiceClient(endpoint, credential=credential)

table = client.get_table_client("<table-name>")

table.get_entity(
    row_key="<row-key>",
    partition_key="<partition-key>"
)
import (
    "context"
    
    "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
    "github.com/Azure/azure-sdk-for-go/sdk/data/aztables"
)

const endpoint = "<account-endpoint>"

func main() {
    credential, _ := azidentity.NewDefaultAzureCredential(nil)
    client, _ := aztables.NewServiceClient(endpoint, credential, nil)
    table := client.NewClient("<table-name>")
    
    _, err := table.GetEntity(context.TODO(), "<partition-key>", "<row-key>", nil)
    if err != nil {
        panic(err)
    }
}
import com.azure.data.tables.TableClient;
import com.azure.data.tables.TableServiceClient;
import com.azure.data.tables.TableServiceClientBuilder;
import com.azure.identity.DefaultAzureCredential;
import com.azure.identity.DefaultAzureCredentialBuilder;

public class Table{
    public static void main(String[] args){
        DefaultAzureCredential credential = new DefaultAzureCredentialBuilder()
            .build();
        
        TableServiceClient client = new TableServiceClientBuilder()
            .endpoint("<table-endpoint>")
            .credential(credential)
            .buildClient();

        TableClient table = client
            .getTableClient("<table-name>");

        table.getEntity("<partition-key>", "<row-key>");
    }
}