Skip to main content

Azure RBAC Policy Reference

This document outlines the Azure RBAC permissions required for NightOps to manage Microsoft Azure resources. The policy follows the principle of least privilege.

Quick Start

For a quick setup, use the custom RBAC role below. For more granular control, see the Per-Service Permissions section.

Complete NightOps Custom Role

{
"Name": "NightOps Resource Manager",
"Description": "Allows NightOps to manage compute resources for cost optimization",
"Actions": [
"Microsoft.Compute/virtualMachines/read",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/deallocate/action",
"Microsoft.Compute/virtualMachines/instanceView/read",
"Microsoft.Compute/virtualMachineScaleSets/read",
"Microsoft.Compute/virtualMachineScaleSets/write",
"Microsoft.Sql/servers/read",
"Microsoft.Sql/servers/databases/read",
"Microsoft.Sql/servers/databases/pause/action",
"Microsoft.Sql/servers/databases/resume/action",
"Microsoft.ContainerService/managedClusters/read",
"Microsoft.ContainerService/managedClusters/agentPools/read",
"Microsoft.ContainerService/managedClusters/agentPools/write"
],
"NotActions": [],
"DataActions": [],
"NotDataActions": [],
"AssignableScopes": [
"/subscriptions/YOUR_SUBSCRIPTION_ID"
]
}

Create the Custom Role

az role definition create --role-definition nightops-role.json

Create Service Principal and Assign Role

# Create service principal
az ad sp create-for-rbac \
--name "NightOps" \
--role "NightOps Resource Manager" \
--scopes /subscriptions/YOUR_SUBSCRIPTION_ID

Per-Service Permissions

Virtual Machines

PermissionPurpose
Microsoft.Compute/virtualMachines/readList and get VM details
Microsoft.Compute/virtualMachines/instanceView/readGet VM power state
Microsoft.Compute/virtualMachines/start/actionStart deallocated VMs
Microsoft.Compute/virtualMachines/deallocate/actionDeallocate running VMs

Built-in Role Alternative: Virtual Machine Contributor

Over-Permissive

The built-in role includes delete permissions. Use the custom role for least privilege.

Azure SQL Database

PermissionPurpose
Microsoft.Sql/servers/readList SQL servers
Microsoft.Sql/servers/databases/readList and get database details
Microsoft.Sql/servers/databases/pause/actionPause serverless databases
Microsoft.Sql/servers/databases/resume/actionResume paused databases

Built-in Role Alternative: SQL DB Contributor

Serverless Only

Pause/resume is only available for serverless tier databases.

AKS Node Pools

PermissionPurpose
Microsoft.ContainerService/managedClusters/readList and get cluster details
Microsoft.ContainerService/managedClusters/agentPools/readList and get node pool details
Microsoft.ContainerService/managedClusters/agentPools/writeUpdate node pool size

Built-in Role Alternative: Azure Kubernetes Service Contributor

VM Scale Sets

PermissionPurpose
Microsoft.Compute/virtualMachineScaleSets/readList and get VMSS details
Microsoft.Compute/virtualMachineScaleSets/writeUpdate VMSS capacity

Built-in Role Alternative: Virtual Machine Contributor


Multi-Subscription Setup

For managing resources across multiple Azure subscriptions, assign the custom role at each subscription level.

Architecture

┌─────────────────┐         ┌─────────────────┐
│ Hub Subscription│ │ Spoke Subscription│
│ (NightOps) │────────▶│ (Customer) │
│ │ RBAC │ │
└─────────────────┘ └─────────────────┘

Setup Steps

  1. Create the custom role in each subscription (or at management group level)
  2. Create a single service principal in Azure AD
  3. Assign the role to the service principal in each subscription
# Assign role in spoke subscription
az role assignment create \
--assignee "SERVICE_PRINCIPAL_APP_ID" \
--role "NightOps Resource Manager" \
--scope "/subscriptions/SPOKE_SUBSCRIPTION_ID"

Management Group Level

For easier management across many subscriptions, create the role at the management group level:

{
"AssignableScopes": [
"/providers/Microsoft.Management/managementGroups/YOUR_MANAGEMENT_GROUP"
]
}

Tag-Based Access Control

Azure supports Conditions for fine-grained access control based on resource tags.

Example: Restrict to Tagged Resources

az role assignment create \
--assignee "SERVICE_PRINCIPAL_APP_ID" \
--role "NightOps Resource Manager" \
--scope "/subscriptions/YOUR_SUBSCRIPTION_ID" \
--condition "((!(ActionMatches{'Microsoft.Compute/virtualMachines/start/action'})) OR (@Resource[Microsoft.Compute/virtualMachines/tags.nightops-managed] StringEquals 'true'))" \
--condition-version "2.0"
Preview Feature

ABAC (Attribute-Based Access Control) conditions are in preview for some resource types.


Permissions Summary Table

ServiceRead PermissionsWrite Permissions
VMsvirtualMachines/read, instanceView/readstart/action, deallocate/action
SQLservers/read, databases/readdatabases/pause/action, databases/resume/action
AKSmanagedClusters/read, agentPools/readagentPools/write
VMSSvirtualMachineScaleSets/readvirtualMachineScaleSets/write

Terraform Module

# Create custom role
resource "azurerm_role_definition" "nightops" {
name = "NightOps Resource Manager"
scope = data.azurerm_subscription.current.id
description = "Allows NightOps to manage compute resources"

permissions {
actions = [
"Microsoft.Compute/virtualMachines/read",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/deallocate/action",
"Microsoft.Compute/virtualMachines/instanceView/read",
"Microsoft.Compute/virtualMachineScaleSets/read",
"Microsoft.Compute/virtualMachineScaleSets/write",
"Microsoft.Sql/servers/read",
"Microsoft.Sql/servers/databases/read",
"Microsoft.Sql/servers/databases/pause/action",
"Microsoft.Sql/servers/databases/resume/action",
"Microsoft.ContainerService/managedClusters/read",
"Microsoft.ContainerService/managedClusters/agentPools/read",
"Microsoft.ContainerService/managedClusters/agentPools/write",
]
not_actions = []
}

assignable_scopes = [
data.azurerm_subscription.current.id
]
}

# Create service principal
resource "azuread_application" "nightops" {
display_name = "NightOps"
}

resource "azuread_service_principal" "nightops" {
application_id = azuread_application.nightops.application_id
}

resource "azuread_service_principal_password" "nightops" {
service_principal_id = azuread_service_principal.nightops.id
}

# Assign custom role
resource "azurerm_role_assignment" "nightops" {
scope = data.azurerm_subscription.current.id
role_definition_id = azurerm_role_definition.nightops.role_definition_resource_id
principal_id = azuread_service_principal.nightops.object_id
}

Bicep Template

targetScope = 'subscription'

@description('The principal ID to assign the role to')
param principalId string

resource nightopsRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' = {
name: guid(subscription().id, 'NightOps Resource Manager')
properties: {
roleName: 'NightOps Resource Manager'
description: 'Allows NightOps to manage compute resources for cost optimization'
type: 'CustomRole'
permissions: [
{
actions: [
'Microsoft.Compute/virtualMachines/read'
'Microsoft.Compute/virtualMachines/start/action'
'Microsoft.Compute/virtualMachines/deallocate/action'
'Microsoft.Compute/virtualMachines/instanceView/read'
'Microsoft.Compute/virtualMachineScaleSets/read'
'Microsoft.Compute/virtualMachineScaleSets/write'
'Microsoft.Sql/servers/read'
'Microsoft.Sql/servers/databases/read'
'Microsoft.Sql/servers/databases/pause/action'
'Microsoft.Sql/servers/databases/resume/action'
'Microsoft.ContainerService/managedClusters/read'
'Microsoft.ContainerService/managedClusters/agentPools/read'
'Microsoft.ContainerService/managedClusters/agentPools/write'
]
notActions: []
}
]
assignableScopes: [
subscription().id
]
}
}

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(subscription().id, principalId, nightopsRole.id)
properties: {
roleDefinitionId: nightopsRole.id
principalId: principalId
principalType: 'ServicePrincipal'
}
}

Security Best Practices

Tag-Based Management

Use Azure tags (nightops-managed=true) to identify resources that NightOps should manage.

Activity Log

Enable Azure Activity Log and configure alerts for NightOps actions for compliance and debugging.

Managed Identity

For Azure-hosted deployments, use Managed Identity instead of service principal secrets.

Key Rotation

If using service principal secrets, rotate them regularly and store in Azure Key Vault.

Next Steps