Custom Azure RBAC Roles

Role Based Access Control is Azure’s method for setting permissions on resources to control who can manage and administer these resources. Each type of Azure resource has a number of permissions that can be set on it, and these permissions can be grouped into roles that can be applied to users or groups of users to grant rights to manage resources. Out of the box Azure comes with a large number of pre-defined roles for common workloads. Firstly there are 3 high level roles:

  • Owner -Owners have all rights on the resource including the ability to change security settings (including membership of RBAC roles)
  • Contributor – As owner, but without the right to change security settings
  • Reader – Read only rights to resources

We then have a large number of resource specific roles:

  • Virtual Machine Contributor – Rights to manage and create virtual machines
  • Storage Contributor – Able to manage storage accounts
  • Backup Contributor – Able to manage backup settings

A full list of the built in roles can be found here.

Custom Roles

Sometimes, the built in roles won’t be specific enough or grant to few or many rights for the workload you need people to undertake. To resolve this you can create custom roles that grant the permissions you require. In this  example we have a scenario where we need our operations team to manage virtual machines, including the managed disks used by the VMs. By default the VM contributor role does not provide rights for managed disks. We can tell this by looking at the VM contributor role assigned to one of our resource in the portal and showing permissions, if we look under the compute resource permisisons we can see that there are no rights assigned to disks:

 

There is no GUI currently to create custom roles in the portal, so all of this will need to be done through the command line or REST API. we’ll look at doing this through PowerShell.

Create a Role Template

Custom roles are defined as a JSON template. The easiest way to create one of these is to download the role template for one of the existing roles. As we want to expand on the “Virtual Machine Contributor” we will download this as our starting point using this PowerShell command:

Get-AzureRmRoleDefinition -Name "Virtual Machine Contributor" |ConvertTo-Json |Out-File "C:\Temp\Virtual Machine Contributor.json"

This will download the template in a JSON file which shows the permissions assigned to this role:

{
    "Name": "Virtual Machine Contributor",
    "Id": "9980e02c-c2be-4d73-94e8-173b1dc7cf3c",
    "IsCustom": false,
    "Description": "Lets you manage virtual machines, but not access to them, and not the virtual network or storage account they're connected to.",
    "Actions": [
        "Microsoft.Authorization/*/read",
        "Microsoft.Compute/availabilitySets/*",
        "Microsoft.Compute/locations/*",
        "Microsoft.Compute/virtualMachines/*",
        "Microsoft.Compute/virtualMachineScaleSets/*",
        "Microsoft.DevTestLab/schedules/*",
        "Microsoft.Insights/alertRules/*",
        "Microsoft.Network/applicationGateways/backendAddressPools/join/action",
        "Microsoft.Network/loadBalancers/backendAddressPools/join/action",
        "Microsoft.Network/loadBalancers/inboundNatPools/join/action",
        "Microsoft.Network/loadBalancers/inboundNatRules/join/action",
        "Microsoft.Network/loadBalancers/probes/join/action",
        "Microsoft.Network/loadBalancers/read",
        "Microsoft.Network/locations/*",
        "Microsoft.Network/networkInterfaces/*",
        "Microsoft.Network/networkSecurityGroups/join/action",
        "Microsoft.Network/networkSecurityGroups/read",
        "Microsoft.Network/publicIPAddresses/join/action",
        "Microsoft.Network/publicIPAddresses/read",
        "Microsoft.Network/virtualNetworks/read",
        "Microsoft.Network/virtualNetworks/subnets/join/action",
        "Microsoft.RecoveryServices/locations/*",
        "Microsoft.RecoveryServices/Vaults/backupFabrics/protectionContainers/protectedItems/*/read",
        "Microsoft.RecoveryServices/Vaults/backupFabrics/protectionContainers/protectedItems/read",
        "Microsoft.RecoveryServices/Vaults/backupFabrics/protectionContainers/protectedItems/write",
        "Microsoft.RecoveryServices/Vaults/backupFabrics/backupProtectionIntent/write",
        "Microsoft.RecoveryServices/Vaults/backupPolicies/read",
        "Microsoft.RecoveryServices/Vaults/backupPolicies/write",
        "Microsoft.RecoveryServices/Vaults/read",
        "Microsoft.RecoveryServices/Vaults/usages/read",
        "Microsoft.RecoveryServices/Vaults/write",
        "Microsoft.ResourceHealth/availabilityStatuses/read",
        "Microsoft.Resources/deployments/*",
        "Microsoft.Resources/subscriptions/resourceGroups/read",
        "Microsoft.Storage/storageAccounts/listKeys/action",
        "Microsoft.Storage/storageAccounts/read",
        "Microsoft.Support/*"
    ],
    "NotActions": [],
    "AssignableScopes": [
        "/"
    ]
}

In this file there are a number of sections:

  • Name – The display name that will be used to refer to this role
  • ID – The ID for the role
  • IsCustom – Indicating if this is a built in or custom role
  • Description – More detail on the role
  • Actions –  The actions or permissions that this role grants to the user to be able to do
  • NotActions – The reverse of actions, explicitly deny permissions for the user
  • AssignableScopes – What scope (subscription, resource group) the role is applied to

Edit the Role Template

Now we have a template to use, we can amend this to fit our needs, but before we work on the permissions there are a few other things we need to change:

  • Update the name and Description to describe our custom role
  • Delete the ID field, Azure will create an ID when we create the role
  • Set IsCustom to true
  • Change the AssignableScope. We don’t have rights to apply this role to all subscriptions, so we need to set it to a specific subscription or list of subscriptions:
 "AssignableScopes": [ "/subscriptions/c276fc76-9cd4-44c9-99a7-4fd71546436e", "/subscriptions/e91d47c4-76f3-4271-a796-21b4ecfe3624", "/subscriptions/34370e90-ac4a-4bf9-821f-85eeedeae1a2" ]

As our template setup we just need to adjust our permissions. We are going to add a small number of allow permissions so it makes sense to use the Actions section. If you wanted to deny permissions or wanted to grant every permission except one then you could use the NotActions section. We need to determine what actions or permissions we want to grant on our resources. To do this you can find a list of the available actions two ways:

  1. Use the Azure portal, got to any resources “Access Control (IAM)” section, then click roles at the top. Find a role that likely uses the resource you are interested in and then click on “permissions”. Locate the resource you are interested in and select it, then select the area you are looking for to be able to see the list of available actions. Here you can see the actions available on Disks:
  2. Use PowerShell to list all actions on a resource. The command below lists actions on the Microsoft Compute resource, which is the one we are interested in.
Get-AzureRMProviderOperation "Microsoft.Compute/*" | fl Operation

Once we identify the actions we want to grant (or deny) we just need to add these to the JSON file. You can grant rights to low level actions, or at a higher level to all actions beneath this level. In our example we want to grant rights on all Disk Actions, so we are going to add:

"Microsoft.Compute/Disks*",

So our final JSON file will be as below:

{
    "Name": "Virtual Machine adn Managed Disk Contributor",
    "IsCustom": true,
    "Description": "Lets you manage virtual machines and their managed disks, but not access to them, and not the virtual network or storage account they're connected to.",
    "Actions": [
        "Microsoft.Authorization/*/read",
        "Microsoft.Compute/availabilitySets/*",
        "Microsoft.Compute/locations/*",
        "Microsoft.Compute/virtualMachines/*",
        "Microsoft.Compute/virtualMachineScaleSets/*",
        "Microsoft.Compute/virtualMachines/*",
        "Microsoft.DevTestLab/schedules/*",
        "Microsoft.Insights/alertRules/*",
        "Microsoft.Network/applicationGateways/backendAddressPools/join/action",
        "Microsoft.Network/loadBalancers/backendAddressPools/join/action",
        "Microsoft.Network/loadBalancers/inboundNatPools/join/action",
        "Microsoft.Network/loadBalancers/inboundNatRules/join/action",
        "Microsoft.Network/loadBalancers/probes/join/action",
        "Microsoft.Network/loadBalancers/read",
        "Microsoft.Network/locations/*",
        "Microsoft.Network/networkInterfaces/*",
        "Microsoft.Network/networkSecurityGroups/join/action",
        "Microsoft.Network/networkSecurityGroups/read",
        "Microsoft.Network/publicIPAddresses/join/action",
        "Microsoft.Network/publicIPAddresses/read",
        "Microsoft.Network/virtualNetworks/read",
        "Microsoft.Network/virtualNetworks/subnets/join/action",
        "Microsoft.RecoveryServices/locations/*",
        "Microsoft.RecoveryServices/Vaults/backupFabrics/protectionContainers/protectedItems/*/read",
        "Microsoft.RecoveryServices/Vaults/backupFabrics/protectionContainers/protectedItems/read",
        "Microsoft.RecoveryServices/Vaults/backupFabrics/protectionContainers/protectedItems/write",
        "Microsoft.RecoveryServices/Vaults/backupFabrics/backupProtectionIntent/write",
        "Microsoft.RecoveryServices/Vaults/backupPolicies/read",
        "Microsoft.RecoveryServices/Vaults/backupPolicies/write",
        "Microsoft.RecoveryServices/Vaults/read",
        "Microsoft.RecoveryServices/Vaults/usages/read",
        "Microsoft.RecoveryServices/Vaults/write",
        "Microsoft.ResourceHealth/availabilityStatuses/read",
        "Microsoft.Resources/deployments/*",
        "Microsoft.Resources/subscriptions/resourceGroups/read",
        "Microsoft.Storage/storageAccounts/listKeys/action",
        "Microsoft.Storage/storageAccounts/read",
        "Microsoft.Support/*"
    ],
    "NotActions": [],
    "AssignableScopes": [
        "/subscriptions/c276fc76-9cd4-44c9-99a7-4fd71546436e"
    ]
}

Deploy the Custom Role

Now we have our JSON role definition done all we need to do is run the PowerShell command below to create it, referencing the JSON file we created:

New-AzureRmRoleDefinition -InputFile 'C:\temp\Virtual Machine Managed Disk Contributor.json'

Once this completes it will return a summary of our new role.

And it is now ready to be used in the RBAC role allocation.