Building Custom Role-Based Access Control on Azure
TypeScriptRole-Based Access Control (RBAC) in Azure is a method used to assign permissions to users, groups, and applications at a certain scope. RBAC ensures that only authorized users are able to perform specific actions within the environment. This helps you to adhere to the principle of least privilege and grant only the access that users need to perform their jobs.
To create custom roles and assign roles to users, we first need to be familiar with the core components:
- Roles: These define the permissions to do management operations at different scopes like a subscription, resource group, or a single resource.
- Role Assignments: This is the process of binding a role to a user, group, application, or managed identity at a specific scope for them to have the defined permissions.
We'll need the following resources for creating a custom RBAC on Azure using Pulumi:
RoleDefinition
: Represents a role definition which includes a type and a set of permissions.RoleAssignment
: Associates a role definition with a particular scope and principal (user, group, service principal, or managed identity).
Let's go step by step to create a custom RBAC using Pulumi in TypeScript. Here's a basic program structure that describes the creation of a custom role that gives read access to virtual machines and an assignment of that role to a user.
import * as pulumi from "@pulumi/pulumi"; import * as azure from "@pulumi/azure-native"; // Replace these variables with actual values or retrieve them from configuration/environment const scope = "/subscriptions/<Your Subscription ID>"; const principalId = "<Principal ID of the User/Group>"; // The user/group/application you want to assign the role to const roleName = "CustomVMReader"; // Create a Custom Role Definition for reading Virtual Machines const customVmReadRole = new azure.authorization.RoleDefinition(roleName, { roleName: roleName, scope: scope, permissions: [ { actions: ["Microsoft.Compute/virtualMachines/read"], notActions: [], }, ], assignableScopes: [scope], }, { parent: pulumi.rootStackResource }); // Using rootStackResource sets a transformation to inherit the parent's stack name // Assign the custom role to the principal at the designated scope const roleAssignment = new azure.authorization.RoleAssignment("roleAssignment", { scope: scope, roleDefinitionId: customVmReadRole.id, principalId: principalId, }, { parent: customVmReadRole }); // Export the id of the role assignment export const roleAssignmentId = roleAssignment.id;
Here’s what each part of the program is doing:
- Importing Pulumi packages for interaction with the Azure platform.
- Declaring variables for scope, principalId, and roleName that are going to be relevant for the role definition and assignment.
- Defining a new custom role
customVmReadRole
usingazure.authorization.RoleDefinition
with actions specified to grant read permissions on virtual machines in thepermissions
field. - Creating a role assignment
roleAssignment
usingazure.authorization.RoleAssignment
, binding our custom role to the principal ID we've provided, applying this to the scope of the full subscription (or you could limit to a resource group or resource). - Exporting the
roleAssignmentId
for reference or use in future operations.
Remember to replace
<Your Subscription ID>
and<Principal ID of the User/Group>
with actual values from your Azure setup. You may retrieve these dynamically through code in more complex scenarios. Also, more permission actions can be added in the permissions section to tailor the role to your specific needs.After writing your Pulumi program, you install the necessary dependencies, initialize a new Pulumi stack if you haven't done so already, and run
pulumi up
to deploy your infrastructure.Keep in mind that RBAC changes in Azure require proper permissions. Make sure the account/identity running the Pulumi program has necessary permissions such as Role Administrator or Owner of the subscription or resource groups you are making these changes to.