Update (10/14/2021) – Custom RBAC Roles in Azure Management Groups is in Public Preview. Link here. It is advised to follow official guidance and use the below as expirimental only.
Azure Management Groups are containers that help you manage access, policy, and compliance across multiple subscriptions. Azure Management Groups provide a level of scope above subscriptions. You organize subscriptions into containers called “Management Groups” and apply your governance conditions to the management groups. All subscriptions within a management group automatically inherit the conditions applied to the Management Group.
Role Based Access Control (RBAC) helps you manage who has access to Azure resources, what they can do with those resources, and what areas they have access to. RBAC is an authorization system that provides fine-grained access management of Azure resources. A list of built-in RBAC roles are available here.
When built-in RBAC roles do not meet your needs, custom RBAC roles can be created which allows you to define what permissions a user has. These permissions can be an allow or an explicit deny.
When creating Management Groups, I have seen organizations struggle with applying Custom RBAC permissions to a Management Group and how it applies to the subscriptions within that Management Group.
Let’s take a look at creating a Management Group, creating a Custom RBAC role, assigning the Custom RBAC role to a Management Group, and how it applies to Azure Subscription(s) within that Management Group.
This article assumes you already have knowledge around the design of Management Groups and Custom RBAC roles. If you need more information on Management Group design, click here. If you need more information on Custom RBAC roles, click here. Sam Cogan also has a very good article on Custom RBAC Roles which you can view here.
Management Group Creation
In order to create our Management Group, go to All Services > search for management groups > click Management Groups.

The Tenant Root Group is the default Management Group that exists with all subscriptions assigned to the Tenant Root Group. Our goal is to create a new Management Group and move the Subscription we are targeting under the new Management Group. In our case, the subscription we are targeting is the Microsoft Partner Network subscription.

Create a new Management Group. We will call our new Management Group “ITDev” which means that any subscriptions that we have decided in design that we have a governance requirement that we require certain Azure Policy and certain RBAC controls to flow down from the ITDev Management Group to all subscriptions that will be within the ITDev Management Group without having to assign these policies and RBAC controls to every subscription manually.
To create the new Management Group, click “Add management group.”

Give the new Management Group an ID and a Display Name. We will use ITDev for both. Click Save.

We now see the ITDev Management Group.

Let’s move our Subscription to fall under the ITDev Management Group. In the Subscription, click the … and choose Move.

Choose the ITDev Management Group and click Save.

We no longer see our subscription under the Tenant Root Group.

However, if we go into the ITDev Management Group, we see our subscription there.

Custom RBAC Role Creation
Let’s use the Custom role example that I linked to earlier in the article. Again, that link is available here. The only thing I’m changing is the name by adding the word Custom to it.
The Custom role example is as follows:
{
"Name": "Custom Virtual Machine Operator",
"Id": "88888888-8888-8888-8888-888888888888",
"IsCustom": true,
"Description": "Can monitor and restart virtual machines.",
"Actions": [
"Microsoft.Storage/*/read",
"Microsoft.Network/*/read",
"Microsoft.Compute/*/read",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/restart/action",
"Microsoft.Authorization/*/read",
"Microsoft.ResourceHealth/availabilityStatuses/read",
"Microsoft.Resources/subscriptions/resourceGroups/read",
"Microsoft.Insights/alertRules/*",
"Microsoft.Insights/diagnosticSettings/*",
"Microsoft.Support/*"
],
"NotActions": [],
"DataActions": [],
"NotDataActions": [],
"AssignableScopes": [
"/subscriptions/{subscriptionId1}",
"/subscriptions/{subscriptionId2}",
"/subscriptions/{subscriptionId3}"
]
}
As you can see in Assignable Scopes, you can define the subscriptions you want this Custom RBAC role to be available in. In order for this to work with Management Groups, you must change the AssignableScopes to a Management Group. Because we created the ITDev Management Group with an ID of ITDev, our assignable scope would be:
"/providers/Microsoft.Management/managementGroups/ITDev"
Therefore, our Custom RBAC role would become:
{
"Name": "Custom Virtual Machine Operator",
"Id": "88888888-8888-8888-8888-888888888888",
"IsCustom": true,
"Description": "Can monitor and restart virtual machines.",
"Actions": [
"Microsoft.Storage/*/read",
"Microsoft.Network/*/read",
"Microsoft.Compute/*/read",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/restart/action",
"Microsoft.Authorization/*/read",
"Microsoft.ResourceHealth/availabilityStatuses/read",
"Microsoft.Resources/subscriptions/resourceGroups/read",
"Microsoft.Insights/alertRules/*",
"Microsoft.Insights/diagnosticSettings/*",
"Microsoft.Support/*"
],
"NotActions": [],
"DataActions": [],
"NotDataActions": [],
"AssignableScopes": [
"/providers/Microsoft.Management/managementGroups/ITDev"
]
}
Go ahead and save this Custom RBAC role as a .json file. We saved our file as customrbacrole.json.
Login to Azure PowerShell using instructions provided here using Connect-AzAccount. Ensure you are connected to the correct subscription using instructions provided here using Set-AzContext.
Create the new Custom RBAC Role using New-AzRoleDefinition pointing to the JSON file we saved.

Testing Custom RBAC Role
We have a user “John Doe” that has no Directory Roles assigned.

The “John Doe” user is also not assigned to any Azure roles.

When this user signs into the Azure Portal, we can see John Doe does not have access to our Subscription nor any resources within the Subscription.

Let’s go back to the Azure Portal with our Administrator account and grant this user access at the Management Group to our custom RBAC role, “Custom Virtual Machine Operator.”
In Management Groups, go to ITDev and click details.

Click Access Control (IAM), click Add, and select Add role assignment.

Select the Custom Role we created, search for John Doe and add him and ensure John Doe becomes a selected member. Click Save.

If we go back to our Azure Subscription and go to Access Control (IAM), let’s again do a Check access.

We can now see the John Doe user is in the Custom Virtual Machine Operator RBAC Custom Role that is being inherited at the Management group scope since we added this Custom RBAC Role permission to John Doe at the Management Group hierarchy level.
Now let’s try signing in again to the Azure Portal as John Doe. This may take a few minutes for the permission to propagate. Once the permission propogates, you will see John Doe now has access to the Azure Subscription as well as resources he has been granted access to.

Let me know if you have any questions in the comments below.
How do you update your custom role when its assignable scope is only a management group? I cannot seem to figure out how to modify this via PowerShell as get-azroledefinition is associated to the context (aka subscription) your PowerShell is running as, not the management group that has the role definition.
To modify:
1. $role = Get-AzRoleDefinition -Name “Custom Virtual Machine Operator”
2. $role.Actions.Add(“Microsoft.Insights/diagnosticSettings/*/read”)
3. Set-AzRoleDefinition -Role $role
(Get-AzRoleDefinition -Name “Custom Virtual Machine Operator”).Actions
To delete:
Need to add subscriptionID as an assignable scope. Then delete.
1. $role = Get-AzRoleDefinition -Name “Custom Virtual Machine Operator”
2. $role.AssignableScopes.Add(“/subscriptions/{SubscriptionID}”)
3. Get-AzRoleDefinition -Name “Custom Virtual Machine Operator” | Remove-AzRoleDefinition
Hope that helps.