Skip to content

Commit 358015c

Browse files
committed
v1 of managed service authentication solution
1 parent 31d52fb commit 358015c

7 files changed

+198
-0
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
---
2+
title: Managed Identity based authentication with Azure Cosmos DB and Azure Active Directory
3+
description: Learn how to configure an Azure AD identity for msi-based authentication to access keys from Azure Cosmos DB.
4+
author: justipat
5+
ms.service: cosmos-db
6+
ms.topic: conceptual
7+
ms.date: 03/20/2020
8+
ms.author: justipat
9+
ms.reviewer: sngun
10+
11+
---
12+
13+
# Managed Identity based authentication and authorization with Azure Cosmos DB and Azure Active Directory
14+
15+
Managed Identity authentication enables your client application to be authenticated by using Azure Active Directory (Azure AD). You can perform Managed Identity based authentication / authorization on any service that [supports system assigned identities](/articles/active-directory/managed-identities-azure-resources/services-support-managed-identities.md). The end result will be a service that can read Azure Cosmos DB keys without having the keys directly in the service. The solution outlined in this article also creates a **robust key rotation agnostic** solution for cosmos key management.
16+
17+
You will learn how to:
18+
19+
* Assign a System Identity
20+
* Grant the System Identity access to your Cosmos DB
21+
* Write the code for robust Cosmos DB key management
22+
23+
In the solution below we will be building an Azure Function that wakes up every hour to read a set of sale receipts from Cosmos DB and create an hourly summary of sales, storing it back into Cosmos DB.
24+
25+
Though we are using Azure Functions below this will work with all Azure Services that support [system managed identity](/articles/active-directory/managed-identities-azure-resources/services-support-managed-identities.md)
26+
27+
## Assign a System Identity to the your Azure Function
28+
29+
In this step, you will assign a managed system identity to your Azure Function.
30+
31+
1. Sign into the [Azure portal](https://portal.azure.com/)
32+
33+
1. Open the Azure Function pane, and for your function app select the **Identity tab**:
34+
![Identity Tab](./media/managed-identity-based-authentication/identity-tab-selection.png)
35+
36+
1. On the **Identity tab** switch **System Identity** to the "On" position. Be sure to click **Save**. You will be prompted to confirm you want to turn this on. Please accept. In the end the **System Identity** pane should look like this:
37+
![Managed System Identity turned on](./media/managed-identity-based-authentication/identity-tab-system-managed-on.png)
38+
39+
## Grant the System Identity Access to your Cosmos DB.
40+
41+
In this step, you will assign a role to the Azure Function's System Identity. Cosmos DB has multiple built in roles you can assign the System Identity too. For this exercise we will just focus on two:
42+
43+
|**Built-in role** |**Description** |
44+
|---------|---------|
45+
|[DocumentDB Account Contributor](../role-based-access-control/built-in-roles.md#documentdb-account-contributor)|Can manage Azure Cosmos DB accounts. Allows retrieval of read/write keys. |
46+
|[Cosmos DB Account Reader](../role-based-access-control/built-in-roles.md#cosmos-db-account-reader-role)|Can read Azure Cosmos DB account data. Allows retrieval of read keys. |
47+
48+
> [!IMPORTANT]
49+
> RBAC support in Azure Cosmos DB applies to control plane operations only. Data plane operations are secured using master keys or resource tokens. To learn more, see [Secure access to data in Azure Cosmos DB](secure-access-to-data.md)
50+
51+
> [!TIP]
52+
> When assigning roles, only assign the needed access. So if your service only need to read, then only assign the Service Identity to **Cosmos DB Account Reader**. For more information on the importance of **least privilege access** please see [lower exposure of privileged accounts](/azure/security/fundamentals/identity-management-best-practices#lower-exposure-of-privileged-accounts).
53+
54+
For our scenario we need to be able to read the sale receipt documents summarize them and then write back that summary to Cosmos DB. We will use the **DocumentDB Account Contributor** role.
55+
56+
1. Open your Cosmos DB in the portal, select the **Access Management (IAM) Pane**, and the the **Role Assignments** tab:
57+
![IAM Pane](./media/managed-identity-based-authentication/cosmos-db-iam-tab.png)
58+
59+
1. Select the **+ Add** button, then **add role assignment**:
60+
![Add Role Assignment](./media/managed-identity-based-authentication/cosmos-db-iam-tab-select-add.png)
61+
62+
1. The **Role Assignment** panel opens to the right:
63+
![Add Role Assignment Pane](./media/managed-identity-based-authentication/cosmos-db-iam-tab-add-role-pane.png)
64+
65+
* **Role** - Select **DocumentDB Account Contributor**
66+
* **Assign access to** - Under the Select **System assigned managed identity** subsection, select **Function App**.
67+
* **Select** - The pane will be populated with all the function apps, in your subscription, that have a **System Managed Identity**. In our case I select the **SummaryService** function app:
68+
![Selection of Function App](./media/managed-identity-based-authentication/cosmos-db-iam-tab-add-role-pane-filled.png)
69+
70+
1. Select the function app and click **Save**.
71+
72+
## Programmatically access the Cosmos DB keys from the Azure Function
73+
74+
Now we have a function app that has a system managed identity. That identity is given the **DocumentDB Account Contributor** role in the Cosmos DB permissions. The code below will run on the **Function App** and will get the keys and create a CosmosClient. Then perform the the summarization business logic.
75+
76+
The api we will be using to get the Cosmos DB Keys is the [List Keys API](https://docs.microsoft.com/en-us/rest/api/cosmos-db-resource-provider/DatabaseAccounts/ListKeys).
77+
78+
79+
The api returns DatabaseAccountListKeysResult. This type is not defined in the C# libraries, but it is easy to implement. The follow is an implementation for this class. Add it to the solution:
80+
```csharp
81+
namespace SummarizationService
82+
{
83+
public class DatabaseAccountListKeysResult
84+
{
85+
public string primaryMasterKey {get;set;}
86+
public string primaryReadonlyMasterKey {get; set;}
87+
public string secondaryMasterKey {get; set;}
88+
public string secondaryReadonlyMasterKey {get;set;}
89+
}
90+
}
91+
```
92+
93+
The library we will use to get our Service Managed token is [Microsoft.Azure.Services.AppAuthentication](https://www.nuget.org/packages/Microsoft.Azure.Services.AppAuthentication). You can find other ways to get the token and more information about the Microsoft.Azure.Service.AppAuthentication library by reading up on [Service To Service Authentication](https://docs.microsoft.com/en-us/azure/key-vault/service-to-service-authentication).
94+
95+
96+
```csharp
97+
using System;
98+
using System.Collections.Generic;
99+
using System.Net.Http;
100+
using System.Net.Http.Headers;
101+
using System.Threading.Tasks;
102+
using Microsoft.Azure.Cosmos;
103+
using Microsoft.Azure.Services.AppAuthentication;
104+
using Microsoft.Azure.WebJobs;
105+
using Microsoft.Extensions.Logging;
106+
107+
namespace SummarizationService
108+
{
109+
public static class SummarizationFunction
110+
{
111+
private static string subscriptionId =
112+
"<azure subscription id>";
113+
private static string resourceGroupName = "
114+
<name of your azure resource group>";
115+
private static string accountName =
116+
"<cosmos db account name>";
117+
private static string cosmosDbEndpoint =
118+
"<cosmos db endpoint>";
119+
private static string databaseName =
120+
"<cosmos db name>";
121+
private static string containerName =
122+
"<container where the sales receipts are>";
123+
private static string indexToQuery =
124+
"<index to query for the sale receipts>";
125+
126+
[FunctionName("SummarizationService")]
127+
public static async Task Run([TimerTrigger("0 5 * * * *")]TimerInfo myTimer, ILogger log)
128+
{
129+
log.LogInformation($"Starting receipt processing: {DateTime.Now}");
130+
131+
// AzureServiceTokenProvider will help us to get the Service Managed token.
132+
var azureServiceTokenProvider = new AzureServiceTokenProvider();
133+
134+
// In order to get the Service Managed token we need to authenticate to the Azure Resource Manager.
135+
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://management.azure.com/");
136+
137+
// To get the Cosmos DB keys setup the List Keys API:
138+
string endpoint = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/{accountName}/listKeys?api-version=2019-12-12";
139+
140+
// setup an HTTP Client and add the access token.
141+
HttpClient httpClient = new HttpClient();
142+
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
143+
144+
// Post to the endpoint to get the keys result.
145+
var result = await httpClient.PostAsync(endpoint, new StringContent(""));
146+
147+
// Get the Result back as a DatabaseAccountListKeysResult.
148+
DatabaseAccountListKeysResult keys = await result.Content.ReadAsAsync<DatabaseAccountListKeysResult>();
149+
150+
log.LogInformation("Starting to create the client");
151+
152+
CosmosClient client = new CosmosClient(cosmosDbEndpoint, keys.primaryMasterKey);
153+
154+
log.LogInformation("Client created");
155+
156+
var database = client.GetDatabase(databaseName);
157+
var container = database.GetContainer(containerName);
158+
159+
// get all the receipts that are for "sales"
160+
QueryDefinition query = new QueryDefinition($"SELECT * FROM {containerName} f WHERE f.type = @type")
161+
.WithParameter("@type", "sales");
162+
163+
SummarySalesReceipt summarySales = new SummarySalesReceipt();
164+
165+
FeedIterator<SalesReceipt> resultSetIterator =
166+
container.GetItemQueryIterator<SalesReceipt>(query,
167+
requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(indexToQuery) });
168+
169+
while (resultSetIterator.HasMoreResults)
170+
{
171+
// Get all the sales receipts
172+
FeedResponse<SalesReceipt> response = await resultSetIterator.ReadNextAsync();
173+
174+
// ... summarization logic for sales receipts.
175+
// The summary is then added to the summarySales document.
176+
// Note: another function will handle cleanup ...
177+
178+
}
179+
180+
log.LogInformation("Finished the summarization");
181+
await container.CreateItemAsync<SummarySalesReceipt>(summarySales);
182+
183+
log.LogInformation($"Ending receipt processing: {DateTime.Now}");
184+
}
185+
}
186+
}
187+
```
188+
You are now ready to [deploy your Azure Function.](https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-function-vs-code?pivots=programming-language-csharp#publish-the-project-to-azure)
189+
190+
> [!IMPORTANT]
191+
> If you want to [assign the **Cosmos DB Account Reader**](#Grant-the-System-Identity-Access-to-your-Cosmos-DB.), you will need to use the read only [List Keys api](https://docs.microsoft.com/en-us/rest/api/cosmos-db-resource-provider/DatabaseAccounts/ListReadOnlyKeys). This would only populate the read only keys on the DatabaseAccountListKeysResult class.
192+
193+
## Next steps
194+
195+
* [Certificate-based authentication with Azure Cosmos DB and Active Directory](certificate-based-authentication.md)
196+
* [Secure Azure Cosmos keys using Azure Key Vault](access-secrets-from-keyvault.md)
197+
198+
* [Security baseline for Azure Cosmos DB](security-baseline.md)
22.8 KB
Loading
48.9 KB
Loading
55.9 KB
Loading
55.8 KB
Loading
146 KB
Loading
64.5 KB
Loading

0 commit comments

Comments
 (0)