top of page

Getting started with Terraform and automated access management in Microsoft Entra ID

  • Writer: Bjørnar Aassveen
    Bjørnar Aassveen
  • 6 days ago
  • 5 min read

Terraform is a tool for Infrastructure as Code (IaC), also for identity and access in Microsoft Entra ID. In this guide, I walk through how you can use Terraform, possibly, to:


  • Create Entra ID groups

  • Expose the group through Access Packages

  • Set up PIM roles that are activated through group membership

  • Require, or not require, approval

  • Limit activation duration to four hours

  • Allow end users to request access themselves


I had never used Terraform myself before this, so this guide is intended to be a practical getting started walkthrough. There may very well be shorter or better ways to reach the same goal.


What are we building?

The target state is as follows:

  • A user requests access through an Access Package

  • The user becomes a member of an Entra ID group

  • The group provides eligible access to multiple Entra ID roles through PIM


The user can activate:

  • Global Reader with approval

  • Global Administrator with approval

  • Teams Administrator without approval

  • SharePoint Administrator without approval


For all roles, defined per role in the GUI and not currently supported in Terraform:

  • PIM activation is required

  • Maximum duration is four hours

  • A justification is required



Prerequisites

Before you get started, you need:

  • An Entra ID tenant with a P2 license

  • Global Administrator permissions

  • Terraform installed locally

  • Azure CLI or Microsoft Graph based authentication




1. Install Terraform


macOS

brew install terraform

Windows (winget)

winget install Hashicorp.Terraform

  1. Install VS Code‑utvidelser

In VS Code (Extensions):

  • Terraform (HashiCorp)

  • Azure Resources

  • Azure CLI Tools 


  1. Create Terraform‑project in VS Code


  1. File → Open Folder

  2. Create folder

entra-terraform
  1. Create files

In VS Code Explorer (left side):

Right click the folder, select New File, and create:

main.tf
providers.tf
variables.tf
terraform.tfvars

  1. Configure Terraform provider (providers.tf)

terraform {
  required_providers {
    azuread = {
      source  = "hashicorp/azuread"
      version = "~> 2.50"
    }
  }
}

provider "azuread" {
  tenant_id = var.tenant_id
}

VS Code will now:

  • Recognize Terraform syntax

  • Suggest blocks and fields

  • Display errors directly in the editor


6. Variables (variables.tf + terraform.tfvars)



variable "tenant_id" {
  description = "Entra ID Tenant ID"
  type        = string
}
tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

The foundational Terraform structure is now in place in VS Code.


7. Authenticate Entra ID

Open Terminal in VS Code:

az login

If you get the message az: The term 'az' is not recognized, it means that Azure CLI is not installed or not available in your PATH.


Install Azure CLI and restart your terminal before running az login.



Now that the basic structure, tools, and access are in place, we can start setting up the Terraform code that builds what we want to build.


  1. Open main.tf and create the access group

resource "azuread_group" "privileged_access_group" {
  display_name     = "Privileged-Access-Users"
  security_enabled = true
  mail_enabled     = false
  assignable_to_role   = true
}
  1. Open main.tf and fetch the Entra ID roles

    First, we need to reference the built in Entra roles. Terraform does not create them, they already exist in the tenant.


# Global Reader
resource "azuread_directory_role" "global_reader" {
  display_name = "Global Reader"
}

# Global Administrator
resource "azuread_directory_role" "global_admin" {
  display_name = "Global Administrator"
}

# Teams Administrator
resource "azuread_directory_role" "teams_admin" {
  display_name = "Teams Administrator"
}

# SharePoint Administrator
resource "azuread_directory_role" "sharepoint_admin" {
  display_name = "SharePoint Administrator"
}


  1. Make the group eligible for Entra ID roles using PIM


# Global Reader – Eligible
resource "azuread_directory_role_assignment" "global_reader_pim" {
  role_id             = azuread_directory_role.global_reader.id
  principal_object_id = azuread_group.privileged_access_group.object_id
}

# Global Administrator – Eligible
resource "azuread_directory_role_assignment" "global_admin_pim" {
  role_id             = azuread_directory_role.global_admin.id
  principal_object_id = azuread_group.privileged_access_group.object_id
}

# Teams Administrator – Eligible
resource "azuread_directory_role_assignment" "teams_admin_pim" {
  role_id             = azuread_directory_role.teams_admin.id
  principal_object_id = azuread_group.privileged_access_group.object_id
}

# SharePoint Administrator – Eligible
resource "azuread_directory_role_assignment" "sharepoint_admin_pim" {
  role_id             = azuread_directory_role.sharepoint_admin.id
  principal_object_id = azuread_group.privileged_access_group.object_id
}



Expose access through Access Packages


In the previous steps, we have:

  • Created an Entra ID group

  • Granted the group eligible access to multiple privileged roles through PIM

  • Configured the PIM policyFour hour duration, with or without approval

Now comes the most important part for the end user experience:


How do users get access to the group without IT having to add them manually?

The answer is Access Packages in Entra ID.


The first time you create an access package, you also need to create an access package catalog. In other words, where does the package belong?


resource "azuread_access_package_catalog" "privileged_catalog" {
  display_name = "Privileged Access - Aassveen IT admin"
  description  = "Tilgang til privilegerte Entra ID-roller via PIM"
}

Next, we create the access package itself.

resource "azuread_access_package" "privileged_roles" {
  display_name = "Privileged Entra Roles - Aassveen IT admin"
  description  = "Gir tilgang til privilegert gruppe som brukes for PIM-roller"
  catalog_id   = azuread_access_package_catalog.privileged_catalog.id
}

Once that is done, the group must be linked to the access package.

Støttes pt. ikke og må gjøres i GUI

As the final step, we need to create an access package policy that defines who can request access and how.


We create a policy that controls:

  • Who is allowed to request access

  • Whether access is time limited

  • Whether approval is required


In my case, I create a policy that does not require approval.

resource "azuread_access_package_assignment_policy" "self_service_policy" {
  access_package_id = azuread_access_package.privileged_roles.id
  display_name      = "Privileged access – self service"
  description       = "Self-service tilgang til privilegert gruppe"

  requestor_settings {
    scope_type = "AllExistingDirectoryMemberUsers"
  }
}

With the setup defined, we are ready to deploy this to productionTesting is for cowards. Or is it? 🐣

From the terminal in VS Code


terraform init
terraform plan
terraform apply


Now that the code has been executed, the group has been created, the group has been assigned roles, and the access package has been published. I had to configure the package assignment and the duration of the PIM roles, with and without approval, manually in the GUI, as this is not currently supported.


For the end user, the flow will then be:


Click on the access package and request access. Here I have to cheat a bit, since the Easter Bunny ran off with my license, so I add my user to the group directly from Entra ID.

Now that my user account has been granted access to the group, and the group in turn has access to the roles through PIM, I can elevate myself through “My roles”.



In the end, I am left with a solution that is largely Infrastructure as Code, but with a few deliberate manual steps, especially around PIM policies, duration, and certain details in Access Packages. Terraform gives us structure, predictability, and a solid framework, while the most sensitive settings are configured once in the portal. Not perfect, but far better than click, click, forgot why.


And when az login, Graph, and Identity Governance decided to be difficult, I ended up performing a classic Ingrid Espelid maneuver: “a little more of this, and it will work”, in the form of granting myself the Identity Governance Administrator role. Not exactly by the book, but dinner was served. As a first encounter with Terraform and Entra ID? Absolutely approved, and quite educational along the way. Doc: Docs overview | hashicorp/azuread | Terraform | Terraform Registry


Bjørnar&AI

 
 
 

Comments


bottom of page