Skip to content

Commit cdc228e

Browse files
authored
Merge pull request #14 from Azure-Samples/copilot/fix-13
Fix managed identity compliance by removing Cosmos DB connection string configuration
2 parents 63b89b6 + e7b7b04 commit cdc228e

5 files changed

Lines changed: 186 additions & 28 deletions

File tree

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Converting Azure Templates from Cosmos DB Connection Strings to Managed Identity
2+
3+
This prompt provides step-by-step instructions for converting Azure Bicep templates from using Cosmos DB connection strings to managed identity authentication, ensuring compliance with Azure security policies that disallow local authentication methods.
4+
5+
## Problem Context
6+
7+
Azure security policies often require disabling local authentication methods for Cosmos DB, which means connection strings cannot be used. Templates must be updated to use managed identity authentication instead.
8+
9+
## Prerequisites
10+
11+
Before starting, ensure:
12+
- The application code already supports managed identity (using `DefaultAzureCredential` and endpoint-based authentication)
13+
- The template has system-assigned managed identity configured for the app service
14+
- You understand the structure of your Bicep template files
15+
16+
## Step-by-Step Conversion Process
17+
18+
### 1. Remove Connection String Configuration
19+
20+
**In your Cosmos DB module (e.g., `infra/app/db-avm.bicep`):**
21+
22+
Remove the following parameters and configurations:
23+
```bicep
24+
// Remove these parameters
25+
param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING'
26+
param keyVaultResourceId string
27+
28+
// Remove this entire secretsExportConfiguration block
29+
secretsExportConfiguration:{
30+
keyVaultResourceId: keyVaultResourceId
31+
primaryWriteConnectionStringSecretName: connectionStringKey
32+
}
33+
34+
// Remove this output
35+
output connectionStringKey string = connectionStringKey
36+
```
37+
38+
**In your main template (e.g., `infra/main.bicep`):**
39+
40+
Remove connection string related configurations:
41+
```bicep
42+
// Remove from cosmos module parameters
43+
keyVaultResourceId: keyVault.outputs.resourceId
44+
45+
// Remove from app settings
46+
AZURE_COSMOS_CONNECTION_STRING_KEY: cosmos.outputs.connectionStringKey
47+
48+
// Remove from template outputs
49+
output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey
50+
```
51+
52+
### 2. Enable Managed Identity Authentication
53+
54+
**Add `disableLocalAuth: true` to your Cosmos DB module:**
55+
```bicep
56+
module cosmos 'br/public:avm/res/document-db/database-account:0.6.0' = {
57+
name: 'cosmos-sql'
58+
params: {
59+
name: accountName
60+
location: location
61+
tags: tags
62+
disableLocalAuth: true // ← Add this line
63+
// ... other parameters
64+
}
65+
}
66+
```
67+
68+
### 3. Handle Role Assignments Properly
69+
70+
**Critical:** Avoid duplicate Cosmos DB deployments that can override the `disableLocalAuth` setting.
71+
72+
**Create a separate role assignment module** (`infra/app/cosmos-role-assignment.bicep`):
73+
```bicep
74+
param cosmosAccountName string
75+
param apiPrincipalId string
76+
77+
// Create a role assignment for the API's managed identity to access Cosmos DB
78+
// Using the built-in "Cosmos DB Built-in Data Contributor" role
79+
resource apiCosmosRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2024-05-15' = {
80+
name: '${cosmosAccountName}/${guid(apiPrincipalId, cosmosAccountName, '00000000-0000-0000-0000-000000000002')}'
81+
properties: {
82+
roleDefinitionId: '${resourceGroup().id}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosAccountName}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002'
83+
principalId: apiPrincipalId
84+
scope: '${resourceGroup().id}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosAccountName}'
85+
}
86+
}
87+
```
88+
89+
**In your main template, reference this module:**
90+
```bicep
91+
// Give the API access to Cosmos using a separate role assignment
92+
module apiCosmosRoleAssignment './app/cosmos-role-assignment.bicep' = {
93+
name: 'api-cosmos-role'
94+
scope: rg
95+
params: {
96+
cosmosAccountName: cosmos.outputs.accountName
97+
apiPrincipalId: api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID
98+
}
99+
}
100+
```
101+
102+
### 4. Important Role Assignment Details
103+
104+
**Use built-in role GUIDs, not custom role names:**
105+
- Cosmos DB Built-in Data Reader: `00000000-0000-0000-0000-000000000001`
106+
- Cosmos DB Built-in Data Contributor: `00000000-0000-0000-0000-000000000002`
107+
108+
**Avoid these common mistakes:**
109+
- Using custom role definition names (like 'writer') instead of GUIDs
110+
- Creating standalone role assignment resources at subscription scope
111+
- Having circular dependencies between cosmos and API modules
112+
113+
### 5. Verify Required Components Remain
114+
115+
Ensure these components are preserved:
116+
- ✅ System-assigned managed identity for the API app service
117+
-`AZURE_COSMOS_ENDPOINT` environment variable for the API
118+
- ✅ Key Vault access policies for the API's managed identity (if using Key Vault)
119+
- ✅ Proper resource group scope for all resources
120+
121+
### 6. Testing and Validation
122+
123+
**Validate your changes:**
124+
1. Run `bicep build` on your templates to check for compilation errors
125+
2. Test deployment in a development environment
126+
3. Verify the application can authenticate using managed identity
127+
4. Confirm no connection string references remain in the template
128+
129+
## Common Issues and Solutions
130+
131+
### Issue: "Local authentication methods are not allowed"
132+
**Solution:** Ensure `disableLocalAuth: true` is set and no duplicate Cosmos DB deployments exist.
133+
134+
### Issue: "Scope 'subscription' is not valid for this resource type"
135+
**Solution:** Use a module approach instead of standalone role assignment resources.
136+
137+
### Issue: "SQL Role Definition name must be a GUID"
138+
**Solution:** Use built-in role GUIDs (`00000000-0000-0000-0000-000000000002`) instead of custom names.
139+
140+
### Issue: Circular dependency errors
141+
**Solution:** Separate role assignment into its own module to break dependency cycles.
142+
143+
## Final Checklist
144+
145+
- [ ] Removed all connection string configuration parameters
146+
- [ ] Removed secretsExportConfiguration from Cosmos DB module
147+
- [ ] Added `disableLocalAuth: true` to Cosmos DB account
148+
- [ ] Created separate role assignment module using built-in role GUID
149+
- [ ] Removed connection string references from app settings
150+
- [ ] Verified no duplicate Cosmos DB module deployments exist
151+
- [ ] Ensured managed identity and Key Vault access are preserved
152+
- [ ] Tested template compilation and deployment
153+
154+
## Application Code Requirements
155+
156+
Your application code should already be using managed identity:
157+
158+
```csharp
159+
var credential = new DefaultAzureCredential();
160+
var cosmosClient = new CosmosClient(builder.Configuration["AZURE_COSMOS_ENDPOINT"], credential, ...);
161+
```
162+
163+
This approach uses the `AZURE_COSMOS_ENDPOINT` environment variable and managed identity instead of connection strings.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,11 @@ The Azure Developer CLI includes many other commands to help with your Azure dev
104104

105105
### Roles
106106

107-
This template creates a [managed identity](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview) for your app inside your Azure Active Directory tenant, and it is used to authenticate your app with Azure and other services that support Azure AD authentication like Key Vault via access policies. You will see principalId referenced in the infrastructure as code files, that refers to the id of the currently logged in Azure Developer CLI user, which will be granted access policies and permissions to run the application locally. To view your managed identity in the Azure Portal, follow these [steps](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-view-managed-identity-service-principal-portal).
107+
This template creates a [managed identity](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview) for your app inside your Azure Active Directory tenant, and it is used to authenticate your app with Azure and other services that support Azure AD authentication like Cosmos DB and Key Vault via access policies and role assignments. You will see principalId referenced in the infrastructure as code files, that refers to the id of the currently logged in Azure Developer CLI user, which will be granted access policies and permissions to run the application locally. To view your managed identity in the Azure Portal, follow these [steps](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-view-managed-identity-service-principal-portal).
108108

109109
### Key Vault
110110

111-
This template uses [Azure Key Vault](https://docs.microsoft.com/azure/key-vault/general/overview) to securely store your Cosmos DB connection string for the provisioned Cosmos DB account. Key Vault is a cloud service for securely storing and accessing secrets (API keys, passwords, certificates, cryptographic keys) and makes it simple to give other Azure services access to them. As you continue developing your solution, you may add as many secrets to your Key Vault as you require.
111+
This template uses [Azure Key Vault](https://docs.microsoft.com/azure/key-vault/general/overview) to securely store secrets such as Application Insights connection strings. Key Vault is a cloud service for securely storing and accessing secrets (API keys, passwords, certificates, cryptographic keys) and makes it simple to give other Azure services access to them. The application uses managed identity to authenticate with Cosmos DB directly, eliminating the need to store database connection strings. As you continue developing your solution, you may add as many secrets to your Key Vault as you require.
112112

113113
## Reporting Issues and Feedback
114114

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
param cosmosAccountName string
2+
param apiPrincipalId string
3+
4+
// Create a role assignment for the API's managed identity to access Cosmos DB
5+
// Using the built-in "Cosmos DB Built-in Data Contributor" role
6+
resource apiCosmosRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2024-05-15' = {
7+
name: '${cosmosAccountName}/${guid(apiPrincipalId, cosmosAccountName, '00000000-0000-0000-0000-000000000002')}'
8+
properties: {
9+
roleDefinitionId: '${resourceGroup().id}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosAccountName}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002'
10+
principalId: apiPrincipalId
11+
scope: '${resourceGroup().id}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosAccountName}'
12+
}
13+
}

infra/app/db-avm.bicep

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
param accountName string
22
param location string = resourceGroup().location
33
param tags object = {}
4-
param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING'
54
param databaseName string = ''
6-
param keyVaultResourceId string
75
param principalId string = ''
86

97
@allowed([
@@ -23,17 +21,15 @@ module cosmos 'br/public:avm/res/document-db/database-account:0.6.0' = {
2321
location: location
2422
tags: tags
2523
backupPolicyType: backupPolicyType
24+
disableLocalAuth: true
2625
locations: [
2726
{
2827
failoverPriority: 0
2928
locationName: location
3029
isZoneRedundant: false
3130
}
3231
]
33-
secretsExportConfiguration:{
34-
keyVaultResourceId: keyVaultResourceId
35-
primaryWriteConnectionStringSecretName: connectionStringKey
36-
}
32+
3733
capabilitiesToAdd: [ 'EnableServerless' ]
3834
automaticFailover: false
3935
sqlDatabases: [
@@ -52,15 +48,9 @@ module cosmos 'br/public:avm/res/document-db/database-account:0.6.0' = {
5248
}
5349
]
5450
sqlRoleAssignmentsPrincipalIds: [ principalId ]
55-
sqlRoleDefinitions: [
56-
{
57-
name: 'writer'
58-
}
59-
]
6051
}
6152
}
6253

6354
output accountName string = cosmos.outputs.name
64-
output connectionStringKey string = connectionStringKey
6555
output databaseName string = actualDatabaseName
6656
output endpoint string = cosmos.outputs.endpoint

infra/main.bicep

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ module api './app/api-appservice-avm.bicep' = {
7474
}
7575
appSettings: {
7676
AZURE_KEY_VAULT_ENDPOINT: keyVault.outputs.uri
77-
AZURE_COSMOS_CONNECTION_STRING_KEY: cosmos.outputs.connectionStringKey
7877
AZURE_COSMOS_DATABASE_NAME: cosmos.outputs.databaseName
7978
AZURE_COSMOS_ENDPOINT: cosmos.outputs.endpoint
8079
API_ALLOW_ORIGINS: web.outputs.SERVICE_WEB_URI
@@ -121,28 +120,22 @@ module cosmos './app/db-avm.bicep' = {
121120
accountName: !empty(cosmosAccountName) ? cosmosAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}'
122121
location: location
123122
tags: tags
124-
keyVaultResourceId: keyVault.outputs.resourceId
125123
principalId: principalId
126124
backupPolicyType: 'Periodic'
127125
}
128126
}
129127

130-
// Give the API the role to access Cosmos
131-
module apiCosmosSqlRoleAssign 'br/public:avm/res/document-db/database-account:0.5.5' = {
132-
name: 'api-cosmos-access'
128+
// Give the API access to Cosmos using a separate role assignment
129+
module apiCosmosRoleAssignment './app/cosmos-role-assignment.bicep' = {
130+
name: 'api-cosmos-role'
133131
scope: rg
134132
params: {
135-
name: cosmos.outputs.accountName
136-
location: location
137-
sqlRoleAssignmentsPrincipalIds: [ api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID ]
138-
sqlRoleDefinitions: [
139-
{
140-
name: 'writer'
141-
}
142-
]
133+
cosmosAccountName: cosmos.outputs.accountName
134+
apiPrincipalId: api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID
143135
}
144136
}
145137

138+
146139
// Create an App Service Plan to group applications under the same payment plan and SKU
147140
module appServicePlan 'br/public:avm/res/web/serverfarm:0.1.1' = {
148141
name: 'appserviceplan'
@@ -237,7 +230,6 @@ module apimApi 'br/public:avm/ptn/azd/apim-api:0.1.0' = if (useAPIM) {
237230

238231
// Data outputs
239232
output AZURE_COSMOS_ENDPOINT string = cosmos.outputs.endpoint
240-
output AZURE_COSMOS_CONNECTION_STRING_KEY string = cosmos.outputs.connectionStringKey
241233
output AZURE_COSMOS_DATABASE_NAME string = cosmos.outputs.databaseName
242234

243235
// App outputs

0 commit comments

Comments
 (0)