Cosmos DB for NoSQL access and design — data-plane RBAC, keys off, network, TLS, partition keys, serverless, with Bicep
Setting up Azure Cosmos DB for NoSQL so apps reach it without keys — the Cosmos-native data-plane roles (Built-in Data Reader and Contributor), their scopes and why Azure RBAC roles don't grant data access, disableLocalAuth, network and TLS settings, choosing a partition key (immutable; 20 GB per logical partition; hierarchical keys), serverless limits, and Bicep for an account, container and role assignment.
markdownreferencev1B2NQ-W3P3stage: designtrust: officialservice: cosmos-dbinterface: iacinterface: cliinterface: sdkCosmos DB data plane RBACCosmos DB Built-in Data ContributorCosmos DB Built-in Data Reader00000000-0000-0000-0000-00000000000200000000-0000-0000-0000-000000000001sqlRoleAssignmentssqlRoleDefinitionsaz cosmosdb sql role assignment createrole assignment scope /dbs /collsCosmos DB OperatorDocumentDB Account Contributor no data accessdisableLocalAuthdisableKeyBasedMetadataWriteAccessreadMetadataexecuteQuery readChangeFeedCosmos DB publicNetworkAccessminimalTlsVersion Tls12TLS 1.0 1.1 retired Cosmos August 31 2025partition key chooselogical partition 20 GBphysical partition 10000 RU/s 50 GBhierarchical partition keyschange partition key container copyserverless EnableServerlessserverless single region 5000 RU/sMicrosoft.DocumentDB/databaseAccounts@2026-03-15privatelink.documents.azure.com
Cosmos DB for NoSQL: access and design
Two permission systems
Control plane (Azure RBAC on the account: Owner, Contributor, DocumentDB Account Contributor, Cosmos DB Operator) manages accounts, databases, containers and throughput. Data plane (Cosmos-native roles, assigned through sqlRoleAssignments) reads and writes items. Neither implies the other: an Owner without a data role gets 403 on items, and a data role can't create containers through the SDK (reference/cosmos-db/errors.md, substatus 5300).
| Built-in data role | ID | Allows |
|---|---|---|
| Cosmos DB Built-in Data Reader | 00000000-0000-0000-0000-000000000001 | readMetadata, item read, executeQuery, readChangeFeed |
| Cosmos DB Built-in Data Contributor | 00000000-0000-0000-0000-000000000002 | readMetadata, containers/*, containers/items/* |
- Scope:
/(whole account),/dbs/<db>,/dbs/<db>/colls/<container>, or the same under the account's resource ID. Queries through the SDKs need bothexecuteQueryandreadChangeFeed. Custom roles can't usenotDataActions. - Assign with
az cosmosdb sql role assignment create -g <rg> --account-name <acct> --role-definition-id 00000000-0000-0000-0000-000000000002 --principal-id <object id> --scope "/", or Bicep below. Not withaz role assignment create: these aren't Azure RBAC roles (a Microsoft Q&A answer suggests otherwise; it's wrong). The portal can't manage data-plane assignments. disableLocalAuth: trueturns off keys and resource tokens, so only Entra works.disableKeyBasedMetadataWriteAccessonly blocks metadata writes with keys.- SDK token scope:
<account endpoint>/.default;@azure/cosmosfalls back tohttps://cosmos.azure.com/.defaultif Entra answersAADSTS500011.
Network and TLS
publicNetworkAccess:Enabled,DisabledorSecuredByPerimeter; IP and VNet rules otherwise; private endpointgroupIdSql, zoneprivatelink.documents.azure.com. Firewall changes take up to 15 minutes.- TLS 1.0 and 1.1 ended on August 31, 2025; new accounts default to
Tls12. The REST spec still says the default is TLS 1.0: setminimalTlsVersion: 'Tls12'explicitly.
Partition keys (choose once)
- A container's partition key can't be changed in place: moving means a new container (container copy jobs) or, for NoSQL, global secondary indexes.
- One logical partition (one key value) holds at most 20 GB; a physical partition serves up to 10,000 RU/s and 50 GB. Exceeding the logical limit gives 403 substatus 1014.
- Pick a property with high cardinality that most queries filter on with equality; for point-read/write workloads
/idworks well. Hierarchical keys (/tenantId,/userId,/id) spread large tenants; Microsoft's pages disagree on the maximum depth (three or four levels), and a unique last level rules out transactional batch and stored procedures. - Item size limit: 2 MB (413 above it).
Serverless or provisioned
Serverless (capabilities: [{ name: 'EnableServerless' }]): single region only, no throughput settings (passing throughput is an error), no shared-throughput databases, each physical partition up to 5,000 RU/s. Choose provisioned (manual or autoscale) for multi-region or steady load.
Bicep
// Cosmos DB for NoSQL: keys off, a database and container, and a data-plane role for an app identity
param location string = resourceGroup().location
param accountName string
param appPrincipalId string // the app's managed identity (principal ID)
resource account 'Microsoft.DocumentDB/databaseAccounts@2026-03-15' = {
name: accountName
location: location
kind: 'GlobalDocumentDB'
properties: {
databaseAccountOfferType: 'Standard'
disableLocalAuth: true // keys and resource tokens stop working; Entra only
publicNetworkAccess: 'Disabled' // reach it through a private endpoint (groupId 'Sql')
minimalTlsVersion: 'Tls12'
consistencyPolicy: {
defaultConsistencyLevel: 'Session'
}
locations: [
{
locationName: location
failoverPriority: 0
isZoneRedundant: false
}
]
capabilities: [
{
name: 'EnableServerless'
}
]
}
}
resource db 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2026-03-15' = {
parent: account
name: 'shop'
properties: {
resource: {
id: 'shop'
}
}
}
resource orders 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2026-03-15' = {
parent: db
name: 'orders'
properties: {
resource: {
id: 'orders'
partitionKey: {
paths: ['/customerId'] // can't be changed later
kind: 'Hash'
version: 2
}
}
}
}
// Cosmos DB Built-in Data Contributor (a Cosmos data-plane role, not an Azure RBAC role)
resource dataContributor 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2026-03-15' existing = {
parent: account
name: '00000000-0000-0000-0000-000000000002'
}
resource appData 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2026-03-15' = {
parent: account
name: guid(account.id, appPrincipalId, dataContributor.id)
properties: {
roleDefinitionId: dataContributor.id
principalId: appPrincipalId
scope: '${account.id}/dbs/shop'
}
}
The app identity needs no Azure role on the account to read and write items: the sqlRoleAssignments entry is the permission. Code: reference/cosmos-db/errors.md. MongoDB, Table, Cassandra and Gremlin, and Azure DocumentDB: reference/cosmos-db/other-apis-and-documentdb.md.