question

SiegfriedHeintze-9929 avatar image
0 Votes"
SiegfriedHeintze-9929 asked SiegfriedHeintze-9929 edited

Wanted: Sample Bicep Script to grant App Service Web App System Service Principal access to Cosmos DB



Presently I'm storing the end point and account key in environment variables for my C# web app running in the Azure App Service.

(1) How do I write a bicep script to grant my App Service's web app system service principal access to my cosmos db partitions?
(2) Assuming I have the above bicep script working, how would my C# connect to the cosmos db? Would I use a connection string instead of the enpoint/accountkeys?
(3) Would I need to store the connection string in the key vault? If so, would I still need to grant my service principal access to my cosmos dB?

Thanks
Siegfried

Monday Evening Update:
I found this blog called partly cloudy that has an associated github project. It looks like he recreates the database each time he deploys via bicep. This sounds like a ridiculous constraint because it is common to have many projects sharing a database... Is it possible to use bicep to to configure access to an existing database? Or, more specifically: can I figure a new service principal (for a webapp or kubernetes cluster) to have access to an existing database?

2022 April 13 Evening Update:
Ah hah! I can answer my question above. I think I can use the "existing" key word in the bicep language. I have not tried it yet, however.

My application needs to be granted read/write access to the cosmos database and read access to the key vault secret.

As explained my reply below, previously I was creating a managed entity and passing this into the bicep script as suggested by the partly cloudy blog (see link above). This managed entity was also a user assigned identity for the App Service Web app. I am granting this same managed identity access to the key vault. However, it is failing to fetch the secret from the key vault when running as a web app in Azure.

Instead of creating a RBAC for this managed identity, I would like to instead create RBAC for the system assigned identity of the Azure App Service Web App. I am creating the web app in the same bicep script... However, I get this error when defining the role definition:

Error BCP120: This expression is being used in an assignment to the "name" property of the "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions" type, which requires a value that can be calculated at the start of the deployment. You are referencing a variable which cannot be calculated at the start ("roleDefId" -> "web"). Properties of web which can be calculated at the start include "apiVersion", "id", "name", "type".


So how do I allow a web app system service principal to have read/write access to a cosmos database?

2022 Apr 16 Sat Morning Update:

Previously I was only granting the user assigned managed identity for the App Service Web app (the principalId in your bicep script as created in the with the powershell command in the Partly Cloudy blog in the above link) access to the key vault (to fetch the AAD client secret) and my blazor server app would die immediately after deployment to azure. Now I am granting both the system assigned and the user assigned managed identity for the web app access to the key vault and I can now log in when deployed to azure. Hurray! This makes me think that assigning the user assigned managed identity to the web app has no affect! I tried to apply the RBAC to the system assigned identity but bicep complains that this is not possible because it is a computed value. So now I can deploy to azure app service and log in but I still cannot access the cosmos db.

Also: I finally started to type in your code but discovered it was identical (almost) to the RBAC bicep code in the partly cloudy blog. The only differences were syntactic.

Lastly: when using your (& CDennig's) bicep code originally, I was not assigning the RBAC to my azure account... only to the managed identity. I am now creating role definitions and role assignments assignments for both the managed identity and my personal azure account and when running my blazor app on my development machine I can now log in and write to the cosmos database... So we now know that the RBAC code is working...

Please help me resolve these mysteries:

(1) How do I see these RBAC role assignments in the azure portal? How do I use the portal to manually add a custom role assignment to a managed identity?

(2) Why is azure ignoring the user assigned managed identity assigned the web app?

(3) How do I create RBAC role assignments for the system assigned identity for the azure web app?

2022 Apr 17 Evening update:

Using powershell commands I was able to grant the RBAC to the system assigned service principal for the App Service Web App. I could not see these roles or role assignments in the portal (why not?) but they worked. So user assigned service principals for App Service Web Apps are not working for key vault access or RBAC for cosmos! Is this bug? Please help me fix my bicep script to add cosmos RBAC to my system assigned service principal.

2022 May 5 Thu Update:

I have created an azure support incident for this issue and I have noticed I don't have an obvious link to my code:
I forked from Azure Active Directory B2C Blazer sample. Here is the source code: If you extract the script from the embedded comments in deploy.bicep they will run... However, I had to hard code the random name generated by bicep and after deploying via bicep I have to (see the comments) use powershell to grant access to the cosmos db.

Ryan has been helping me with a different approach but we are having trouble with the LinuxFxVersion.

I have lots of questions such has why does not CDennig's approach (see link above) of granting a user assigned service principal access to the cosmos db work? What is the difference between Ryan's access rights and CDennig's access rights?

Thanks



azure-webappsazure-cosmos-dbazure-key-vault
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

OuryBa-MSFT avatar image
0 Votes"
OuryBa-MSFT answered ryanchill edited

Hi @SiegfriedHeintze-9929 Thank you for your query

I want to make sure I understand completely but your end goal is to store the connection string in the key vault and need to grant your service principal access to my cosmos db.
Your prior question was about how to write a bicep script to grant my App Service's web app system service principal access to my cosmos db partitions. Please let me know if my understanding is not correct.

(1) Please also confirm which cosmos db API you are using (this is for SQL API). You can also find for other APIs in this Documentation


(2) In my understanding for question 2 the ask is how you would connect the bicep script to your cosmos db. Please let me know if that is not the ask.
There are many ways to deploy Azure Bicep files including, [Azure CLI][3], Azure PowerShell and Cloud Shell.

It’s advisable to store connection strings in key vault but it’s not necessary. But it is best to grant access to cosmos or any other service for that matter via service principal.

@description('Application Name')
@maxLength(30)
param applicationName string = 'todo-app-${uniqueString(resourceGroup().id)}'
@description('Location for all resources.')
param location string = resourceGroup().location
@allowed([
'F1'
'D1'
'B1'
'B2'
'B3'
'S1'
'S2'
'S3'
'P1'
'P2'
'P3'
'P4'
])
@description('App Service Plan\'s pricing tier. Details at https://azure.microsoft.com/en-us/pricing/details/app-service/')
param appServicePlanTier string = 'F1'
@minValue(1)
@maxValue(3)
@description('App Service Plan\'s instance count')
param appServicePlanInstances int = 1
@description('The URL for the GitHub repository that contains the project to deploy.')
param repositoryUrl string = 'https://github.com/Azure-Samples/cosmos-dotnet-core-todo-app.git'
@description('The branch of the GitHub repository to use.')
param branch string = 'main'
@description('The Cosmos DB database name.')
param databaseName string = 'Tasks'
@description('The Cosmos DB container name.')
param containerName string = 'Items'
var cosmosAccountName = toLower(applicationName)
var websiteName = applicationName
var hostingPlanName = applicationName
var keyvaultName = applicationName
// Use built-in roles https://docs.microsoft.com/en-us/azure/key-vault/general/rbac-guide?tabs=azure-cli#azure-built-in-roles-for-key-vault-data-plane-operations
var keyVaultSecretsUserRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2021-04-15' = {
name: cosmosAccountName
kind: 'GlobalDocumentDB'
location: location
properties: {
consistencyPolicy: {
defaultConsistencyLevel: 'Session'
}
locations: [
{
locationName: location
failoverPriority: 0
isZoneRedundant: false
}
]
databaseAccountOfferType: 'Standard'
}
}
resource kv 'Microsoft.KeyVault/vaults@2019-09-01' = {
// Make sure the Key Vault name begins with a letter.
name: keyvaultName
location: location
properties: {
sku: {
family: 'A'
name: 'standard'
}
tenantId: subscription().tenantId
enableRbacAuthorization: true
enabledForDeployment: false
enabledForDiskEncryption: true
enabledForTemplateDeployment: false
}
resource cosmosDbAccountSecret 'secrets' = {
name: 'CosmosDbAccount'
properties: {
value: cosmosAccount.properties.documentEndpoint
}
}
resource cosmostDbKeySecret 'secrets' = {
name: 'CosmostDbKey'
properties: {
value: cosmosAccount.listKeys().primaryMasterKey
}
}
}
resource hostingPlan 'Microsoft.Web/serverfarms@2020-06-01' = {
name: hostingPlanName
location: location
sku: {
name: appServicePlanTier
capacity: appServicePlanInstances
}
}
resource website 'Microsoft.Web/sites@2020-06-01' = {
name: websiteName
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
serverFarmId: hostingPlan.id
siteConfig: {
appSettings: [
{
name: 'CosmosDb:Account'
value: '@Microsoft.KeyVault(VaultName=${kv.name};SecretName=${kv::cosmosDbAccountSecret.name})'
}
{
name: 'CosmosDb:Key'
value: '@Microsoft.KeyVault(VaultName=${kv.name};SecretName=${kv::cosmostDbKeySecret.name})'
}
{
name: 'CosmosDb:DatabaseName'
value: databaseName
}
{
name: 'CosmosDb:ContainerName'
value: containerName
}
]
}
}
}
resource kvWebsitePermissions 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = {
name: guid(kv.id, website.name, keyVaultSecretsUserRole)
scope: kv
properties: {
principalId: website.identity.principalId
principalType: 'ServicePrincipal'
roleDefinitionId: keyVaultSecretsUserRole
}
}
resource srcControls 'Microsoft.Web/sites/sourcecontrols@2020-06-01' = {
name: '${website.name}/web'
properties: {
repoUrl: repositoryUrl
branch: branch
isManualIntegration: true
}
}

Please feel free to reach if you have any further queries or have any doubt regarding the above questions.

Regards,
Oury


· 9
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi @SiegfriedHeintze-9929 Checking if the above was helpful Please let us know if you are still block so we can further assist you.

Regards,
Oury

0 Votes 0 ·

OuryBa:

Thanks! I'm still (eagerly) studying your response.
However: I cannot copy and paste your code.... It looks like you pasted a screen capture bit map and I will have to type it into VSCode.... Please post the text so I can paste it!

With regard to Question #1: I am running the SQL (Document) API ad demonstrated in Jeremy's cosmos db /Entity Framework example: PlanetaryDocs.

Regarding connection strings: I don't know if I need a connection string. See question #2.

So I'm looking at your script parameters... The accountname has a default value... this leads me to believe it is creating a new cosmosDB? I want configure access to my existing cosmos DB. Is this possible?


Thanks!
Siegfried


[2]: https://github.com/JeremyLikness/PlanetaryDocs/blob/main/PlanetaryDocsLoader/CosmosLoader.cs#L45

0 Votes 0 ·

Question #2: I presently have the C# code to accept account key and endpoint as exemplified here: CosmosLoader.cs The problem with this approach is that I don't want to hard code the account key in the source code or even the appconfig.json! How would I modify this code to securely connect to the cosmos db assuming I'm using your above bicep script and running inside (or under?) a service principal (or my personal azure account) that has been granted access to my cosmosdb? Is it possible to securely connect to the cosmosdb without storing an account key or connection string in a key vault? (AND without storing this stuff in environment variables)? I'm looking at my cosmosdb on portal.azure.com and I see there keys and I don't think I want to use these unless they are stored i na key vault because they contain account keys.


0 Votes 0 ·
OuryBa-MSFT avatar image OuryBa-MSFT SiegfriedHeintze-9929 ·

Thank you @SiegfriedHeintze-9929 for clarifying this. I need to repo this issue again from my end and get back to you.

Thank you for your patience

Regards,
Oury

0 Votes 0 ·
Show more comments

Oury:

See my questions below including your choice to use appsettings in the web app instead of settings in the app config. I'm worried that that appsettings in the web will have no affect when I'm running locally on my dev machine (in contrast to appcnfig).

2022 April 26 Afternoon Update:
My question states that I want to grant my web app access to the cosmos db... I see where you grant access to the key vault! Where do you grant me access to the cosmos db? I apologize if you got confused by my connection string question... I asked about using a connection string because I wanted to demonstrate multiple techniques for accessing the cosmos database. Best practices recommend no connection strings... Please look at the partly cloudy blog in the above link... CDennig (the author) grants his Kubernetes (K8) pod access to cosmos with no connection string... I want to do the same with a app service web instead of a K8. In fact, I have succeed in this but not with bicep!. I have two problems I need help with:

  1. When I grant RBAC access to the web's user assigned managed identity, azure ignores it. Why?

  2. When I try to grant access to the web's system assigned managed identity, bicep says no I cannot do that because this is a computed value.

When I grant access to the system assigned managed identity with powershell, it works! Can you help me do this with bicep instead of powershell?

Thanks
Siegfried






0 Votes 0 ·
ryanchill avatar image ryanchill SiegfriedHeintze-9929 ·

Granting your app service RBAC permissions to Cosmos should be

resource websiteDocumentDbPermissions 'Microsoft.Authorization/roleAssignments@2015-07-01' = {
  name: guid(cosmosAccount.id, website.name, documentDbContributorRole)
  scope: cosmosAccount
  properties: {
    principalId: website.identity.principalId
    roleDefinitionId: documentDbContributorRole
  }
}


Where var documentDbContributorRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') according to https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#documentdb-account-contributor. That should yield the same results as https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-setup-managed-identity. If that doesn't work, let us know. By the way, I've updated the bicep.


0 Votes 0 ·
mainbicep.txt (6.6 KiB)

Oops... Missed some of your questions... Ohh Wow! you posted that on Apr 5 and I did not see it... Shame on me..

(1) I'm using the SQL API...

(2) Yes I would like to connect to cosmos DB SQL api.. As explained elsewhere, I want to demonstrate the common approach of using a connection string (this is no problem) and the Recommended (Microsoft best practices) approach where we grant a service principal access to the cosmos DB (similar to the way we grant access to the key vault with out using passwords).

0 Votes 0 ·
ryanchill avatar image
0 Votes"
ryanchill answered SiegfriedHeintze-9929 commented

Hi @SiegfriedHeintze-9929 hope all is well. Oury and I were able to determine the following main.bicep that should help shed some light. This file will create an app service from a GitHub repo (connected through Deployment Center), a key vault, and cosmos db. The app service app settings will pull the cosmos db connection from Key Vault. The Key Vault itself is configured to use RBAC role which is used for the permission on behalf of the app service managed service identity.

We were working on the other approach which would be to use access policies but having difficulties getting around a cyclic reference between key vault and the app service.


· 8
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I'm glad you are doing Github as that was my plan.

I am comparing the azure AppConfig object in deploy.bicep that works nicely with the ASP.NET IConfigBuilder and your most recent main.bicep that uses the appsettings feature in the website.

  1. Why the switch from appconfig to the appsettings in the website?

  2. We don't store secrets in the app config because it is not secure. Are secrets secure in the appsettings?

  3. Can I access the appsettings of the webapp using the same IConfigBuilder Class and syntax that I use for fetching values from the azure appconfig object?

  4. I took a look at main.bicep and was surprised to not see any role definitions or role assignments as seen in Oury's early response.

FYI: I am also planning on incorporating a VNET to protect the cosmosDB: how-to-configure-vnet-to-protect-cosmos-db-but-all.html



1 Vote 1 ·
OuryBa-MSFT avatar image OuryBa-MSFT SiegfriedHeintze-9929 ·

@ SiegfriedHeintze-9929 Please let us know the status of this issue. Do you need any further assistance?
If the reply was helpful, please do mark as accept answer.

Regards,
Oury

0 Votes 0 ·

Oury:
Please see my April 26 Afternoon update.... It appears you misunderstood my question. I early await your response!
Thanks
Siegfried

0 Votes 0 ·
ryanchill avatar image ryanchill SiegfriedHeintze-9929 ·

I appreciate the patience here @SiegfriedHeintze-9929. Oury and I came up with that solution centered around this question. To address you first question, I have adjusted the main.bicep that will:

  • Create an Azure Cosmos DB

  • Create a Key Vault that will store the URI and account key of the Cosmos DB as secrets in addition to a database name and container

  • Add an access policy granting get permissions for a web app managed identity

  • Create an App Configuration service that will references the key vault secrets containing the Azure Comos DB settings

  • Create the aforementioned web app and app service plan and add the app configuration endpoint as a connection string in addition to enabling logging and adding slot app settings for the fun of it.

One caveat, I'm getting a LinuxFxVersion invalid message passing DOTNETCORE|6.0 that I haven't resolved yet.


0 Votes 0 ·
mainbicep.txt (5.3 KiB)

As for your other questions:

2) No, secrets are not secure in appsettings. Anyone with read/contributor RBAC permissions to the app service can read these values. Storing in Key Vault gives you far more granular control
3) For fetching values from the App Config, leverage the Microsoft.Azure.AppConfguration.AspNetCore (or similar) nuget package. See Program.cs.
4) You can do either or. This commit shows how you can leverage built-in roles. What we're doing here though is the adding the web app managed identity to the key vault secret built-in role (towards the bottom).


0 Votes 0 ·

Ryan: Could you take a quick look at this: how-to-configure-vnet-to-protect-cosmos-db-but-all.html. I thought you might know something about it because you made a comment in Feb 2019 in stack overflow. I tried to implement that stack overflow solution but I cannot make it work. Maybe AnuragSharma-MSFT is going to make it into a support incident?

Ouray & Ryan:
Thanks... I'm trying out your solution now... I'm going to copy that nice bicep code you gave me previously so it will work with that sample AADB2C ASP.NET blazorApp that authenticates.

Is there any reason you are using the S1 plan instead of the F1 plan for the AppService...? Also: please elaborate on LinuxFxVersion. A quick bing search is not revealing what this is or why we want it. Is it a linux distro? table.php

Thanks
Siegfried


0 Votes 0 ·
ryanchill avatar image ryanchill SiegfriedHeintze-9929 ·

LinuxFxVersion defines what runtime the app service is intended to run. As for S1, it's a standard plan that supports slots. Lower SKUs don't support that, and I used this template to test how slots can be configured in bicep.


0 Votes 0 ·

Any progress on this linuxFxVersion? I'm stuck on it too.

0 Votes 0 ·