{"id":12589,"date":"2026-06-25T06:00:50","date_gmt":"2026-06-25T13:00:50","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cosmosdb\/?p=12589"},"modified":"2026-06-24T09:08:49","modified_gmt":"2026-06-24T16:08:49","slug":"which-azure-cosmos-db-role-does-my-app-need","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cosmosdb\/which-azure-cosmos-db-role-does-my-app-need\/","title":{"rendered":"Which Azure Cosmos DB Role Does My App Need?"},"content":{"rendered":"<p><em>In the previous post in the series, we covered the <a href=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/im-starting-a-new-cosmos-db-app-what-security-do-i-actually-need\/\">security decisions you make on day one<\/a>. In this part, we will talk about how to give your app access to Cosmos DB data, using roles and a managed identity instead of keys.<\/em><\/p>\n<h2><strong>The situation<\/strong><\/h2>\n<p>You\u2019ve built your app. It works locally. Now you\u2019re ready to connect it to Azure Cosmos DB, and you hit the question: how does my app get access? There are really only three questions you need to answer:<\/p>\n<ol>\n<li><strong>Who<\/strong>\u00a0is asking for access? (your app&#8217;s identity)<\/li>\n<li><strong>What<\/strong>\u00a0are they allowed to do? (a role)<\/li>\n<li><strong>Where<\/strong>\u00a0does that permission apply? (a scope)<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2026\/06\/Blog-2-2.png\"><img decoding=\"async\" class=\"aligncenter wp-image-12595 size-large\" src=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2026\/06\/Blog-2-2-1024x559.png\" alt=\"Illustrated Azure Cosmos DB access guide showing the formula \u201cApp + Role + Scope = Access,\u201d with managed identities, Reader and Contributor roles, account-to-container scopes, control-plane versus data-plane permissions, CLI commands, and keyless code access.\" width=\"1024\" height=\"559\" srcset=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2026\/06\/Blog-2-2-1024x559.png 1024w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2026\/06\/Blog-2-2-300x164.png 300w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2026\/06\/Blog-2-2-768x419.png 768w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2026\/06\/Blog-2-2.png 1408w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<h2><strong>First, the one distinction that trips everyone up<\/strong><\/h2>\n<p>Cosmos DB has two completely separate permission systems. If you remember nothing else from this post, remember this:<\/p>\n<table style=\"border-collapse: collapse; width: 100%; height: 96px;\">\n<tbody>\n<tr style=\"height: 24px;\">\n<td style=\"width: 14.6552%; height: 24px;\"><\/td>\n<td style=\"width: 35.3448%; height: 24px;\"><strong>Control Plane<\/strong><\/td>\n<td style=\"width: 25%; height: 24px;\"><strong>Data Plane<\/strong><\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 14.6552%; height: 24px;\"><strong>What it governs<\/strong><\/td>\n<td style=\"width: 35.3448%; height: 24px;\">Managing the account: create\/delete databases and containers, change throughput, read keys, configure networking<\/td>\n<td style=\"width: 25%; height: 24px;\">Reading and writing the actual data: documents, queries, stored procedures<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 14.6552%; height: 24px;\"><strong>Where roles live<\/strong><\/td>\n<td style=\"width: 35.3448%; height: 24px;\">Azure RBAC (the same <code class=\"language-default\" style=\"font-size: inherit;\">Microsoft.Authorization<\/code>roles you use everywhere in Azure)<\/td>\n<td style=\"width: 25%; height: 24px;\">Cosmos DB&#8217;s own data plane RBAC (separate role definitions)<\/td>\n<\/tr>\n<tr style=\"height: 24px;\">\n<td style=\"width: 14.6552%; height: 24px;\"><strong>Example role<\/strong><\/td>\n<td style=\"width: 35.3448%; height: 24px;\">&#8220;Cosmos DB Account Reader&#8221;<\/p>\n<p>&#8220;Contributor&#8221;<\/td>\n<td style=\"width: 25%; height: 24px;\">&#8220;Cosmos DB Built-in Data Reader,&#8221;<\/p>\n<p>&#8220;Cosmos DB Built-in Data Contributor&#8221;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2><strong>The three pieces<\/strong><\/h2>\n<h3><strong>1. Who: your app&#8217;s identity (use a managed identity)<\/strong><\/h3>\n<p>Your app needs an identity that Azure recognizes. You have two real options:<\/p>\n<ul>\n<li><strong>Managed identity<\/strong>\u00a0(recommended): Azure manages the credentials for you. No secrets, no rotation, nothing in your config files. If your app runs on App Service, Functions, Container Apps, AKS, or a VM, use this.<\/li>\n<li><strong>Service principal<\/strong>: a manually registered app identity with a client secret or certificate. Use this only when a managed identity is not available (for example, code running outside Azure).<\/li>\n<\/ul>\n<p>Pick managed identity unless you have a specific reason not to. It removes the single most common source of leaked Cosmos DB credentials: a connection string or key sitting in source control.<\/p>\n<p>To turn on a system-assigned managed identity for, say, an App Service:<\/p>\n<pre class=\"prettyprint language-default\"><code class=\"language-default\">az webapp identity assign \\ \r\n--name my-app \\ \r\n--resource-group my-rg<\/code><\/pre>\n<p>That command returns a <code class=\"language-default\">principalId<\/code><span style=\"font-family: Consolas, Monaco, monospace;\">. Hold onto it. That is the &#8220;who&#8221; in your role assignment.<\/span><\/p>\n<h3><strong>2. What: a built-in data role<\/strong><\/h3>\n<p>You do not need to invent roles. Cosmos DB ships two built-in data plane roles that cover almost every app:<\/p>\n<table style=\"border-collapse: collapse; width: 100%; height: 120px;\">\n<tbody>\n<tr style=\"height: 24px;\">\n<td style=\"width: 33.3333%; height: 24px;\"><span style=\"font-size: 14pt;\"><strong>Role<\/strong><\/span><\/td>\n<td style=\"width: 33.3333%; height: 24px;\"><span style=\"font-size: 14pt;\"><strong>ID<\/strong><\/span><\/td>\n<td style=\"width: 33.3333%; height: 24px;\"><span style=\"font-size: 14pt;\"><strong>Capabilities<\/strong><\/span><\/td>\n<\/tr>\n<tr style=\"height: 48px;\">\n<td style=\"width: 33.3333%; height: 48px;\">Cosmos DB Built-in Data Reader<\/td>\n<td style=\"width: 33.3333%; height: 48px;\"><code>00000000-0000-0000-0000-000000000001<\/code><\/td>\n<td style=\"width: 33.3333%; height: 48px;\">Read documents, run queries, read change feed<\/td>\n<\/tr>\n<tr style=\"height: 48px;\">\n<td style=\"width: 33.3333%; height: 48px;\">Cosmos DB Built-in Data Contributor<\/td>\n<td style=\"width: 33.3333%; height: 48px;\"><code>00000000-0000-0000-0000-000000000002<\/code><\/td>\n<td style=\"width: 33.3333%; height: 48px;\">Everything the reader can do, plus create, replace, upsert and delete documents<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Most apps that write data want <strong>Data Contributor<\/strong>. A read-only reporting service or a cache-warming job wants <strong>Data Reader<\/strong>. Start with the narrowest role that still lets your app do its job. You can always widen it later, and your future self-doing a security review will thank you.<\/p>\n<p>If neither built-in role fits (for example, you want to write but not delete), you can define a custom role. That is a more advanced topic. For now, the built-ins are almost certainly what you want.<\/p>\n<h3><strong>3. Where: the scope<\/strong><\/h3>\n<p>A role assignment applies to a scope. For data plane roles, the scope is a path inside your account:<\/p>\n<ul>\n<li><strong>Whole account <\/strong>(<code class=\"language-default\">\/<\/code><span style=\"font-family: Consolas, Monaco, monospace;\">): <\/span>The role applies to every database and container. Simple, and fine for most single-app accounts.<\/li>\n<li><strong>A specific database or container<\/strong>: The role applies only to that path. Use this when different apps share an account and should be isolated.<\/li>\n<\/ul>\n<p>For your first app, scope to the account. Tighten later if you split workloads.<\/p>\n<h3><strong>Putting it together<\/strong><\/h3>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\"># Variables\r\nACCOUNT=\"my-cosmos-account\"\r\nRESOURCE_GROUP=\"my-rg\"\r\nPRINCIPAL_ID=\"&lt;the principalId from your managed identity&gt;\"\r\n\r\n# The built-in Data Contributor role\r\nROLE_ID=\"00000000-0000-0000-0000-000000000002\"\r\n\r\n# Scope to the whole account\r\nSCOPE=\"\/\"\r\n\r\naz cosmosdb sql role assignment create \\\r\n--account-name \"$ACCOUNT\" \\\r\n--resource-group \"$RESOURCE_GROUP\" \\\r\n--role-definition-id \"$ROLE_ID\" \\\r\n--principal-id \"$PRINCIPAL_ID\" \\\r\n--scope \"$SCOPE\"<\/code><\/pre>\n<p>With this, your app&#8217;s identity can now read and write data. Note this command uses <code class=\"language-default\">cosmosdb sql role assignment<\/code>, which is the <strong>data plane<\/strong> RBAC surface, distinct from <code class=\"language-default\">az role assignment create<\/code>(which is Azure control plane RBAC). Easy to confuse, very different effects.<\/p>\n<p><div class=\"alert alert-success\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Lightbulb\"><\/i><strong>Tip<\/strong><\/p> Want to see what roles you already have? Use the below to see every data plane assignment on the account<\/p>\n<p><code>az cosmosdb sql role assignment list<\/code><\/p>\n<p><code>--account-name \"$ACCOUNT\"<\/code><\/p>\n<p><code>--resource-group \"$RESOURCE_GROUP\"<\/code><\/p>\n<p><\/div><\/p>\n<h2><strong>Now wire it into your app<\/strong><\/h2>\n<p>With the role assigned, your code authenticates with the managed identity instead of a key. The SDK does the token work for you through<code class=\"language-default\">DefaultAzureCredential<\/code>, which automatically picks up the managed identity in Azure and your developer sign-in locally.<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">using Azure.Identity;\r\nusing Microsoft.Azure.Cosmos;\r\n\r\nvar client = new CosmosClient(\r\n   accountEndpoint: \"https:\/\/my-cosmos-account.documents.azure.com:443\/\",\r\n   tokenCredential: new DefaultAzureCredential());<\/code><\/pre>\n<p>The other SDKs follow the same shape: pass<code class=\"language-default\">DefaultAzureCredential<\/code>instead of a key. Notice what is missing here: there is no key, no connection string, no secret. The endpoint is not sensitive. The identity and the role assignment do all the work.<\/p>\n<p>That is really all there is to it: three answers, one role assignment, and not a single secret to manage. Once those three questions feel natural, the harder stuff gets a lot less intimidating, whether you are sharing access with your team or tightening things down for production.<\/p>\n<h2>Try it yourself<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"122\">The best way to make this stick is to do it once, end to end:<\/p>\n<ol class=\"code-line\" dir=\"auto\" data-line=\"124\">\n<li class=\"code-line\" dir=\"auto\" data-line=\"124\"><strong>Turn on a managed identity<\/strong>\u00a0for an app you already have running in Azure (App Service, Functions, or Container Apps).<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"125\"><strong>Assign the Built-in Data Reader role<\/strong>\u00a0scoped to your account, using the commands above.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"126\"><strong>Swap your key for\u00a0<\/strong><code>DefaultAzureCredential<\/code>\u00a0in your code and confirm reads still work.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"127\"><strong>Delete the key or connection string<\/strong>\u00a0from your config and watch your app keep running. That deletion is the whole point.<\/li>\n<\/ol>\n<div class=\"markdown-heading\" dir=\"auto\">\n<h2 class=\"heading-element\" dir=\"auto\" tabindex=\"-1\">About Azure Cosmos DB<\/h2>\n<\/div>\n<p dir=\"auto\">Azure Cosmos DB is a fully managed and serverless NoSQL and vector database for modern app development, including AI applications. With its SLA-backed speed and availability as well as instant dynamic scalability, it is ideal for real-time NoSQL and MongoDB applications that require high performance and distributed computing over massive volumes of NoSQL and vector data.<\/p>\n<p dir=\"auto\">To stay in the loop on Azure Cosmos DB updates, follow us on\u00a0<a href=\"https:\/\/twitter.com\/AzureCosmosDB\" rel=\"nofollow\">X<\/a>,\u00a0<a href=\"https:\/\/aka.ms\/AzureCosmosDBYouTube\" rel=\"nofollow\">YouTube<\/a>, and\u00a0<a href=\"https:\/\/www.linkedin.com\/company\/azure-cosmos-db\/\" rel=\"nofollow\">LinkedIn<\/a>. Join the discussion with other developers on the\u00a0<a href=\"https:\/\/discord.gg\/pczdC2SU\" rel=\"nofollow\">#nosql channel on the Microsoft Open Source Discord<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous post in the series, we covered the security decisions you make on day one. In this part, we will talk about how to give your app access to Cosmos DB data, using roles and a managed identity instead of keys. The situation You\u2019ve built your app. It works locally. Now you\u2019re ready [&hellip;]<\/p>\n","protected":false},"author":188311,"featured_media":12609,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[14,1918,667],"tags":[499,1913,668],"class_list":["post-12589","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-core-sql-api","category-rbac","category-security","tag-azure-cosmos-db","tag-rbac","tag-security"],"acf":[],"blog_post_summary":"<p>In the previous post in the series, we covered the security decisions you make on day one. In this part, we will talk about how to give your app access to Cosmos DB data, using roles and a managed identity instead of keys. The situation You\u2019ve built your app. It works locally. Now you\u2019re ready [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/12589","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/users\/188311"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/comments?post=12589"}],"version-history":[{"count":2,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/12589\/revisions"}],"predecessor-version":[{"id":12627,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/12589\/revisions\/12627"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/media\/12609"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/media?parent=12589"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/categories?post=12589"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/tags?post=12589"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}