bicep icon indicating copy to clipboard operation
bicep copied to clipboard

Ability to generate the same GUID value the portal does for roleAssignments

Open slapointe opened this issue 3 years ago • 29 comments

It is very frustrating today because we cannot compute the same GUID value for the roleAssignment's name that the one created by ARM (Portal & PIM) for the same role assignment values. This makes porting back governance & permissions "as code" way more difficult. We need to delete the ones not created by ARM Templates/Bicep if we want to declare them "as code".

ARM gives me this error message if I try to declare the same roleAssignment in Bicep:

{"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.","details":[{"code":"Conflict","message":"{\r\n  \"error\": {\r\n    \"code\": \"RoleAssignmentExists\",\r\n    \"message\": \"The role assignment already exists.\"\r\n  }\r\n}"}]}}

I get that the name is not the same and the problem is there. I don't have any way to guess how to come up with the same GUID that is generated on the ARM side. Is there any way to dig this info from the platform?

  1. I'd like to, know the inputs and in which order, to pass to the guid() function to come up with the same value as ARM does. I guess this will be hard to reproduce, if even possible.

  2. If the above is not possible, could we expose a new function that would allow this?

We are close to idempotency here, it is only a matter of generating the same GUID value for the name property.

Not exactly the same ask as #5105 but closely related.

slapointe avatar Jan 21 '22 15:01 slapointe

I have reached out to the identity team to see if they publish any info on their GUID generation algorithm. If it is something that could be easily replicated in a bicep function, that could be an interesting way of dealing with this problem.

alex-frankel avatar Jan 31 '22 17:01 alex-frankel

I have reached out to the identity team to see if they publish any info on their GUID generation algorithm. If it is something that could be easily replicated in a bicep function, that could be an interesting way of dealing with this problem.

Any return from the team on this @alex-frankel ?

slapointe avatar Apr 06 '22 17:04 slapointe

This problem is frustrating me as well. There is always a point in time where some things were done manually, and you are trying to transition to a more automated and compliant approach. The suggestion to remove the role assignment and recreated it via the ARM template is no good because your production systems may depend on this role assignment to function.

mitchdenny avatar Jun 16 '22 07:06 mitchdenny

Joining this conversation because we face the same issue during automatic deployment through bicep files, where some role assignments were deployed manually beforehand. Or even if a bicep file was run local and afterwards in a pipeline.

How is this guid()-function generating its values? Could it also be dependent on the machine you run it?

I also do not quite understand why there is such a globally unique name required for a role assignment and this is then checked during deployment. Shouldn't it be like if the roleDefinitionId and the to be assigned principalId (with correct principalType) are matching, the role assignment is the same and therefore idempotent? Why is the name checked for idempotency if the defined attributes are the same?

@alex-frankel have you any news from the team?

clma91 avatar Aug 30 '22 09:08 clma91

How is this guid()-function generating its values? Could it also be dependent on the machine you run it?

It is not dependent on the machine you run it from. All template functions are processed server side, on Azure.

I also do not quite understand why there is such a globally unique name required for a role assignment and this is then checked during deployment. Shouldn't it be like if the roleDefinitionId and the to be assigned principalId (with correct principalType) are matching, the role assignment is the same and therefore idempotent? Why is the name checked for idempotency if the defined attributes are the same?

roleAssignments are treated like any other resources and need an ID for their CRUD operations at the REST API to work properly. The platform enforces there cannot be 2 identical tuple of principalId, roledefinitionId & scope. Thus the conflict error message we encounter between the manual assignment in the portal and any name provided in templates/Bicep.

We need to have the recipe on how to generate the same name as they do. A better approach IMO would be to have a new function that does just this. That way people cannot misplace the properties used and their order.

slapointe avatar Aug 30 '22 12:08 slapointe

It is not dependent on the machine you run it from. All template functions are processed server side, on Azure.

Thanks for explaining.

We need to have the recipe on how to generate the same name as they do. A better approach IMO would be to have a new function that does just this. That way people cannot misplace the properties used and their order.

I totally agree with you. It makes life just hard if these names are generated in a different way through the portal. It would be very useful indeed to have either a way of come to the same guid or as you suggest a function which would do this, so no one could do it wrong.

On the other hand, IMO there should be no need for us to provide a name for something like a role assignment in Bicep. A role assignment is defined throughout its properties which exist or have to be created by the Azure Resource Manager. No matter what the name is, because we do not have the control if someone created it through the portal or ARM/bicep.

clma91 avatar Aug 30 '22 12:08 clma91

Hi slapointe, this issue has been marked as stale because it was labeled as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. Thanks for contributing to bicep! :smile: :mechanical_arm:

ghost avatar May 24 '23 04:05 ghost

Can this please be re-opened for tracking purposes?

ckittel avatar Jun 09 '23 17:06 ckittel

Not sure how this could be marked as closed. What's the solution?

Geronius avatar Jun 09 '23 17:06 Geronius

It looks like it got auto-closed by a bot. Hopefully just a mistake.

ckittel avatar Jun 09 '23 17:06 ckittel

I've looked into this issue previously and found that for new role assignments created with Azure PowerShell and Azure CLI the assignments are done with non-deterministic uuid/guid() functions in .NET/Python respectively. Similar with terraform and role assignments there.

I would assume the portal does something similar, so it would be hard to "solve" this from the Bicep side in a declarative way unless the name (id) property can be omitted and the deployment engine could deduce the role assignment from a combiantion of principal / role / scope.

matsest avatar Jun 12 '23 14:06 matsest

This issue is a major blocker for using Bicep templates with role assignments. We need a workaround for this, so that Bicep deployments will not always fail due to role assignment conflicts.

dermot-cochran-ul avatar Jul 25 '23 07:07 dermot-cochran-ul

@dermot-cochran-ul can you help us understand why this is a "major blocker"? Why not delete the portal-generated role assignment and re-create it bicep using a deterministic method?

alex-frankel avatar Jul 25 '23 16:07 alex-frankel

I didn't add any portal-generated role assignments. It is all done by Bicep but I am still getting role conflicts.

Do I need to remove all portal-generated role assignments from the resource group, even if the scope is different?

Are there any issues with using role assignments scoped to a BLOB container instead of a Storage Account?

dermot-cochran-ul avatar Jul 25 '23 17:07 dermot-cochran-ul

It sounds like you may be running into a separate issue. This thread is specifically about how the portal generates role assignments. Do you mind opening a separate issue with the bicep code you are using and the error you are running into?

alex-frankel avatar Jul 25 '23 23:07 alex-frankel

Thanks, yes I will do that. Here it is: https://github.com/Azure/bicep/issues/11367

dermot-cochran-ul avatar Jul 26 '23 08:07 dermot-cochran-ul

Same problem here. Since our pipeline was not deploying roleassignments for a while, we had to retroactively patch some environments by deploying a lot of roleassignments using az cli. None of these environments can be managed using bicep now, because all the pipelines fail due to the "RoleAssignment already exists" error. Simply because the names differ, but the contents (assignee+role) are identical. We are in quite the pickle due to this issue. It would be really helpful if Microsoft could provide a guid() implementation for all common programming languages (we require python) so we can update all the affected names in-place using scripting.

Aggrobatics avatar Dec 01 '23 09:12 Aggrobatics

Just came across this issue too. When I'm writing bicep templates I want to test them locally using az deployment group create. Once I'm happy I then push my bicep files and deploy them via an Azure Pipeline. The pipeline deployment fails with RoleAssignmentExists, so I have to manually delete the role in the Azure Portal to get the Pipeline to pass. If I do that, then my local deployments will now fail, because RoleAssignmentExists.

While writing this it occurred to me that a workaround would be to use a different dev Azure subscription for running my local az deployment group create.

mattfrear avatar Dec 14 '23 01:12 mattfrear

Yes facing the same issue. Now need to abandon the bicep and resort to AZ CLI.

htwashere avatar Feb 27 '25 15:02 htwashere

@htwashere does azure-cli have the same random GUID values as the Azure Portal or ARM templates?

danmacode avatar Mar 10 '25 12:03 danmacode

@htwashere does azure-cli have the same random GUID values as the Azure Portal or ARM templates?

I ended up using Azure Powershell (or same as in AZ CLI), I check to see if assignment exists first before doing a new assignment. It's an inconvenience because now I have a powershell script that calls a combination of Biceps and powershell commands.

htwashere avatar Mar 10 '25 12:03 htwashere

I ended up using Azure Powershell (or same as in AZ CLI), I check to see if assignment exists first before doing a new assignment. It's an inconvenience because now I have a powershell script that calls a combination of Biceps and powershell commands.

I've done something similar, but I usually delete any existing roleAssignment before deploying a template that creates or updates one.

Once the Bicep templates are deployed, AZ CLI and Az PowerShell won't generate a new random GUID when updating the roleAssignment; instead, they'll keep the existing one.

It's inconvenient since you have to script around Bicep's GUID mismatch errors, but I haven't found a more reliable solution yet.

danmacode avatar Mar 11 '25 18:03 danmacode

The lack of idempotence when calling Microsoft.Authorization/roleAssignments guarantees failure when the role assignment exists but under a different name (most of the time it's a GUID). Any way we can pass in a flag (like idempotent: true) that checks if the role assignment already exists under a combination of scope, principal ID, and role definition ID?

jvivescortes avatar Sep 02 '25 16:09 jvivescortes

Per my understanding, it's not about idempotency. It's just the Azure Portal extension uses a different algorithm to come up with the name than your/my/everyone else's Bicep file. If you somehow "crack" it and manage to come up with the exact same name - it should work.

Another option might be to use the upcoming feature in Bicep @onlyIfNotExists() aka "check if resource already exists", see #4023.

abatishchev avatar Sep 02 '25 16:09 abatishchev

A quick solution I'll take for now is to output whether or not a role assignment already exists via deployment script. Pass in the output "roleAssignmentExists" to another module and then conditionally deploy the role assignment based on that parameter.

jvivescortes avatar Sep 02 '25 21:09 jvivescortes

Per my understanding, it's not about idempotency. It's just the Azure Portal extension uses a different algorithm to come up with the name than your/my/everyone else's Bicep file. If you somehow "crack" it and manage to come up with the exact same name - it should work.

Another option might be to use the upcoming feature in Bicep @onlyIfNotExists() aka "check if resource already exists", see #4023.

resource existence is based on resource id (which includes name). This would require you to know the name of the resource... And if you know the name of the resource, then you don't have this problem in the first place. unless they add custom implementation to check the existence of a role assignment, that does not use the name. then that would work :)

serbrech avatar Sep 04 '25 01:09 serbrech

Per my understanding, it's not about idempotency. It's just the Azure Portal extension uses a different algorithm to come up with the name than your/my/everyone else's Bicep file. If you somehow "crack" it and manage to come up with the exact same name - it should work.

Another option might be to use the upcoming feature in Bicep @onlyIfNotExists() aka "check if resource already exists", see #4023.

Worst case scenario: There is no algorithm: only randon guid generation Proof: Try to assign same principalId, scope, roleId triplet twice. They will have different GUIDs and second request will fail.

AFAIK at least Az Powershell has identical behaviour.

jikuja avatar Nov 07 '25 17:11 jikuja

Yes, you're right, it's a "real" guid, random every time. So there is no way one can replicate it in their IaC (whatever the technology is) :-(

abatishchev avatar Nov 07 '25 19:11 abatishchev

One of the best ways to make this issue easier for end users would be to include existing roleAssignment GUID into error message, maybe even full property triplet.

If user is deploying into environment that has limitation for RBAC assignment removal, displaying triplet and existing GUID on error message gives user enough information to change GUID on IaC without doing extra management API queries.


If other tooling ever gives an option to provide your own GUID here is what guid()method does:

From the documentation

The guid function implements the algorithm from RFC 4122 §4.3. The original source can be found in GuidUtility with some modifications. In the guid() function implementation, the namespaceId is set to 11fb06fb-712d-4ddd-98c7-e71bbd588830, and the version is set to 5. The value is generated by converting each parameter of the guid() function to a string and concatenating them with - as delimiters.

Python implementation(AI but tested).

#!/usr/bin/env python3
import argparse
import uuid

def main():
    parser = argparse.ArgumentParser(description="Generate a name-based UUID (v3 or v5).")
    parser.add_argument("-n", "--namespace", required=True, help="Namespace UUID (e.g., DNS: 6ba7b810-9dad-11d1-80b4-00c04fd430c8)")
    parser.add_argument("-a", "--algorithm", choices=["MD5", "SHA1"], default="MD5", help="Hash algorithm: MD5=v3, SHA1=v5")
    parser.add_argument("names", nargs="+", help="Name(s) to generate the UUID from")
    args = parser.parse_args()

    try:
        namespace_uuid = uuid.UUID(args.namespace)
    except ValueError:
        print("Invalid namespace UUID format.")
        return

    # Concatenate multiple names deterministically
    combined_name = "-".join(args.names)

    # Generate UUID using built-in methods
    if args.algorithm.upper() == "MD5":
        guid = uuid.uuid3(namespace_uuid, combined_name)
    else:
        guid = uuid.uuid5(namespace_uuid, combined_name)

    print(guid)

if __name__ == "__main__":
    main()

Compared with bicep console:

Image Image

Looks like Bicep includes that logic with some of the Azure.Deployments.* packages at leasr for bicep console maybe also for snapshots and local-deploy

jikuja avatar Nov 08 '25 10:11 jikuja