Skip to main content

Deploying Azure Custom Roles from Code

· 6 min read

There are quite a few options for deploying Azure custom roles from code. Below they are listed and briefly summarised in order from worst to best.

tip

If you also need to export your current custom roles from Azure to code (JSON or YAML) then see my other blog: How to Export Custom Roles from Azure

PowerShell

★ ★ ☆ ☆ ☆

It is simple to deploy a single custom role using a PowerShell command, but it is not idempotent so any implementation in a pipeline would need some additional coding to ensure only changed definitions are redeployed.

e.g.

New-AzRoleDefinition -InputFile "C:\CustomRoles\customrole1.json"

If you like the look of this then Microsoft has documentation: Create or update Azure custom roles using Azure PowerShell

There is some work involved but very good solutions can be made using PowerShell.

CLI

★ ★ ☆ ☆ ☆

It is simple to deploy a single custom role using an AZ CLI command, but it is not idempotent so any implementation in a pipeline would need some additional coding to ensure only changed definitions are redeployed.

e.g.

az role definition create --role-definition ~/roles/vmoperator.json

If you like the look of this then Microsoft has documentation: Create or update Azure custom roles using Azure CLI

There is some work involved but solutions can be made using the Azure CLI.

REST API

★ ★ ☆ ☆ ☆

It is simple to deploy a single custom role using the REST API, but it is not idempotent so any implementation in a pipeline would need some additional coding to ensure only changed definitions are redeployed.

e.g.

PUT https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId}?api-version=2022-04-01

If you like the look of this then Microsoft has documentation: Create or update Azure custom roles using the REST API

There is some work involved but very good solutions can be made using the REST API.

ARM Templates

★ ★ ★ ★ ☆

ARM Templates are idempotent which is an improvement on the proceeding options.

Apart from being quite verbose it is a good option.

e.g.

{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.5.6.12127",
"templateHash": "2227781763411200690"
}
},
"parameters": {
"actions": {
"type": "array",
"defaultValue": [
"Microsoft.Resources/subscriptions/resourceGroups/read"
],
"metadata": {
"description": "Array of actions for the roleDefinition"
}
},
"notActions": {
"type": "array",
"defaultValue": [],
"metadata": {
"description": "Array of notActions for the roleDefinition"
}
},
"roleName": {
"type": "string",
"defaultValue": "Custom Role - RG Reader",
"metadata": {
"description": "Friendly name of the role definition"
}
},
"roleDescription": {
"type": "string",
"defaultValue": "Subscription Level Deployment of a Role Definition",
"metadata": {
"description": "Detailed description of the role definition"
}
}
},
"variables": {
"roleDefName": "[guid(subscription().id, string(parameters('actions')), string(parameters('notActions')))]"
},
"resources": [
{
"type": "Microsoft.Authorization/roleDefinitions",
"apiVersion": "2018-07-01",
"name": "[variables('roleDefName')]",
"properties": {
"roleName": "[parameters('roleName')]",
"description": "[parameters('roleDescription')]",
"type": "customRole",
"permissions": [
{
"actions": "[parameters('actions')]",
"notActions": "[parameters('notActions')]"
}
],
"assignableScopes": [
"[subscription().id]"
]
}
}
]
}

If you like the look of this then Microsoft has documentation: Create or update Azure custom roles using an ARM template

Bicep

★ ★ ★ ★ ☆

Bicep, like ARM Templates, is idempotent which is an improvement on the proceeding options.

Apart from being quite verbose it is a good option.

e.g.

targetScope = 'subscription'

@description('Array of actions for the roleDefinition')
param actions array = [
'Microsoft.Resources/subscriptions/resourceGroups/read'
]

@description('Array of notActions for the roleDefinition')
param notActions array = []

@description('Friendly name of the role definition')
param roleName string = 'Custom Role - RG Reader'

@description('Detailed description of the role definition')
param roleDescription string = 'Subscription Level Deployment of a Role Definition'

var roleDefName = guid(subscription().id, string(actions), string(notActions))

resource roleDef 'Microsoft.Authorization/roleDefinitions@2018-07-01' = {
name: roleDefName
properties: {
roleName: roleName
description: roleDescription
type: 'customRole'
permissions: [
{
actions: actions
notActions: notActions
}
]
assignableScopes: [
subscription().id
]
}
}

If you like the look of this then Microsoft has documentation: Create or update Azure custom roles using Bicep

Terraform

★ ★ ★ ★ ☆

Terraform is also idempotent.

It is a good option.

e.g.

Data "azurerm_subscription" "primary" {
}

resource "azurerm_role_definition" "example" {
name = "my-custom-role"
scope = data.azurerm_subscription.primary.id
description = "This is a custom role created via Terraform"

permissions {
actions = ["*"]
not_actions = []
}

assignable_scopes = [
data.azurerm_subscription.primary.id, # /subscriptions/00000000-0000-0000-0000-000000000000
]
}

And of course Terraform has plan and apply stages so you can see what changes are to be made before applying them.

If you like the look of this then the Terraform resource is documented here: azurerm_role_definition

Excellent solutions can be implemented using Terraform

Osservante RBAC Extension

★ ★ ★ ★ ★

This extension is a ready to go solution written in PowerShell/C#. It is a available in the Visual Studio Marketplace.

It was designed from the ground up as a easy to use centralised solution for Role Assignments and Custom Roles.

To deploy Azure custom roles you only need to understand YAML or JSON formats, no programming or Terraform skills are required.

note

Custom Role functionality is in preview and will be available in June 2024

Example Custom Roles

- Name: Osservante RBAC - Apply
IsCustom: true
Description: Manage RBAC and Tags
Actions:
- Microsoft.Authorization/roleAssignments/*
- Microsoft.Resources/subscriptions/resourceGroups/read
- Microsoft.Resources/subscriptions/resourceGroups/write
- Microsoft.Resources/subscriptions/resourceGroups/resources/read
- Microsoft.Resources/subscriptions/resources/read
- Microsoft.Resources/tags/*
NotActions: []
DataActions: []
NotDataActions: []
AssignableScopes:
- /providers/Microsoft.Management/managementGroups/OSX-MG-MAIN
- Name: Osservante RBAC - Read
IsCustom: true
Description: Manage RBAC and Tags
Actions:
- Microsoft.Authorization/roleAssignments/read
- Microsoft.Resources/subscriptions/resourceGroups/read
- Microsoft.Resources/subscriptions/resourceGroups/resources/read
- Microsoft.Resources/subscriptions/resources/read
- Microsoft.Resources/tags/read
NotActions: []
DataActions: []
NotDataActions: []
AssignableScopes:
- /providers/Microsoft.Management/managementGroups/OSX-MG-MAIN

Example Azure Role Assignments

---
assignments:
- role: Key Vault Reader
objectName: Susan.Fisher@osservantex.onmicrosoft.com
objectType: User
scope: "/providers/Microsoft.KeyVault/vaults/osxkvappx"

The extension enables you to take control of your Azure RBAC.

  • Centralise all RBAC configuration in one repository and pipeline
  • Simplify with human-readable files, and folder structure
  • Control with a plan stage that enables you to review changes and implement approval processes
  • Get started easily by exporting your current configuration to code

If you like the look of this then the find out more at the Osservante website, or in the Visual Studio Marketplace: