Assigning PIM Azure RBAC permissions using Terraform and ARM template

Currently, Terraform does not support eligible assignments of permissions in Azure RBAC, and only active assignments using the azurerm_role_assignment resource. Continue reading if you want to be able to assign your eligible assignments using ARM or Terraform (Terraform willl use the ARM template).

With the 3rd version of the PIM APIs, we have something called Role Eligibility Schedule Request, available through documented through the API documentation and the ARM documentation. However, the documentation can be a bit difficult to understand, especially because the roleDefinitionId in the ARM template must be provided differently than the when using the API.

Let’s first define an ARM template, as below, that can be used to assign eligible permissions:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        // Service principal objectid or user objectid
        "principalId": {
            "type": "string"
        },
        // Full role definition id, such as Contributor: subscriptions/1232951b-df54-45eb-9c08-a8c93ea18306/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c
        "roleDefinitionId": {
            "type": "string"
        },
        // Random unique GUID, to identify the request
        "id": {
            "type": "string",
            "defaultValue": "[newGuid()]"
        },
        // Random unique GUID, to identify the request
        "requestType": {
            "type": "string",
            "defaultValue": "AdminUpdate"
        }
    },
    "outputs": {},
    "resources": [
        {
            "type": "Microsoft.Authorization/roleEligibilityScheduleRequests",
            "apiVersion": "2020-10-01-preview",
            "name": "[parameters('id')]",
            "properties": {
                "principalId": "[parameters('principalId')]",
                "roleDefinitionId": "[parameters('roleDefinitionId')]",
                "requestType": "[parameters('requestType')]",
                "scheduleInfo": {
                    "startDateTime": "2021-10-13T08:32:07.0706351+02:00",
                    "expiration": {
                        "type": "AfterDuration",
                        "duration": "P365D"
                    }
                }
            }
        }
    ]
}

Parameter – principalId

This should be the objectid of the principal you are granting the access to. If you are assigning permissions to user IrvinS@M365x942777.OnMicrosoft.com, use the following value:

Parameter – roleDefinitionId

I spent way too much time to figure out the format of the value for this parameter, but it should be like this:

subscriptions/1272951b-df54-45eb-9c08-a8c93ea18302/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c

The first guid (1272951b-df54-45eb-9c08-a8c93ea18302) should be changed to the subscription id of your subscription, while the second guid (b24988ac-6180-42a0-ab88-20f7382dd24) is the Azure RBAC role id, found here. The example provided is “Contributor”.

Parameter – id

All eligible schedule requests have a unique ID, defined client side, so this should basically just be a unique guid. The ARM template generates ut automatically.

Parameter – requestType

I have defaulted this to AdminUpdate, as that will also work with new assignments. However, due to how PIM works, in order to actually remove an assignment, you must deploy the ARM template with the value “AdminRemove”. This is super anoying, from a Terraform perspective. Also, when deploying with AdminRemove for a second time, it fails with RoleAssignmentDoesNotExist.

Deploying using ARM template

Here is how to deploy eligible contributor permission to a user with objectid e9176fb9-63d3-480a-a51f-e5399059b588 on subscription level:

az deployment sub create --template-file .\pim_assignment.json --parameters principalId=e9176fb9-63d3-480a-a51f-e5399059b588 roleDefinitionId=subscriptions/4272951b-df54-45eb-9c08-a8c93ea18306/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c --name "contributor_assignment_1" --location westeurope

And this is how to do the same thing on resource group level:

az deployment group create --resource-group mygroup --template-file .\pim_assignment.json --parameters principalId=e9176fb9-63d3-480a-a51f-e5399059b588 roleDefinitionId=subscriptions/4272951b-df54-45eb-9c08-a8c93ea18306/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c --name "contributor_assignment_1"

Now that we have things going with ARM template, let’s do it with Terraform aswell.

Deploying using Terraform

Some say it’s cheating, but we need to use the ARM template here aswell. This is because currently no Terraform resource exist for eligible role assignments.

Also, Terraform does not support comments in JSON documents, so remove them before saving the file.

Go to https://github.com/goodworkaround/terraform-az-rbac-pim-assignment and clone my Terraform example. The example uses the Azure AD provider to create groups, which is not necessary. You can get away with only the AzureRM provider, but then you need to specify the objectids of the principals you are granting access to.

There are two modules available:

PIM Assignment – Subscription

The following code will create an Azure AD group called “subscription_owner_group_1” and assign it eligible “Owner”

// Example for subscription owner as PIM role
resource "azuread_group" "subscription_owner_group_1" {
    display_name     = "subscription_owner_group_1"
    mail_enabled     = false
    security_enabled = true
}

module "pim_assignment_1" {
    source = "./PIM Assignment - Subscription"

    principal_id = azuread_group.subscription_owner_group_1.object_id
    role_definition_name = "Owner"
}

As an admin in PIM, you will find the following assignment:

Members of the group should see this in PIM:

It is worth noting that simply unloading the module will not remove the assignment. Instead, the module must be used with request_type = “AdminRemove”, as it will remove the permission. Then you can remove the module.

PIM Assignment – Resource Group

The following code will create an Azure AD group called “rg_contributor_group_1”, a resource group “rg1” and delegate the group eligible Contributor on the resource group:

// Example for contributor role on resource group
resource "azuread_group" "rg_contributor_group_1" {
    display_name     = "rg_contributor_group_1"
    mail_enabled     = false
    security_enabled = true
}

resource "azurerm_resource_group" "rg1" {
    name = "rg1"
    location = "westeurope"
}

module "pim_assignment_2" {
    source = "./PIM Assignment - Resource Group"

    resource_group_name  = azurerm_resource_group.rg1.name
    principal_id         = azuread_group.rg_contributor_group_1.object_id
    role_definition_name = "Contributor"
}

As an admin in PIM, you will find the following assignment:

Members of the group should see this in PIM:

It is worth noting that simply unloading the module will not remove the assignment. Instead, the module must be used with request_type = “AdminRemove”, as it will remove the permission. Then you can remove the module.

Have fun!

16 thoughts on “Assigning PIM Azure RBAC permissions using Terraform and ARM template

  1. Error: removing items provisioned by this Template Deployment: `properties.OutputResources` was nil – insufficient data to clean up this Template Deployment

  2. one more thing I noticed, after assigning az resource PIM role to principleID, If I run the same release again, it fails with error that {
    “code”: “Conflict”,
    “message”: “A role assignment request with Id: a2d47b66-96d6-16d4-5b35-29b3139cae94 already exists”
    }
    . I thought it would just pass through

  3. Will this work for ADRoles like “User Administrator” or “Application Administrator” . I am trying to work make PIM for Azure AD roles

      1. That is right, also make sure yours SPN has the right api permissions and Rbac to do this via code for Az Resource Roles or AD roles 🙂
        For Resource Roles I was able to get it working with User Access Admin on the subscription as well as Contributor Access
        with User.Read, PrivilegedAccess.Read.AzureResources and PrivilegedAccess.Write.AzureResources access with Grant persimmons as least .

  4. Great solution!
    A couple of questions if you don’t mind!

    1/ we were thinking to use ‘az rest’ to achieve the same but all of our testing using:
    https://docs.microsoft.com/en-us/rest/api/authorization/role-eligibility-schedule-requests/create#requesttype

    with basically an identical payload it didn’t create the assignment, it left it in a strange ‘pending’ state

    any thoughts on why this might be?

    2/ any ideas on setting the role policy via ARM or REST? eg MFA required, 4 hours, etc… it uses PATCH method:
    https://docs.microsoft.com/en-us/rest/api/authorization/privileged-role-policy-rest-sample#update-a-role-management-policy

  5. This is great solution but I am facing an issue when running AdminRemove on resource group template, it fails by saying below error:

    Error: removing items provisioned by this Template Deployment: deleting Nested Resource “/subscriptions/***/resourceGroups/***/providers/Microsoft.Authorization/roleEligibilityScheduleRequests/***”: resources.Client#DeleteByID: Failure sending request: StatusCode=405 — Original Error: Code=”Failed” Message=”The async operation failed.” AdditionalInfo=[{“message”:”The requested resource does not support http method ‘DELETE’.”}]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s