The way we use Azure Managed Applications is to offer an end-to-end solution to the customer. We must be able to hide the intellectual property, while offering a nice solution to the customer.

Resources within the managed application resource group are protected using deny assignments. Anything other than read is denied for all principles, except the managed application identity itself and the principles we included during the setup of the managed application.

In the managed application we have for example:

  • App service or azure functions configured with system assigned identity, these app services should access the storage account, included in the same application, with Write permissions and using the system assigned identity
  • Storage account

As it is now, the app service is not able to get any write permissions due to the deny assignment, since the newly created system assigned identity is not in the excluded principle list.

The only way I have found is by using the customer allowed actions to enable Write permissions on the storage account.

However, this beats the point of providing a "black box" to the customer, since he now has access to the storage account directly instead of using for example the API we are offering.

The only way we can counter this for the moment is using storage keys instead of system assigned identity within the managed application, but this not a wanted workaround.

Can the principes that are created during the setup of the managed application, also be automatically included in the excluded list of deny assignments? And by doing so, will the principle of a black box still be valid?

Any other solution that can help us?

Thanks!

2

There are 2 answers

0
DrInvincibilis On

I can only confirmed that this case is open with Azure support however so far the issue is unresolved. The Deny assignment in the managed resource group states clearly that write operations are only allowed for Admin principal - the rest is hard blocked.

For the moment I would consider RBAC in the managed applications as unsupported scenario. In our project we resorted to connection strings etc.

2
NickSpag On

The process that works for us:

  • Create a User-Assigned Managed Identity as part of your solution.
  • Assign it the owner role on the managed resource group (see below).
  • Assign your app services and resources that same identity.

The tricky part is assigning the initial role. Role assignment inherently looks in the publisher tenant for the object that will be assigned the role. This is why in this context the tenant().tenantId bicep func returns the publisher tenant, and subscription().tenantId returns the customer tenant (it's what the deployment is scoped to).

To tell the deployment to look in the customer tenant, where the user-assigned identity lives, you have to use the delegatedManagedIdentityResourceId property. However you cannot use that property when deploying to your own tenant (say, for testing purposes). So it's best to determine if you're deploying on your tenant or you're in a cross tenant scenario, and use a conditional.

Here is a bicep example:

//however you determine this is fine, but this is an easy way to know if it's being deployed to a different tenant
var crossTenant = tenant().tenantId != subscription().tenantId 
var managedIdentityName = 'whatever-identity-name'

@description('A user assigned managed identity to own the managed resource group and be used by resources within it')
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
  name: 'whatever-identity-name'
  location: 'eastus'
}

@description('This is the built-in Owner role. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#owner')
resource ownerRoleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
  name: '8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
}

resource ownerRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' =  {
  name: guid(subscription().id, managedIdentityName, ownerRoleDefinition.id)
  scope: resourceGroup()
  properties: {
    principalId: managedIdentity.properties.principalId
    roleDefinitionId: ownerRoleDefinition.id
    principalType: 'ServicePrincipal'
    delegatedManagedIdentityResourceId: crossTenant ? managedIdentity.id : null
  }
}