Microsoft Recommends the UnifiedRoleDefinition Graph API for Role Assignment Automation
A New Graph API to Replace Two Existing APIs
The Graph change log update posted on October 21, 2024 contains a simple and blunt recommendation for developers to use the unifiedRoleDefinition Graph resource type (API) instead of the directoryRole and directoryRoleTemplate resource types (Figure 1). Sounds good. Using one API to replace two should simplify development, and the new API delivers more information about Entra ID roles.
The Impact on PowerShell Scripts
But then you look under the surface and understand that this change affects PowerShell scripts that use Microsoft Graph PowerShell SDK cmdlets like Get-MgDirectoryRole (find Entra ID roles) and Get-MgDirectoryRoleMember (find the membership of a role group). This March 2022 article explains how to use those and other SDK cmdlets to assign roles to user accounts.
In effect, if you want to follow Microsoft’s recommendation, you need to upgrade scripts to use cmdlets like Get-MgRoleManagementDirectoryRoleDefinition instead (or the beta version, which provides more information). There’s no necessity to upgrade scripts for the moment, but this issue could arise in the future if Microsoft decides to deprecate the older APIs. All of which creates the question of why Microsoft didn’t simply upgrade the old APIs instead?
I’m sure that there’s good technical reasons for introducing a new API. It’s cleaner for a start because it avoids all the messiness involved in supporting backwards compatibility for an indeterminate period. Professional developers are used to changing APIs and, while they’ll grumble at the need to do more work, the change will happen. Those who write PowerShell might be less happy. There’s a lot happening in the Entra space to force change in scripts already and introducing another change to the mix now sounds like a bad idea that demonstrates a certain lack of sympathy for part-time PowerShell developers, many of whom are struggling to move over to the Graph SDK.
Example PowerShell Cmdlet Changes
Let’s look at the script code changes that might be required. I’ll use the code in the March 2022 article as the base. First, we find the set of Entra ID roles in the tenant.
[array]$DirectoryRoles = Get-MgDirectoryRole | Sort-Object DisplayName
The equivalent with the unifiedRoleDefinition API is:
[array]$DirectoryRoles = Get-MgRoleManagementDirectoryRoleDefinition | Sort-Object DisplayName
In my tenant, the first command returns 36 roles, the second 113. This is because the new API returns all roles and role templates.
Interestingly, the Graph X-Ray tool reveals that the Entra admin center uses the beta version of the cmdlet (Get-MgBetaRoleManagementDirectoryRoleDefinition) when it fetches roles for the All roles page.
Each role has an identifier which is needed to find information about role assignments. To find the identifier for a role, use the role name to filter the set of roles fetched above. For instance, here’s how to find the identifier for the Teams administrator role. This code remains the same:
$TeamsAdminRoleId = $DirectoryRoles | Where-Object {$_.DisplayName -eq "Teams administrator"} | Select -ExpandProperty Id
Note that the two APIs return different identifiers for roles. If you have hard-coded role identifiers in scripts, the values will need to be updated to match the API.
We want to make a new role assignment to add a user account to the role. To start, we find the set of current role members. With the old API, the command is:
[array]$RoleMembers = Get-MgDirectoryRoleMember -DirectoryRoleId $TeamsAdminRoleId
With the new, the command is:
[array]$RoleMembers = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$TeamsAdminRoleId'" -ExpandProperty "principal"
One thing to note is that the information returned for role assignments in the older API only includes user accounts, and the additionalProperties property includes details of the accounts, which means that you can do something like this to see the names of the assignees:
ForEach ($Member in $RoleMembers) { Write-Output $Member.AdditionalProperties.displayName }
This doesn’t happen with the new API. I think two reasons exist why this is so. First, the API includes service principals in role assignments, something that’s important for objects like Azure automation accounts used to execute runbooks or Entra registered apps that need to act in an administrative role. In the assignments listed in Figure 2, only two are user accounts.. Second, the API includes support for scoping (full directory, administrative units, or applications).
Making a Role Assignment
The last example from the article is to assign a role to a user account. The code first checks if the user account already has an assignment, and if not, calls the New-DirectoryRoleMemberByRef cmdlet to make the assignment:
If ($User.Id -notin $RoleMembers.Id) { Write-Host ("Adding user {0} to the Teams administrator role" -f $User.DisplayName) New-MgDirectoryRoleMemberByRef -DirectoryRoleId $TeamsAdminRoleId -BodyParameter @{"@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($user.Id)"}
To make a role assignment with the new API, create a hash table to hold the parameters, including the identifier for the role to assign, the identifier of the account or service principal to receive the role assignment, and the directory scope. In this case, the scope is “/”, meaning the entire directory. If you want to limit the role assignment to specific administrative units, include the identifiers for the administrative units instead.
If ($User.Id -notin $RoleMembers.Id) { $Assignment = @{} $Assignment.Add("@odata.type","#microsoft.graph.unifiedRoleAssignment") $Assignment.Add("roleDefinitionId", $TeamsAdminRoleId) $Assignment.Add("principalId", $User.Id) $Assignment.Add("directoryScopeId", "/") New-MgRoleManagementDirectoryRoleAssignment -BodyParameter $Assignment } Id PrincipalId RoleDefinitionId DirectoryScopeId AppScopeId -- ----------- ---------------- ---------------- ---------- 3ywjKSOT_UKt4h0JevPk3oeS4FkbrPdPgKMI0NHu2Tk-1 59e09287-ac1b-4ff7-80a3-08d0d1eed939 29232cdf-9323-42fd-ade2-1d097af3e4de / $Assignment Name Value ---- ----- roleDefinitionId 4c962061-2581-417f-938a-7cc1b38fc2a2 directoryScopeId / principalId 59e09287-ac1b-4ff7-80a3-08d0d1eed939 @odata.type #microsoft.graph.unifiedRoleAssignment
In terms of Privileged Identity Management, the resulting assignment is active and permanent, so the new holder can use it immediately and will show up in administrative interfaces as a role holder (and in scripts like my reporting administrative role assignments script, which uses the unifiedRoleDefinition API).
More to Come
There’s lots more to investigate in terms of exploiting the UnifiedRoleDefinition API to automate the management of Entra role assignments. We’ll get to that in future articles. In the meantime, the message is that Microsoft is focused on using the UnifiedRoleDefinition API going forward, so that’s where your focus should be too.
Need more advice about how to write PowerShell for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.