Open darrenFrowen opened 2 weeks ago
[!IMPORTANT] The "Needs: Triage :mag:" label must be removed once the triage process is complete!
[!TIP] For additional guidance on how to triage this issue/PR, see the BRM Issue Triage documentation.
@darrenFrowen, thanks for submitting this issue for the avm/ptn/authorization/role-assignment
module!
[!IMPORTANT] A member of the @Azure/avm-ptn-authorization-roleassignment-module-owners-bicep or @Azure/avm-ptn-authorization-roleassignment-module-contributors-bicep team will review it soon!
@darrenFrowen - Thanks for the feedback. When using this module you must deploy at the management group scope. Here is a link to a similar issue where we discuss the reasons why. https://github.com/Azure/bicep-registry-modules/issues/2519
Hi @arnoldna,
This is what is confusing me as I had a working RBAC assignment to a resource Group using this method using a non AVM resource module. Here you can see the main.bicep has target scope 'subscription' and the the mainModule.bicep has the default scope resourceGroup (no scope defined). This works fine using the following deployment method current subscription scope.
# Deploy the Bicep template
New-AzDeployment -name $deploymentName `
-TemplateParameterFile $paramFile `
-TemplateFile $templateFile `
-Location $location
Therefore i proceeded to raise an issue #2612 against the 'resource-role-assignment' module as effectively this is what i am doing albeit without the AVM module required parameter 'resourceId'. The scope in my main.bicep module takes care of the resourceId effectively. The #2612 issue was closed on me saying the following.
I see now your issue. You are trying to assign a contributor role to a resource group.
That won't work with this module.
As the description says: "This module deploys a Role Assignment for a specific resource."
Resource Group != Resource
To me all the options 'resource-role-assignment' and 'role-assignment' use the same resource template ''Microsoft.Authorization/roleAssignments@2022-04-01' so i believe its just matter of changing one of the templates to make the 'resourceId' optional = null parameter if not present?
resourceId: !empty(resourceId) ? resourceId: null
What are your thoughts on this please?
main.bicep
targetScope = 'subscription'
param hubPrefix string
param principalId string
@description('This is the existing Hub Virtual Network Resource Group')
resource hubVirtualNetworkServicesRg 'Microsoft.Resources/resourceGroups@2021-01-01' existing = {
name: 'rg-${hubPrefix}-network'
}
@description('This is the required vault Role assignment for Virtual Network Resource Group')
module backupVaultVnetRgRoleAssignment 'mainModule.bicep' = {
name: 'backupVaultVnetRgRoleAssignment'
scope: hubVirtualNetworkServicesRg
params: {
principalId: principalId
roleDefinitionId: 'Contributor'
scope: hubVirtualNetworkServicesRg.id
}
}
mainModule.bicep
@description('DEploy Role Assignment at the specified scope')
param principalId string
param roleDefinitionId string
param scope string
@description('Deploy Role Assignment at the specified scope')
resource resourceRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(scope, principalId, roleDefinitionId)
properties: {
principalId: principalId
roleDefinitionId: roleDefinitionId
}
}
Hey @darrenFrowen, let me chime in here and add my 5 cents since I know the module since the CARML days. There seems to be a fundamental misunderstanding/confusion regarding the a ARM/Bicep deployment's scope and what impact that has on your deployments.
First things first,
main.bicep
. That means, no matter what you do and what else it references it will always always need to be deployed from that scope. If you provide it with a subscriptionId, it just means that the module will internally deploy a nested template that runs at that scope to do your bidding. The requirements for this top-level main.bicep don't change though.Now a bit of history as to why this module is designed in the way it is: As said before, it has been around since the CARML days, and in CARML we're targeting an inner-sourcing scenario. What it's CI did was to take the modules (like the ones in AVM) and publish them and all their nested child-modules as their own modules. For example, you ended up with a KeyVault module, a key module, a secret module etc. For this module here that meant that 4 modules got published
Back to AVM we are currently not able to publish child/nested modules which is why only this very first top-level main.bicep
is published and to wrap the story up, why you also only find a key-vault module, but not 'key-vault.keys' or 'key-vault.secrets' module in the registry. It's something we want to somehow make possible, but it's not a quick fix.
In any case - given the described scope issue you cannot achieve what you want to achieve with the given role-assignment modules, unless you're willing to run the role-assignment module from an initial management-group scope. It's not the first time this confusion came up, and in full transparency I adviced against migrating this module for that very reason. It's child modules are useful - the parent, not really. It would instead have been better to split the module in multiple independent ptn modules that would only target their respective scope. So to unblock yourself, I highly recommend to do what you did in your last comment and run your own custom role assignment.
It's far from satisfying, but I hope what I wrote above at least makes sense to explain what's happening - or rather not happening.
Hi @AlexanderSehr ,
Thank you for the detailed and historical CARML information. Even though i had noticed the three modules scopes i hadn't noticed the scope in the top level main.bicep. So now understand why i was getting the error. I completely agree with you where you say."It would instead have been better to split the module in multiple independent ptn modules that would only target their respective scope".
Given what you have said and the fact that the role-assignment module cannot be changed. I am wondering if i either need to re-open #2612 as this is at the correct scope. However i can see that the resource-role-assignment is actually calling a roleAssignment ARM template module not sure why? Or request a new ptn module specifically for resource-group assignment. I would suspect this issue is going to repeat itself many times over.
For context the reason i needed the role assignment at the resource group level was the pre req required to configure private endpoint for a Recovery Services vault sub resource 'AzureBackup'. This requires the vault to have Contributor role on the Virtual Network Resource Group and the Private DNS Zone resource group. So anyone else doing this trying to use AVM will hit the same issue unless they have started at scope management group which i reckon is unlikely.
Please let me know your thoughts.
and the fact that the role-assignment module cannot be changed. I am wondering if i either need to re-open #2612 as this is at the correct scope. However i can see that the resource-role-assignment is actually calling a roleAssignment ARM template module not sure why? Or request a new ptn module specifically for resource-group assignment. I would suspect this issue is going to repeat itself many times over.
Hey @darrenFrowen, makes sense to me. I connected with @arnoldna in the background and we'll discuss what the best next steps would be (e.g., if it's worth creating 3 sepeate ptn modules, or if there is another alternative). Mind you, just 'splitting' the module in 3 is also not as straight forwad as it may seem. Technically it's easy - but - if the ptn ends up just deploying a role assignment to a scope, the question may be asked what the added value of that module is compared to a Bicep-native deployment (aside from saving you to create a file) 😄
Regarding re-opening #2612, I'd say - no. As I tried to explain in my original response, that module won't do you any good either as it was published in the resource group scope. This means its role assignments can only target resources inside a resource group, not the resource group itself (which would be at the subscription scope).
I can anyhow explain though why it uses an ARM template as it does indeed appear odd on first sight, but bare with me. In Bicep, if you want to assign a role to a resource you always need a direct reference to that resource. For example:
resource searchService 'Microsoft.Search/searchServices@2024-03-01-preview' = {
name: name
// (...)
properties: {
// (...)
}
}
resource searchService_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: 'TheCakeIsALie'
properties: {
// (...)
}
scope: searchService
}
or
resource searchService 'Microsoft.Search/searchServices@2024-03-01-preview' existing = {
name: name
}
resource searchService_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: 'TheCakeIsALie'
properties: {
// (...)
}
scope: searchService
}
What you can not do (and that's the kicker) is something like
resource searchService_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: 'TheCakeIsALie'
properties: {
// (...)
}
scope: '/subscriptions/(...)/resourceGroups/(...)/providers/Microsoft.Search/searchServices/mySearchService'
}
That is, to set the scope dynamically via a resource Id. Reason being: Bicep wants to help you set the correct values, but by doing so also isn't as dynamic as native ARM which relies primarily on primitive types like strings. This limitation leads to the impossiblity of a role assignment module that can be applied to every resource using native Bicep.
So what @peterbud applies in his module is a little trick. By using an ARM template for the actual role assignment, he can pass the 'scope' of a resource as a string (instead of a 'resource' reference like in the first 2 examples), and ARM will just accept that and do whatever magic in the background to make the role assignment happen: https://github.com/Azure/bicep-registry-modules/blob/021b0a706fb4af400ae024e01bc2f7aa4e5fae5e/avm/ptn/authorization/resource-role-assignment/modules/generic-role-assignment.json#L40
Small sidenote: We apply a similar trick in the StorageAccount file shares module to work around a resource provider bug: https://github.com/Azure/bicep-registry-modules/blob/021b0a706fb4af400ae024e01bc2f7aa4e5fae5e/avm/res/storage/storage-account/file-service/share/modules/nested_roleAssignment.bicep#L61
@AlexanderSehr Hi,
Yes i just tried to use the other module resource-role-assignment but failed to get it working locally and thank you again for the detailed explanation another one i never knew around Bicep limitations regarding the limitation of the scope compared to that of ARM.
So just to share with you what i have done. I copied the content from your role-assignment sub module scoped at the resource group unedited 'as-is' into a local folder and file 'modules/templates/role/main.bicep' . So here is a snipet from my main.bicep taking the vaultSystemAssignedMIPrincipalId as an output from my backup vault module ran it and worked absolutely fine. So if this could be used as a separate pattern module specifically for resource-group-role?
targetScope = 'subscription'
@description('This is Azure Recovery Services Vault and Vault Policy resources')
module sharedBackupVault './modules/01-sharedBackupVault.bicep' = {
name: 'SharedBackupVaultModule'
scope: sharedBackupVaultResourceGroup
params: {
// (...)
}
}
@description('This is the existing Hub Virtual Network Resource Group')
resource hubVirtualNetworkServicesRg 'Microsoft.Resources/resourceGroups@2021-01-01' existing = {
name: 'rg-${hubPrefix}-network'
}
@description('This is the built-in Contributor role. See https://docs.microsoft.com/azure/role-based-access-control/built-in-roles#contributor')
resource contributorRoleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
scope: subscription()
name: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
}
@description('This AVM role assignment "Works" for Contributor Role on the RG Scope')
module avmRoleAssignment 'modules/templates/role/main.bicep' = {
name: 'roleAssignmentDeployment'
scope: hubVirtualNetworkServicesRg
params: {
principalId: sharedBackupVault.outputs.vaultSystemAssignedMIPrincipalId
roleDefinitionIdOrName: contributorRoleDefinition.id
principalType: 'ServicePrincipal'
}
}
Check for previous/existing GitHub issues
Issue Type?
I'm not sure
Module Name
avm/ptn/authorization/role-assignment
(Optional) Module Version
0.1.1
Description
Hi,
I am trying to assign a service principle the Contributor Role to a resource group. My main.bicep is scoped to subscription as i am deploying multiple resource groups and resources. This is the main.bicep showing the targetScope and required parameters..
If in my main.bicep my module is set like this using as described below.
Parameter:
resourceGroupName
Name of the Resource Group to assign the RBAC role to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided RBAC role to the resource group.
''
I set the Resource Group Name and Subscription ID to assign the role to the resource group. Then i see the same error.
Can you please advise how this module shoould be formatted if my current target scope is allready at subscription scope? if i negate the resourceGroupName and subscriptionId and set the scope on the module i see this error instead.
(Optional) Correlation Id
No response