|
| 1 | +--- |
| 2 | +title: How to use a system-assigned managed identity to access Azure Cosmos DB data |
| 3 | +description: Learn how to configure an Azure AD system-assigned managed identity to access keys from Azure Cosmos DB. msi, managed service identity, aad, azure active directory, identity |
| 4 | +author: j-patrick |
| 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 | +# How to use a system-assigned managed identity to access Azure Cosmos DB data |
| 14 | + |
| 15 | +In this article you will set up a **robust, key rotation agnostic,** solution to access Azure Cosmos DB keys by leveraging [managed identities](../active-directory/managed-identities-azure-resources/services-support-managed-identities.md). The example in this article uses an Azure Function. However, you can achieve this solution by using any service that supports managed identities. |
| 16 | + |
| 17 | +You'll learn how to create an Azure Function that can access Azure Cosmos DB without needing to copy any Azure Cosmos DB keys. The function will wake up every minute and record the current temperature of an aquarium fish tank. To learn how to set up a timer triggered Azure Function see the [Create a function in Azure that is triggered by a timer](../azure-functions/functions-create-scheduled-function.md) article. |
| 18 | + |
| 19 | +To simplify the scenario, cleanup of older temperature documents is handled by an already configured [Time To Live](./time-to-live.md) setting. |
| 20 | + |
| 21 | +## Assign a system-assigned managed identity to an Azure Function |
| 22 | + |
| 23 | +In this step, you'll assign a system-assigned managed identity to your Azure Function. |
| 24 | + |
| 25 | +1. In the [Azure portal](https://portal.azure.com/), open the **Azure Function** pane and navigate to your function app. |
| 26 | + |
| 27 | +1. Open the **Platform features** > **Identity** tab: |
| 28 | + |
| 29 | +  |
| 30 | + |
| 31 | +1. On the **Identity** tab, turn **On** the **System Identity** status. Be sure to select **Save**, and confirm that you want to turn on the system identity. At the end the **System Identity** pane should look as follows: |
| 32 | + |
| 33 | +  |
| 34 | + |
| 35 | +## Grant the managed identity access to your Azure Cosmos account |
| 36 | + |
| 37 | +In this step, you'll assign a role to the Azure Function's system-assigned managed identity. Azure Cosmos DB has multiple built-in roles that you can assign to the managed identity. For this solution, you will use the following two roles: |
| 38 | + |
| 39 | +|Built-in role |Description | |
| 40 | +|---------|---------| |
| 41 | +|[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. | |
| 42 | +|[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. | |
| 43 | + |
| 44 | +> [!IMPORTANT] |
| 45 | +> RBAC support in Azure Cosmos DB is applicable to control plane operations only. Data plane operations are secured using master keys or resource tokens. To learn more, see the [Secure access to data](secure-access-to-data.md) article. |
| 46 | +
|
| 47 | +> [!TIP] |
| 48 | +> When assigning roles, only assign the needed access. If your service only requires reading data, then assign the managed identity to **Cosmos DB Account Reader** role. For more information about the importance of least privilege access, see the [lower exposure of privileged accounts](../security/fundamentals/identity-management-best-practices.md#lower-exposure-of-privileged-accounts) article. |
| 49 | +
|
| 50 | +For your scenario, you will read the temperature, then write back that data to a container in Azure Cosmos DB. Because you have to write the data, you will use the **DocumentDB Account Contributor** role. |
| 51 | + |
| 52 | +1. Sign in to the Azure portal and navigate to your Azure Cosmos DB account. Open the **Access Management (IAM) Pane**, and then the **Role Assignments** tab: |
| 53 | + |
| 54 | +  |
| 55 | + |
| 56 | +1. Select the **+ Add** button, then **add role assignment**. |
| 57 | + |
| 58 | +1. The **Add Role Assignment** panel opens to the right: |
| 59 | + |
| 60 | +  |
| 61 | + |
| 62 | + * **Role** - Select **DocumentDB Account Contributor** |
| 63 | + * **Assign access to** - Under the Select **System-assigned managed identity** subsection, select **Function App**. |
| 64 | + * **Select** - The pane will be populated with all the function apps, in your subscription, that have a **Managed System Identity**. In our case I select the **SummaryService** function app: |
| 65 | + |
| 66 | +  |
| 67 | + |
| 68 | +1. After the function app's identity is selected click **Save**. |
| 69 | + |
| 70 | +## Programmatically access the Azure Cosmos DB keys from the Azure Function |
| 71 | + |
| 72 | +Now we have a function app that has a system-assigned managed identity. That identity is given the **DocumentDB Account Contributor** role in the Azure Cosmos DB permissions. The following function app code will get the Azure Cosmos DB keys, create a CosmosClient object, get the temperature, then save this to Cosmos DB. |
| 73 | + |
| 74 | +This sample uses the [List Keys API](https://docs.microsoft.com/rest/api/cosmos-db-resource-provider/DatabaseAccounts/ListKeys) to access your Azure Cosmos DB account keys. |
| 75 | + |
| 76 | +> [!IMPORTANT] |
| 77 | +> If you want to [assign the **Cosmos DB Account Reader**](#grant-the-managed-identity-access-to-your-azure-cosmos-account) role, you will need to use the read only [List Keys api](https://docs.microsoft.com/rest/api/cosmos-db-resource-provider/DatabaseAccounts/ListReadOnlyKeys). This will only populate the read only keys. |
| 78 | +
|
| 79 | +The List Keys API returns the `DatabaseAccountListKeysResult` object. This type isn't defined in the C# libraries. The following code shows the implementation of this class: |
| 80 | + |
| 81 | +```csharp |
| 82 | +namespace SummarizationService |
| 83 | +{ |
| 84 | + public class DatabaseAccountListKeysResult |
| 85 | + { |
| 86 | + public string primaryMasterKey {get;set;} |
| 87 | + public string primaryReadonlyMasterKey {get; set;} |
| 88 | + public string secondaryMasterKey {get; set;} |
| 89 | + public string secondaryReadonlyMasterKey {get;set;} |
| 90 | + } |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +The example also uses a simple document called "TemperatureRecord", which is defined as follows: |
| 95 | + |
| 96 | +```csharp |
| 97 | +using System; |
| 98 | + |
| 99 | +namespace Monitor |
| 100 | +{ |
| 101 | + public class TemperatureRecord |
| 102 | + { |
| 103 | + public string id { get; set; } = Guid.NewGuid().ToString(); |
| 104 | + public DateTime RecordTime { get; set; } |
| 105 | + public int Temperature { get; set; } |
| 106 | + |
| 107 | + } |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +You will use the [Microsoft.Azure.Services.AppAuthentication](https://www.nuget.org/packages/Microsoft.Azure.Services.AppAuthentication) library to get the system-assigned managed identity token. To learn other ways to get the token and more information about the `Microsoft.Azure.Service.AppAuthentication` library, see the [Service To Service Authentication](../key-vault/service-to-service-authentication.md) article. |
| 112 | + |
| 113 | +```csharp |
| 114 | +using System; |
| 115 | +using System.Net.Http; |
| 116 | +using System.Net.Http.Headers; |
| 117 | +using System.Threading.Tasks; |
| 118 | +using Microsoft.Azure.Cosmos; |
| 119 | +using Microsoft.Azure.Services.AppAuthentication; |
| 120 | +using Microsoft.Azure.WebJobs; |
| 121 | +using Microsoft.Extensions.Logging; |
| 122 | + |
| 123 | +namespace Monitor |
| 124 | +{ |
| 125 | + public static class TemperatureMonitor |
| 126 | + { |
| 127 | + private static string subscriptionId = |
| 128 | + "<azure subscription id>"; |
| 129 | + private static string resourceGroupName = |
| 130 | + "<name of your azure resource group>"; |
| 131 | + private static string accountName = |
| 132 | + "<Azure Cosmos DB account name>"; |
| 133 | + private static string cosmosDbEndpoint = |
| 134 | + "<Azure Cosmos DB endpoint>"; |
| 135 | + private static string databaseName = |
| 136 | + "<Azure Cosmos DB name>"; |
| 137 | + private static string containerName = |
| 138 | + "<container to store the temperature in>"; |
| 139 | + |
| 140 | + [FunctionName("TemperatureMonitor")] |
| 141 | + public static async Task Run([TimerTrigger("0 * * * * *")]TimerInfo myTimer, ILogger log) |
| 142 | + { |
| 143 | + log.LogInformation($"Starting temperature monitoring: {DateTime.Now}"); |
| 144 | + |
| 145 | + // AzureServiceTokenProvider will help us to get the Service Managed token. |
| 146 | + var azureServiceTokenProvider = new AzureServiceTokenProvider(); |
| 147 | + |
| 148 | + // In order to get the Service Managed token we need to authenticate to the Azure Resource Manager. |
| 149 | + string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://management.azure.com/"); |
| 150 | + |
| 151 | + // To get the Azure Cosmos DB keys setup the List Keys API: |
| 152 | + string endpoint = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/{accountName}/listKeys?api-version=2019-12-12"; |
| 153 | + |
| 154 | + // setup an HTTP Client and add the access token. |
| 155 | + HttpClient httpClient = new HttpClient(); |
| 156 | + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); |
| 157 | + |
| 158 | + // Post to the endpoint to get the keys result. |
| 159 | + var result = await httpClient.PostAsync(endpoint, new StringContent("")); |
| 160 | + |
| 161 | + // Get the Result back as a DatabaseAccountListKeysResult. |
| 162 | + DatabaseAccountListKeysResult keys = await result.Content.ReadAsAsync<DatabaseAccountListKeysResult>(); |
| 163 | + |
| 164 | + log.LogInformation("Starting to create the client"); |
| 165 | + |
| 166 | + CosmosClient client = new CosmosClient(cosmosDbEndpoint, keys.primaryMasterKey); |
| 167 | + |
| 168 | + log.LogInformation("Client created"); |
| 169 | + |
| 170 | + var database = client.GetDatabase(databaseName); |
| 171 | + var container = database.GetContainer(containerName); |
| 172 | + |
| 173 | + log.LogInformation("Get the temperature."); |
| 174 | + |
| 175 | + var tempRecord = new TemperatureRecord() { RecordTime = DateTime.UtcNow, Temperature = GetTemperature() }; |
| 176 | + |
| 177 | + log.LogInformation("Store temperature"); |
| 178 | + |
| 179 | + await container.CreateItemAsync<TemperatureRecord>(tempRecord); |
| 180 | + |
| 181 | + log.LogInformation($"Ending temperature monitor: {DateTime.Now}"); |
| 182 | + } |
| 183 | + |
| 184 | + private static int GetTemperature() |
| 185 | + { |
| 186 | + // fake the temperature sensor for this demo |
| 187 | + Random r = new Random(DateTime.UtcNow.Second); |
| 188 | + return r.Next(0, 120); |
| 189 | + } |
| 190 | + } |
| 191 | +} |
| 192 | +``` |
| 193 | + |
| 194 | +You are now ready to [deploy your Azure Function](../azure-functions/functions-create-first-function-vs-code.md). |
| 195 | + |
| 196 | +## Next steps |
| 197 | + |
| 198 | +* [Certificate-based authentication with Azure Cosmos DB and Active Directory](certificate-based-authentication.md) |
| 199 | +* [Secure Azure Cosmos keys using Azure Key Vault](access-secrets-from-keyvault.md) |
| 200 | +* [Security baseline for Azure Cosmos DB](security-baseline.md) |
0 commit comments