Skip to content

Commit ba8232e

Browse files
committed
fix(cippdb): cache PIM role data with app token
Add the required RoleManagement permissions to SAMManifest and update PIM cache jobs to use app-only Graph calls (`-AsApp`) so role APIs stop failing with unauthorized delegated access. Role assignment and eligibility schedule caching now expands `principal` to preserve member identity fields, and role management policy caching now reads `roleManagementPolicyAssignments` (with required filter/expands) and flattens policy rules/effectiveRules alongside `roleDefinitionId` for role-to-policy lookups.
1 parent 45bd6c9 commit ba8232e

4 files changed

Lines changed: 76 additions & 5 deletions

File tree

Config/SAMManifest.json

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,34 @@
582582
{
583583
"id": "b7887744-6746-4312-813d-72daeaee7e2d",
584584
"type": "Scope"
585+
},
586+
{
587+
"id": "dd199f4a-f148-40a4-a2ec-f0069cc799ec",
588+
"type": "Role"
589+
},
590+
{
591+
"id": "8c026be3-8e26-4774-9372-8d5d6f21daff",
592+
"type": "Scope"
593+
},
594+
{
595+
"id": "fee28b28-e1f3-4841-818e-2704dc62245f",
596+
"type": "Role"
597+
},
598+
{
599+
"id": "62ade113-f8e0-4bf9-a6ba-5acb31db32fd",
600+
"type": "Scope"
601+
},
602+
{
603+
"id": "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8",
604+
"type": "Role"
605+
},
606+
{
607+
"id": "31e08e0a-d3f7-4ca2-ac39-7343fb83e8ad",
608+
"type": "Role"
609+
},
610+
{
611+
"id": "1ff1be21-34eb-448c-9ac9-ce1f506b2a68",
612+
"type": "Scope"
585613
}
586614
]
587615
},
@@ -689,4 +717,4 @@
689717
"isEnabled": true,
690718
"allProperties": true
691719
}
692-
}
720+
}

Modules/CIPPDB/Public/DBCache/Set-CIPPDBCacheRoleAssignmentScheduleInstances.ps1

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,17 @@ function Set-CIPPDBCacheRoleAssignmentScheduleInstances {
1818

1919
try {
2020
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching role assignment schedule instances' -sev Debug
21-
New-GraphGetRequest -Uri 'https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleInstances' -tenantid $TenantFilter -Stream |
21+
# -AsApp is required: the RoleManagement.*.Directory grant is an APPLICATION permission,
22+
# so it only appears in an app-only token. The default delegated path (service account +
23+
# GDAP) fails with "Attempted to perform an unauthorized operation" because PIM reads via
24+
# delegated access additionally need the signed-in user to hold a directory role such as
25+
# Privileged Role Administrator, which GDAP does not grant.
26+
# $expand=principal is required by Get-CippDbRoleMembers, which reads
27+
# $member.principal.displayName/.userPrincipalName/.'@odata.type'. The API returns only
28+
# principalId (a GUID) by default, so without this those all resolve to $null and every
29+
# role member surfaces with a blank name.
30+
$Uri = 'https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleInstances?$expand=principal'
31+
New-GraphGetRequest -Uri $Uri -tenantid $TenantFilter -AsApp $true -Stream |
2232
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'RoleAssignmentScheduleInstances' -AddCount
2333

2434
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Cached role assignment schedule instances successfully' -sev Debug

Modules/CIPPDB/Public/DBCache/Set-CIPPDBCacheRoleEligibilitySchedules.ps1

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ function Set-CIPPDBCacheRoleEligibilitySchedules {
1818

1919
try {
2020
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching role eligibility schedules' -sev Debug
21-
New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/roleManagement/directory/roleEligibilitySchedules' -tenantid $TenantFilter -Stream |
21+
# -AsApp is required: RoleManagement.*.Directory is an APPLICATION permission and only
22+
# lands in an app-only token. The default delegated path returns "unauthorized".
23+
# $expand=principal — see the note in Set-CIPPDBCacheRoleAssignmentScheduleInstances;
24+
# Get-CippDbRoleMembers reads principal.displayName off these records too.
25+
$Uri = 'https://graph.microsoft.com/beta/roleManagement/directory/roleEligibilitySchedules?$expand=principal'
26+
New-GraphGetRequest -uri $Uri -tenantid $TenantFilter -AsApp $true -Stream |
2227
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'RoleEligibilitySchedules' -AddCount
2328

2429
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Cached role eligibility schedules successfully' -sev Debug

Modules/CIPPDB/Public/DBCache/Set-CIPPDBCacheRoleManagementPolicies.ps1

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,36 @@
11
function Set-CIPPDBCacheRoleManagementPolicies {
22
<#
33
.SYNOPSIS
4-
Caches role management policies for a tenant
4+
Caches PIM role management policies for a tenant, keyed by the role they apply to
55
66
.PARAMETER TenantFilter
77
The tenant to cache role management policies for
88
99
.PARAMETER QueueId
1010
The queue ID to update with total tasks (optional)
11+
12+
.NOTES
13+
Reads roleManagementPolicyAssignments, NOT roleManagementPolicies, because only the
14+
assignment carries roleDefinitionId — the policy record does not identify the role it
15+
applies to, and the role id is not derivable from it (its id embeds a policy GUID that
16+
matches no roleTemplateId). Consumers need to find "the policy for role X", so the
17+
assignment is the correct entity.
18+
https://learn.microsoft.com/graph/api/policyroot-list-rolemanagementpolicyassignments
19+
20+
$filter is REQUIRED by this API — it must be scoped to a scopeId and scopeType, and the
21+
request errors without it. rules/effectiveRules are navigation properties on the policy,
22+
so they need a nested $expand; they are absent from the default response and consumers
23+
read both.
24+
25+
The policy is flattened up one level so cached records expose roleDefinitionId alongside
26+
rules/effectiveRules, which is the shape the tests consume. This costs -Stream (the
27+
Select-Object has to materialize), but the result set is small (~144 records/tenant).
28+
29+
-AsApp is required: RoleManagement.*.Directory is an APPLICATION permission and only
30+
lands in an app-only token. The default delegated path returns "unauthorized".
31+
32+
A tenant that has never onboarded PIM returns "MissingProvider: The provider is missing"
33+
regardless of query or permissions. That is tenant state, not a bug in this call.
1134
#>
1235
[CmdletBinding()]
1336
param(
@@ -18,7 +41,12 @@ function Set-CIPPDBCacheRoleManagementPolicies {
1841

1942
try {
2043
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching role management policies' -sev Debug
21-
New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/roleManagementPolicies' -tenantid $TenantFilter -Stream |
44+
45+
$Uri = "https://graph.microsoft.com/beta/policies/roleManagementPolicyAssignments?`$filter=scopeId eq '/' and scopeType eq 'DirectoryRole'&`$expand=policy(`$expand=rules,effectiveRules)"
46+
New-GraphGetRequest -uri $Uri -tenantid $TenantFilter -AsApp $true |
47+
Select-Object -Property policyId, roleDefinitionId, scopeId, scopeType,
48+
@{ Name = 'rules'; Expression = { $_.policy.rules } },
49+
@{ Name = 'effectiveRules'; Expression = { $_.policy.effectiveRules } } |
2250
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'RoleManagementPolicies' -AddCount
2351

2452
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Cached role management policies successfully' -sev Debug

0 commit comments

Comments
 (0)