Manage Entra Privileged Identity Management with Terraform

Manage Entra Privileged Identity Management with Terraform

Introduction

There is no surprise to anyone who has seen my content before that I kind of love Infrastructure as Code (IaC), I really do. However, there seem to be a lot of people who think only about Azure resources when IaC is introduced into the conversation but that is not the case. We have the ability to also create service principals, users and groups in Microsoft Entra.

This is probably quite known by a lot of people but maybe you are not aware that you can also work the the Entra Privileged Identity Management (PIM) API and configure PIM workflows using Terraform as well.

PIM is a great and in my opinion a mandatory tool for any serious organization using Azure. You have the ability to create just-in-time access and control approval flows and much more using this technology which increases the security and level of control in your environment, reducing your overall attack surface.

It is important to note that there is a way to deal with PIM both in the azurerm provider which deals with PIM and Azure RBAC roles, as well as with the azuread provider which deals with Entra roles and groups. In this post we will focus on the first mentioned provider and look more at azuread provider in a later post.

You can find all the source code for this post in the Github repo HERE

Setting up Terraform

The objective is to create a resource group, assign myself as reader which will be an active assignment as well as assign myself as contributor via PIM where I need to provide justification for activating that role.

We'll setup our module with some pre-requisites. I will create a new folder where I will store my Terraform config: mkdir -p ~/github/azure-terraform-pim

We will go ahead and configure the provider with the following code block inside a new main.tf file which we will create alongside a variables.tf file

In main.tf

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "4.8.0"
    }
  }
}

provider "azurerm" {
  features {}
  subscription_id = var.subscription_id
}

And in variables.tf

variable "subscription_id" {
  type = string
}

variable "environment" {
  type    = string
  default = "dev"
}

variable "location" {
  type    = string
  default = "swedencentral"
}

variable "location_short" {
  type    = string
  default = "se"
}

I have added some defaults, you can skip them if you wish

Once you login to Azure via the AZ CLI we are ready to actually start configuring resources.

Example with the azurerm provider

The resource we are interested in is azurerm_pim_eligible_role_assignment which you can find the documentation on here:

Some common questions you should have answers for when setting up this resource is:

  • Which role should we assign eligible permissions to?
  • At which scope is the permission applied, on the subscription or on a resource group?
  • Who gets the permission?

There are more things we can do such as require MFA, approval, justification and so on, refer to the docs in the link for all the possibilities that exist.

I will update my main.tf file to create:

  • A resource group that I will create permissions for
  • A data block for the current azurerm_client_config and azurerm_subscription
  • Data block for role definition Reader which will be an active permanent assignment, I will use the role definition ID from the data block
  • Data block for role definition Contributor which will be an eligible assignment, I will use the role definition ID from the data block
  • A azurerm_role_management_policy for the Contributor role where we will control how the role may be activated
  • A resource block for the active role assignment of Reader on the resource group scope
  • A resource block for the eligible role assignment of Contributor on the resource group scope

This gives us a new updated main.tf

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "4.8.0"
    }
  }
}

provider "azurerm" {
  features {}
  subscription_id = var.subscription_id
}

data "azurerm_client_config" "current" {}

data "azurerm_subscription" "primary" {}

resource "azurerm_resource_group" "this" {
  name     = "rg-${var.environment}-${var.location_short}-pim"
  location = var.location
}

data "azurerm_role_definition" "reader" {
  name = "Reader"
}

data "azurerm_role_definition" "contributor" {
  name = "contributor"
}

resource "azurerm_role_assignment" "reader" {
  scope                = azurerm_resource_group.this.id
  role_definition_name = "Reader"
  principal_id         = data.azurerm_client_config.current.object_id
}

resource "azurerm_role_management_policy" "contributor" {
  scope              = azurerm_resource_group.this.id
  role_definition_id = data.azurerm_role_definition.contributor.role_definition_id

  eligible_assignment_rules {
    expiration_required = false
  }

  activation_rules {
    maximum_duration      = "PT8H"
    require_approval      = false
    require_justification = true
  }
}

resource "azurerm_pim_eligible_role_assignment" "contributor" {
  scope              = azurerm_resource_group.this.id
  role_definition_id = "${data.azurerm_subscription.primary.id}${data.azurerm_role_definition.contributor.id}"
  principal_id       = data.azurerm_client_config.current.object_id
}

Now deploying this in my CLI and supplying the subscription ID running the command terraform apply -var 'subscription_id=(your-sub-id)' & approving the deployment:

And in the portal if I go to manage Azure Resources and select my subscription and resource group I should see the following direct assignment:

Select Manage resource at the bottom

And from the point of my user if I want to activate my permissions I can do that as well:

Now if you want to tear it all down just run: terraform destroy -var 'subscription_id=(your-sub-id)'

Conclusion

You can control PIM for Azure RBAC role assignments at different scopes. We now require PIM for the Contributor role at a resource group and you need to provide justification before you can activate the role. We have several layers of benefits here:

  • Audit trail inside the Azure Environment
  • Reduced attack surface as the role is not always active
  • Everything is documented in source control and any changes will be documented as well

References

Terraform Registry
GitHub - carlzxc71/azure-terraform-pim: This repository contains some demo code for configuring PIM with Terraform
This repository contains some demo code for configuring PIM with Terraform - carlzxc71/azure-terraform-pim

About me

About me
If you have landed on my page you will have already understood my passion for tech, but obviously there is more to life than that. Here I will try and outline a few of my other hobbies. Strength training I am a person who loves to move around and challenge