Microsoft.Authorization/roleAssignments DevOps CI / ARM Template Issue

Jamie Evans 16 Reputation points
2022-09-14T14:45:32.497+00:00

Hi,

I've been using DevOps and ARM templates for a long time. Until now, I have not had to use the Microsoft.Authorization/roleAssignments resource provider under a storageAccount/tableService/tables scope before.

I have managed to add some Storage Table Data Contributor role assignments to some Function App managed identities/service principals. I was frustrated that I was not able to use a copy loop due to the name property having to be a GUID, and the newGuid() function only being able to be used as the default value of a parameter. Nonetheless....

The resources are not nested within the storage account; they're in the main resources section of the template. The issue is that the resource provider, despite my deployment being incremental, does not respect the fact that the assignments already exist. The pipeline output is:

[error]RoleAssignmentExists: The role assignment already exists.

This causes the stage of the pipeline to fail after first run. From a CI/CD perspective this is not acceptable for my use case. I have fed static GUIDs in to the name properties of the resource hoping that the RP would recognise these are existing resources which do not need to be deployed or modified. But this was not the case. Using dynamically generated GUIDs at deployment time produces the same issue.

Is there a way around this issue?

Thanks for your help.

Jamie.

Azure Role-based access control
Azure Role-based access control
An Azure service that provides fine-grained access management for Azure resources, enabling you to grant users only the rights they need to perform their jobs.
673 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Jamie Evans 16 Reputation points
    2022-09-14T17:40:27.137+00:00

    Okay, I've actually found my own solution.

    First I deleted the role assignments so I could take a different approach. My approach was not to use the newGuid() function as the default value of x parameters in the template. This was causing a new GUID, and therefore a new "name" property value to be generated every time the pipeline was run, and therefore the role assignment was different each time it was compared, but the essential components - Principal, Role, and Scope were the same.

    My objective was to ensure idempotency of the GUID, and so I used the guid() function instead, passing unique arguments for each roleAssignment resource which would not change across runs of the pipeline.

    Here's a snippet:

    {  
                "type": "Microsoft.Authorization/roleAssignments",  
                "apiVersion": "2020-04-01-preview",  
                "name": "[guid(resourceGroup().name, parameters('myArrayOfFunctionAppNames')[0])]",  
                "scope": "[concat('Microsoft.Storage/storageAccounts/', parameters('storageAccName'), '/tableServices/default/tables/', parameters('tableName'))]",  
                "dependsOn": [  
                    "[parameters('storageAccName')]"  
                ],  
                "properties": {  
                    "roleDefinitionId": "[variables('builtInRoles')['storageAccTableDataContributor']]",  
                    "principalId": "[reference(resourceId(subscription().subscriptionId, parameters('myResourceGroupName'), 'Microsoft.Web/sites', parameters('myArrayOfFunctionAppNames')[0]), '2019-08-01', 'Full').identity.principalId]"  
                }  
            }  
    

    The above is grabbing the principalId of the Function App in question using the reference() function, and my Function App names are stored in a parameter of type array which is why I reference the index [0] in this case, but increment for other roleAssignment resources to ensure uniqueness of GUID but idempotence across pipeline runs.

    Et voila.

    3 people found this answer helpful.