PIM for Groups with Terraform

PIM for Groups with Terraform

Technical overview of a new AVM-style module for PIM Groups

PIM for Groups with Terraform

In this post I explore a new resource module I propose for Azure Verified Modules (AVM) to handle Privileged Identity Management (PIM) Groups: terraform-azuread-avm-res-pim-group.

We’ll cover the key recommendations, which have been referenced within the examples that test the module. This ability to reference the why behind the solution is often an underused superpower of infrastructure as code that your future self will thank you for.

What is PIM?

Privileged Identity Management (PIM) provides time-bound, approval-based access to resources. It enforces just-in-time (JIT) access, reducing the attack surface by ensuring users only hold privileged permissions when necessary.

Module Capabilities

The module is designed to cover the full spectrum of PIM group scenarios:

  1. PIM for Groups (Eligible Users): The group holds the role permanently; users activate membership.
  2. Group Eligible: The group itself is eligible for the role; users are permanent members.
  3. PIM for Standard Groups: PIM features applied to standard security groups (non-role-assignable).

Preferred Pattern: PIM for Groups

The module prioritizes the “PIM for Groups” pattern (Eligible Users). In this configuration, the group is permanently assigned the Azure role (e.g., Contributor), but users are only eligible members of the group.

Diagram showing a User as an Eligible Member of a PIM Group, which has a Permanent Assignment to an Azure Resource

Deep Dive: PIM for Groups

The Typical Example demonstrates a Role-Assignable Group with direct user eligibility. Instead of nesting another group, individual users are made eligible for the role-assignable group.

To assign Entra ID roles (e.g., Global Administrator) to a group, the group must be created with assignable_to_role = true. This property is immutable.

Role-assignable groups offer enhanced protection:

  • Management Restriction: Only Global Admins, Privileged Role Admins, or the group Owner can manage membership.
  • Escalation Prevention: Lower-privileged admins (e.g., Helpdesk) cannot reset credentials for members of these groups, preventing lateral movement to higher privileges.

Lateral Movement Risk

This design choice avoids a critical security risk. If you were to nest a standard security group (e.g., “Operations Team”) and make it eligible for a role-assignable group, any administrator who can manage that standard group (e.g., a User Administrator) could add themselves to it. They could then activate PIM and escalate their privileges to the role-assignable group’s level (potentially Global Admin). By using direct user assignment (or ensuring nested groups are also role-assignable), this lateral movement path is closed.

Alternative Patterns

Group Eligible

The Group Eligible pattern is supported but reserved for edge cases. Specifically, some M365 portals (SharePoint, OneDrive) experience latency with PIM for Groups. In those specific scenarios, activating the group itself can be a necessary workaround.

Diagram showing the Group Eligible pattern where users are permanent members and the group is eligible for the role. Note the warning that activation grants access to all members.

This approach should be used judiciously, the “PIM for Groups” pattern is preferred for a number of reasons:

  • Audit Trail: Activation is recorded per user. In the “Group Eligible” pattern, logs only show the group activating the role, obscuring individual accountability.
  • Blast Radius: When a group activates a role, all members inherit the permission, regardless of whether they are performing a task. PIM for Groups ensures only the active user holds the privilege.
  • Tooling: Standard workflows and approvals are designed around user identities.

Standard Groups

For Azure Resource RBAC (where Entra ID roles are not required), Standard Security Groups can be used. This avoids consuming the tenant quota for role-assignable groups (limit of 500). Since these groups are not role-assignable, using nested groups for assignment does not introduce the same lateral movement risks described above.

Baked-in Best Practices

AVM uses examples as AVM tests, with a PR workflow deploying each in turn. Besides this, I like to use examples to represent and record recommended patterns. The Typical Example, for instance, demonstrates how to enforce strong governance by default.

It combines the security of Role-Assignable Groups with strict PIM activation policies—requiring MFA, ticket numbers, and approval workflows in a few lines of code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
  pim_policy_settings = {
    activation_rules = [
      {
        require_ticket_info = true
        approval_stage = [
          {
            primary_approver = [
              {
                object_id = azuread_user.approver.object_id
              }
            ]
          }
        ]
      }
    ]
  }
  pim_require_approval_on_activation = true
  pim_require_mfa_on_activation      = true

Proposal and Feedback

The module is currently under proposal with the AVM team. You can review the full proposal and provide feedback on the GitHub issue:

AVM Issue #2501: [Module Proposal]: terraform-azuread-avm-res-pim-group

Alternatively, drop me a line on social media (I really should get comments added to the blog too!)

This post is licensed under CC BY 4.0 by the author.