Namely, how to create role assignment that can be used for different principals (but for the same scope/role)?
All examples that I found (for example this answer) use role definition ID for a role assignment name which causes clashes if I want to create another assignment for the same scope and role but for a different principal.
The examples of ARM templates used for this, clearly have this separation:
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[concat(parameters('StorageAccountName'), '/default/',parameters('ContainerName'), '/Microsoft.Authorization/', parameters('roleNameGuid'))]",
"properties": {
"roleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
"principalId": "[parameters('principalId2')]"
}
}
Here name
and principalId
are clearly different and you can just use a different name for the different principal.
However in C# SDK, the role definition is passed as a roleAssignmentName
:
var roleAssignmentResource = armClient.GetRoleAssignmentResource(
RoleAssignmentResource.CreateResourceIdentifier(
scope: $"/subscriptions/{subscriptionId.ToString()}/resourceGroups/{resourceGroupName}",
roleAssignmentName: "acdd72a7-3385-48ef-bd42-f606fba81ae7")); // this is the ID for the Reader role
Why does C# SDK combine them? How do I decouple them to be able to create multiple assignments for different principals?
Note that, role assignment name needs to be unique within a scope. If you use roleAssignmentName as role definition Id, it will create a clash when you are assigning role to multiple service principals under same scope.
Alternatively, you can generate unique roleAssignmentName values by using Guid.NewGuid().ToString()
to avoid conflicts.
In my case, I used below modified code to create role assignments for different principals under same scope:
using Azure;
using Azure.Core;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.Authorization;
using Azure.ResourceManager.Authorization.Models;
class Program
{
static async Task Main(string[] args)
{
TokenCredential cred = new DefaultAzureCredential();
ArmClient client = new ArmClient(cred);
string subscriptionId = "subId";
string resourceGroupName = "rgname";
string scope = $"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}";
// Define the role definition (Reader role in this case)
string roleDefinitionId = "/subscriptions/subId/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7";
string[] principalIds = new string[]
{
"userId01",
"userId02"
};
foreach (var principalId in principalIds)
{
// Generate a GUID for the role assignment ID (it should be GUID based, not based on principalId)
string roleAssignmentName = Guid.NewGuid().ToString(); // Use GUID directly as role assignment ID
ResourceIdentifier roleAssignmentResourceId = RoleAssignmentResource.CreateResourceIdentifier(scope, roleAssignmentName);
RoleAssignmentResource roleAssignment = client.GetRoleAssignmentResource(roleAssignmentResourceId);
RoleAssignmentCreateOrUpdateContent content = new RoleAssignmentCreateOrUpdateContent(
new ResourceIdentifier(roleDefinitionId), Guid.Parse(principalId))
{
PrincipalType = RoleManagementPrincipalType.User // Set to ServicePrincipal if assigning to a service principal
};
// Create or update the role assignment
ArmOperation<RoleAssignmentResource> lro = await roleAssignment.UpdateAsync(WaitUntil.Completed, content);
RoleAssignmentResource result = lro.Value;
RoleAssignmentData resourceData = result.Data;
// Output the result
Console.WriteLine("Role assignment created:");
Console.WriteLine($" Principal ID: {principalId}");
Console.WriteLine($" Role Definition ID: {roleDefinitionId}");
Console.WriteLine($" Assignment ID: {resourceData.Id}");
Console.WriteLine(new string('-', 50)); // Separator line
}
}
}
Response:
To confirm that, I checked the same in Portal where role assigned successfully to users under same scope: