Skip to content

Role Assignments

Copy/Paste Quick Reference

role_assignment = {
    # For assigning a role to a group you created in terraform, use "group" for principal type
    # and use the key for the group you created
    Example_Role = {
        scope = "/subscriptions/<subscription_id>"
        role_definition_name = "Reader"
        description = "Assigns the built-in Reader role to the Example entra group"
        principal = {
            type = "group"
            key  = "example_group"
        }
    }
    # For assigning a role to an existing resource not created with terraform, get the resource's
    # object ID and use either "Group", "ServicePrincipal", or "User" for principal_type
    Example_Role_2 = {
        scope = "/subscriptions/<subscription_id>/resourceGroups/<resource group name>"
        role_definition_name = "Storage Blob Data Reader"
        description = "Assigns the built-in Storage Blob Data Reader role to a user account"
        principal_id = "<user account object ID>"
        principal_type = "User"
    }
    # For assigning a role to an identity you created in terraform, use "user_assigned_identity"
    # for principal type and use the key for the identity you created
    Example_Role_3 = {
        scope = "/subscriptions/<subscription_id>"
        role_definition_name = "Virtual Machine Contributor"
        description = "Assigns the built-in Virtual Machine Owner role to the ex_ua_identity user assigned identity"
        principal = {
            type = "user_assigned_identity"
            key  = "ex_ua_identity"
        }
    }
    # For scoping a role assignment to a resource you created in terraform, use "scope_resource"
    # instead of "scope" and reference the type and key of the resource created in tfvars
    Example_Role_4 = {
        scope_resource = {
            type = "container_registry"
            key  = "example_registry"
        }
        role_definition_name = "AcrPull"
        description = "Allows the ex_ua_identity user assigned identity to pull images from the example_registry container registry"
        principal = {
            type = "user_assigned_identity"
            key  = "ex_ua_identity"
        }
    }
}

Overview

Use this module to manage Azure RBAC role assignments in terraform. Role assignments require 3 key components: Scope, Role Definition, Principal. This module also includes the Description argument which should also be used.

Components

See the above Copy/Paste Quick Reference section for examples of each of the following components.

scope: The scope to which a role assignment applies. The scope can be anything as broad as a management group or as specific as a resource. You need the azure path to the resource you want to apply the scope to, which you can find in the path of the azure portal url of the resource when viewing it in a browser. Most often you will be defining role assignments at the resource group level, so your scope should look like /subscriptions/<subscription_id>/resourceGroups/<resource group name>

  • Avoid using specific resources as the scope if you can. Over time, most deployments will grow and change in some ways. Role assignments to specific resources can add additional headache to changes made over time. It will also make your tfvars file longer and harder to read. Some custoemrs or situations may require scope be defined to a single resource as a security measure.
  • Use raw scope strings only for resources not managed in this terraform (e.g. subscriptions, management groups, or pre-existing resources). If the resource is created in tfvars, use scope_resource instead.

scope_resource: An alternative to scope for scoping a role assignment to a resource created in this terraform. Instead of hardcoding the resource's ARM ID, use the scope_resource = { type = "", key = "" } format (mirroring the principal block) to reference the type and key of the resource defined in tfvars. The scope is resolved from the module's output, which has two benefits: a rename or subscription move can't silently break the assignment, and referencing the output creates the dependency edge automatically so the assignment can't be created before its target resource on a fresh apply. Set exactly one of scope or scope_resource — validation enforces that they are mutually exclusive.

  • Currently supported types: container_registry.
  • When developing new modules that can be used as a scope, add a new block to the supported_scope_types in role_assignment\locals.tf mapping the value you want to use as the type in tfvars to the module and the attribute holding its ARM ID (usually id). Then, add the module to role_assignment\variables.tf and main.role_assignment.tf.

role_definition_name: The name of the built-in azure role to be used with the role assignment. A role is a collection of permissions. See the full list of possible roles here. Make sure you follow the principal of least privilege when assigning roles: only give a user the minimum necessary permissions need to preform a job junction. Frequently, azure documentation for different resources include information about roles necessary for managing that resource. AI is also good at helping figure out what role you need, and you can use the previous linked list of built-in roles to verify the permissions included in proposed roles.

principal: The principal is a object receiving the permissions defined by the assigned role. Frequently, this will either be a user group in Entra or a managed identity. You can also assign roles to service principals or directly to users. Avoid assigning roles to single users for the same reasons you should avoid assigning scope to single resources. As people's responsiblities shift and ownership areas grow and shrink, single-user role assignments can create some headache. When possible, create an Entra group and assign roles to that group so users can be added/removed without having to update terraform.

  • When creating role assignments for resources created with the azuread_group or user_assigned_identity, you can use the principal = { type = "", key = "" } format to reference the type and key of the resource created in tfvars.
    • When developing new modules that can be used with the role_assignment module, you only need to add a new block to the supported_principal_types in role_assignment\locals.tf mapping value you want to use as the type in tfvars to the module, attribute to use as its ID (usually object or principal ID), and the principal type expected by the azurerm provider. Then, add the module to role_assignment\variables.tf and main.role_assignment.tf.
  • When creating role assignments for resources defined outside of our tfvars, use "principal_id" and "principal_type" to directly pass in the values expected by the azurerm provider.

description: A description of the role assignment created is optional, but highly encouraged. This description will be visible in some places and makes the assignments in tfvars more parsable. Include any key words that will help others find the role assignment and understand the intent and scope.