Craig Forrester

The Pleasure of Finding Things Out

github linkedin email rss
How to Create an Azure Service Principal with Limited Rights
May 16, 2018
4 minutes read

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


Back to posts