Role-Based Least Privilege
In some cases, we have operations teams or developers that need tools or applications to be able access to their Azure virtual machines, but they only need the ability to perform basic operations: starting, stopping, restarting, or deallocating VMs. In this case, we can assign them a service principal for a given role with rights to perform only those operations. This is referred to as “role based access control” or “RBAC” and is in keeping with the best practice of least-privilege.
Creating a service principle limited to specific virtual machines in this way is fairly straightforward if the VMs are in resource groups by their application stack.
Creating a Custom RBAC Role
The first thing we need to do is to create a custom role. To do this, we need to create a role definition in JSON format.
{
"Name": "Virtual Machine Operator",
"IsCustom": true,
"Description": "Manage the power state of virtual machines, but nothing else.",
"Actions": [
"Microsoft.Storage/*/read",
"Microsoft.Network/*/read",
"Microsoft.Compute/*/read",
"Microsoft.Compute/virtualMachines/deallocate/action",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/restart/action",
"Microsoft.Authorization/*/read",
"Microsoft.Resources/subscriptions/resourceGroups/read"
],
"NotActions": [],
"AssignableScopes": [
"/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
]
}
Notice that we’ve limited the virtual machine actions to deallocate
, start
, and restart
in the “Actions” array. You could additionally add powerOff
as well, which does not deallocate the VM after shutdown. The remaining actions are read operations only, to allow for basic visibility into related resource status where it may be required.
To create the role, we simply save the definition as a JSON file and then run the following Azure CLI command:
$ az role definition create --role-definition ./vm-operator.json
Description Name RoleName RoleType
-------------------------------------------------------------------- ------------------------------------ ------------------------- ----------
Manage the power state of select virtual machines, but nothing else. XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Virtual Machine Operator CustomRole
Viewing Role Definitions
To view the role definition for a specific role, such as the one we just created, reference the role by name, as follows:
az role definition list -n 'Virtual Machine Operator' -o json
Note that we’re using list
as the command here. Unlike some other Azure CLI modules, the role definition
sub group doesn’t have a show
command.
If you wish to see a list of all the custom roles on the subscription, run the following:
az role definition list --custom-role-only true
Creating Service Principal using a Custom RBAC Role
Now that we have a custom role, we can create a service principal based on that role. This is a key part of the process as this the point at which we limit the scope of the permissions to a subset of resources — in this case, virtual machines.
az ad sp create-for-rbac --name "vm_operator_prod" \
--role "Virtual Machine Operator" \
--scopes "/subscriptions/${subscription_id}/resourceGroups/prod-web-rg" \
"/subscriptions/${subscription_id}/resourceGroups/prod-db-rg" \
"/subscriptions/${subscription_id}/resourceGroups/prod-mq-rg" \
--query '{"app_id": appId, "tenant": tenant, "password": password}' \
--output json
The name we supplied here as vm_operator_prod
could be anything we want, provided it doesn’t collide with another principal. The role name should match whatever custom role you want to assign.
The scopes are the a list of the resource IDs that we want to limit the service principal to using its role privileges on. In this case, we’ve limited this principal to starting, restarting, or deallocating virtual machines in the three resource groups we’ve listed.
Once the command completes, the --query
will return the crucial pieces of information we will need in order to use the service principal:
{
"app_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"tenant": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"password": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}
Using the appId
, tenant
, and password
, we can configure whatever applications in our tool chain require the ability to perform the functions in the role.
Important: Take note of at least the password immediately, as you will not be able to retrieve it again. The only way to obtain a password is at creation time, so you’ll have to reset it if you don’t make a note of it right away.
Log in Using the Service Principal
All that’s left is to log in with Azure CLI or PowerShell:
# Log into Azure CLI with the Service Principle
az login --service-principal \
--username $app_id --password $password --tenant $tenant
Additional Reading
- Documentation: Understand role definitions in Azure RBAC
- Documentation: Use role-based access control (RBAC)
- Documentation: Log in using the service principal (Azure CLI)