|
| 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 | + |
| 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 | + |
| 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 | + |
| 58 | + |
| 59 | +1. Select the **+ Add** button, then **add role assignment**: |
| 60 | + |
| 61 | + |
| 62 | +1. The **Role Assignment** panel opens to the right: |
| 63 | + |
| 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 | + |
| 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) |
0 commit comments