When I last wrote about the new msgraph provider for Terraform my conclusion was that the preview release was not yet really usable, partly due to missing features. Well, one month later, and version 0.2.0 was shipped this week with one major update to change a lot of that and help us solve some (imho) pretty big problems with the old azuread provider
the new msgraph_update_resource function!
While the first release provided support for HTTP GET and POST requests, the new msgraph_update_resource adds support for sending HTTP PATCH requests to the API. This greatly expands the coverage of endpoints we can now manage, and most importantly for this post: we can now configure role settings used in Privileged Identity Management to configure requirements for users who need to elevate their permissions to Entra ID roles.
PIM explained like you're 5
If you're not familiar with the concept of Privileged Identity Management in Entra ID or Azure, it is functionality that lets you avoid assigning privileged (i.e. dangerous) permissions to users and have them active 24/7. Instead, you can let your users elevate their permissions when they need them for specific tasks.
You can use PIM for group memberships, Azure roles, and Entra ID roles. e.g you can have a company policy that every developer only has read access to Azure, but the possibility to become contributor/owner for a day if there is some kind of issue that requires it. Their request will be logged and access is removed when the timeslot expires.
The gap in existing providers
We have had support in terraform-provider-azuread and terraform-provider-azurerm for configuring more specific role settings with azuread_group_role_management_policy or azurerm_role_management_policy, but for some unknown reasons there has not been any support for doing the same with Entra ID roles.
Considering that roles like Global Administrator or Security Administrator easily are the ones with most potential to cause harm if compromised, this has always felt a bit strange for me. If there is one place I would start with requiring stronger authentication contexts, just in time access, potentially a required approval and notifications on use, this would be it.
Configuring role settings with msgraph
Working with the msgraph provider is similar to working with azapi for Azure resources and takes a bit of mental effort to get used to. Mostly because the API is so large, and not everything is very easy to find.
The first thing we need to do is to set up two data sources to get the required information for where we can apply our update with new settings:
data "msgraph_resource" "role" {
url = "roleManagement/directory/roleDefinitions"
query_parameters = {
"$filter" = ["displayName eq 'Global Administrator'"]
}
response_export_values = {
id = "value[0].id"
}
} First, we need to query the API to get the role definition id for the Entra ID role we want to configure. In this case, our prime suspect, the Global Administrator role.
We can then use this id to get our second data source:
data "msgraph_resource" "role_assignment_policy" {
url = "policies/roleManagementPolicyAssignments"
query_parameters = {
"$filter" = ["scopeId eq '/' and scopeType eq 'DirectoryRole' and roleDefinitionId eq '${data.msgraph_resource.role.output.id}'"]
}
response_export_values = {
policy_id = "value[0].policyId"
}
} A note to observe is that we are required to add additional filters to our query in addition to only returning the role definition id we are interested in. More information is available in the API reference (list roleManagementPolicies - Query Parameters)
With this second datasource we now have the policyId we need to add to the URL in our msgraph_update_resource block where we apply our new settings:
resource "msgraph_update_resource" "policy_rules" {
url = "policies/roleManagementPolicies/${data.msgraph_resource.role_assignment_policy.output.policy_id}"
body = {
rules = [
{
"@odata.type" = "#microsoft.graph.unifiedRoleManagementPolicyExpirationRule"
id = "Expiration_EndUser_Assignment"
maximumDuration = "PT4H"
}
]
}
} In our simple example we just modify the maximum duration of a users request to be Global Administrator from the default 8 hours to 4 hours. To provide a valid policy rule we need to supply the @odata.type, id and required parameters for the specific id. To learn more about the various types of rules and their expected formats I recommend to read this: Update unifiedRoleManagementPolicyRule as there are many other options we can configure. Like requiring approvals on user requests, specifying authentication context to require use of passkeys, max expiration on assignments, and where notifications should be sent.
And lastly. We can make life a bit easier for ourself and avoid copying&pasting these 3 blocks multiple times in our codebase and encapsulate it in a terraform module. I have published a very basic module in Terraform Registry that requires a role name and a list of rules as parameters.