---
cuppy_hub: azure
cuppy_path: reference/cosmos-db/access-and-modeling.md
cuppy_section: docs
cuppy_doc: B2NQ-W3P3
cuppy_doc_uuid: db41c8cb-a18d-42f7-916a-3f7e20bcd95f
cuppy_permalink: https://cuppy.io/azure/d/B2NQ-W3P3
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/azure/docs/reference/cosmos-db/access-and-modeling
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 4164b09c7a6a1d45a6be0efd5b016672618164376c3fd79fdb9f60117f7a5210
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# 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 both `executeQuery` and `readChangeFeed`. Custom roles can't use `notDataActions`.
- 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** with `az 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: true`** turns off keys and resource tokens, so only Entra works. `disableKeyBasedMetadataWriteAccess` only blocks metadata writes with keys.
- SDK token scope: `<account endpoint>/.default`; `@azure/cosmos` falls back to `https://cosmos.azure.com/.default` if Entra answers `AADSTS500011`.

## Network and TLS

- `publicNetworkAccess`: `Enabled`, `Disabled` or `SecuredByPerimeter`; IP and VNet rules otherwise; private endpoint `groupId` `Sql`, zone `privatelink.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: set `minimalTlsVersion: '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 `/id` works 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

```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`.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 107 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/azure/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
