11function Update-CippSamPermissions {
22 <#
33 . SYNOPSIS
4- Repairs the CIPP-SAM app registration permissions in the partner tenant .
4+ Reconciles the applied CIPP-SAM permission set in the AppPermissions table .
55 . DESCRIPTION
6- Diffs the effective CIPP-SAM permission set (manifest defaults + saved extras) against the live
7- CIPP-SAM application registration in the partner tenant and ADDS any missing permissions to the
8- app registration's requiredResourceAccess. This is additive only: it never removes permissions,
9- so it cannot strip a legitimately-configured entry. Extra permissions found on the app that are
10- not part of the effective set are reported back so an admin can review/remove them manually .
6+ Writes the full applied permission set - the SAM manifest base PLUS any admin-configured extra
7+ permissions - into the AppPermissions table, so the table always reflects everything the
8+ CIPP-SAM app is expected to have. Get-CippSamPermissions diffs the manifest against this table
9+ to decide when a Permissions repair is needed, so persisting the manifest here is what lets that
10+ check clear after a repair .
1111
12- Pushing these permissions out to customer tenants is handled separately by the CPV refresh.
12+ It deliberately does NOT write the partner CIPP-SAM app registration's requiredResourceAccess.
13+ Permissions reach the CIPP-SAM service principal(s) - partner and clients - through the grant
14+ flow (Add-CIPPApplicationPermission / Add-CIPPDelegatedPermission, which read this table), not
15+ through the app registration.
1316 . PARAMETER UpdatedBy
1417 The user or system that is performing the update. Defaults to 'CIPP-API'.
1518 . OUTPUTS
@@ -22,87 +25,69 @@ function Update-CippSamPermissions {
2225 )
2326
2427 try {
25- $CurrentPermissions = Get-CippSamPermissions
26- $PartnerAppDiff = $CurrentPermissions.PartnerAppDiff
27- $MissingPermissions = $CurrentPermissions.MissingPermissions
28+ # Manifest base - the always-required permissions.
29+ $ManifestPermissions = (Get-CippSamPermissions - ManifestOnly).Permissions
2830
29- $MissingAppIds = @ ($MissingPermissions.PSObject.Properties.Name )
30- $ExtraAppIds = @ ($PartnerAppDiff.PSObject.Properties.Name | Where-Object {
31- ($PartnerAppDiff .$_.extraApplicationPermissions | Measure-Object ).Count -gt 0 -or
32- ($PartnerAppDiff .$_.extraDelegatedPermissions | Measure-Object ).Count -gt 0
33- })
34-
35- if ($MissingAppIds.Count -eq 0 ) {
36- if ($ExtraAppIds.Count -gt 0 ) {
37- $ExtraSummary = foreach ($AppId in $ExtraAppIds ) {
38- $Names = @ ($PartnerAppDiff .$AppId.extraApplicationPermissions.value ) + @ ($PartnerAppDiff .$AppId.extraDelegatedPermissions.value )
39- " $AppId ($ ( $Names -join ' , ' ) )"
40- }
41- return " No missing permissions to add. The following extra permissions are present on the app and should be reviewed/removed manually: $ ( $ExtraSummary -join ' ; ' ) "
31+ $Table = Get-CIPPTable - TableName ' AppPermissions'
32+ $SavedRow = Get-CippAzDataTableEntity @Table - Filter " PartitionKey eq 'CIPP-SAM' and RowKey eq 'CIPP-SAM'"
33+ $Saved = $null
34+ if ($SavedRow.Permissions ) {
35+ try {
36+ $Saved = $SavedRow.Permissions | ConvertFrom-Json - ErrorAction Stop
37+ } catch {
38+ $Saved = $null
4239 }
43- return ' No permissions to update'
4440 }
4541
46- # Retrieve the live CIPP-SAM application registration in the partner tenant.
47- $PartnerApp = New-GraphGETRequest - uri " https://graph.microsoft.com/beta/applications(appId='$ ( $env: ApplicationID ) ')?`$ select=id,requiredResourceAccess" - tenantid $env: TenantID - NoAuthCheck $true
42+ # Build the full applied set = manifest base ∪ admin extras, keyed by resource appId.
43+ $Applied = @ {}
44+ $AppIds = @ (@ ($ManifestPermissions.PSObject.Properties.Name ) + @ ($Saved.PSObject.Properties.Name )) | Where-Object { $_ } | Sort-Object - Unique
45+ foreach ($AppId in $AppIds ) {
46+ $ManifestApp = $ManifestPermissions .$AppId
47+ $SavedApp = $Saved .$AppId
48+ $ManifestAppIds = @ ($ManifestApp.applicationPermissions.id )
49+ $ManifestDelIds = @ ($ManifestApp.delegatedPermissions.id )
4850
49- $RequiredResourceAccess = [System.Collections.Generic.List [object ]]::new()
50- foreach ($Resource in $PartnerApp.requiredResourceAccess ) {
51- $ResourceAccess = [System.Collections.Generic.List [object ]]::new()
52- foreach ($Access in $Resource.resourceAccess ) {
53- $ResourceAccess.Add (@ { id = $Access.id ; type = $Access.type })
54- }
55- $RequiredResourceAccess.Add ([PSCustomObject ]@ {
56- resourceAppId = $Resource.resourceAppId
57- resourceAccess = $ResourceAccess
58- })
59- }
51+ $AppPerms = [System.Collections.Generic.List [object ]]::new()
52+ $DelPerms = [System.Collections.Generic.List [object ]]::new()
6053
61- $AddedPermissions = [System.Collections.Generic.List [string ]]::new()
62- foreach ($AppId in $MissingAppIds ) {
63- $Resource = $RequiredResourceAccess | Where-Object - Property resourceAppId -EQ $AppId | Select-Object - First 1
64- if (! $Resource ) {
65- $Resource = [PSCustomObject ]@ {
66- resourceAppId = $AppId
67- resourceAccess = [System.Collections.Generic.List [object ]]::new()
54+ # Manifest base (always applied).
55+ foreach ($Permission in $ManifestApp.applicationPermissions ) {
56+ $AppPerms.Add ([PSCustomObject ]@ { id = $Permission.id ; value = $Permission.value })
57+ }
58+ foreach ($Permission in $ManifestApp.delegatedPermissions ) {
59+ $DelPerms.Add ([PSCustomObject ]@ { id = $Permission.id ; value = $Permission.value })
60+ }
61+ # Admin extras (anything the manifest does not already cover).
62+ foreach ($Permission in $SavedApp.applicationPermissions ) {
63+ if ($Permission.id -and $ManifestAppIds -notcontains $Permission.id ) {
64+ $AppPerms.Add ([PSCustomObject ]@ { id = $Permission.id ; value = $Permission.value })
6865 }
69- $RequiredResourceAccess.Add ($Resource )
7066 }
71- $ExistingIds = @ ($Resource.resourceAccess.id )
72-
73- foreach ($Permission in $MissingPermissions .$AppId.applicationPermissions ) {
74- if ($Permission.id -and $ExistingIds -notcontains $Permission.id ) {
75- $Resource.resourceAccess.Add (@ { id = $Permission.id ; type = ' Role' })
76- $AddedPermissions.Add (" $ ( $Permission.value ) (Application)" )
67+ foreach ($Permission in $SavedApp.delegatedPermissions ) {
68+ if ($Permission.id -and $ManifestDelIds -notcontains $Permission.id ) {
69+ $DelPerms.Add ([PSCustomObject ]@ { id = $Permission.id ; value = $Permission.value })
7770 }
7871 }
79- foreach ($Permission in $MissingPermissions .$AppId.delegatedPermissions ) {
80- if ($Permission.id -and $ExistingIds -notcontains $Permission.id ) {
81- $Resource.resourceAccess.Add (@ { id = $Permission.id ; type = ' Scope' })
82- $AddedPermissions.Add (" $ ( $Permission.value ) (Delegated)" )
72+
73+ if ($AppPerms.Count -gt 0 -or $DelPerms.Count -gt 0 ) {
74+ $Applied .$AppId = @ {
75+ applicationPermissions = @ ($AppPerms )
76+ delegatedPermissions = @ ($DelPerms )
8377 }
8478 }
8579 }
8680
87- if ($AddedPermissions.Count -eq 0 ) {
88- return ' No permissions to update'
81+ $Entity = @ {
82+ ' PartitionKey' = ' CIPP-SAM'
83+ ' RowKey' = ' CIPP-SAM'
84+ ' Permissions' = [string ]([PSCustomObject ]$Applied | ConvertTo-Json - Depth 10 - Compress)
85+ ' UpdatedBy' = $UpdatedBy
8986 }
87+ $null = Add-CIPPAzDataTableEntity @Table - Entity $Entity - Force
9088
91- $PatchBody = @ { requiredResourceAccess = @ ($RequiredResourceAccess ) } | ConvertTo-Json - Depth 10 - Compress
92- $null = New-GraphPOSTRequest - uri " https://graph.microsoft.com/beta/applications/$ ( $PartnerApp.id ) " - tenantid $env: TenantID - body $PatchBody - type PATCH - NoAuthCheck $true
93-
94- Write-LogMessage - API ' UpdateCippSamPermissions' - message " CIPP-SAM app registration permissions repaired by $UpdatedBy " - Sev ' Info' - LogData @ { Added = $AddedPermissions }
95-
96- $Result = " Added $ ( $AddedPermissions.Count ) missing permission(s) to the CIPP-SAM app registration: $ ( $AddedPermissions -join ' , ' ) . Run a CPV refresh to apply these to customer tenants."
97- if ($ExtraAppIds.Count -gt 0 ) {
98- $ExtraSummary = foreach ($AppId in $ExtraAppIds ) {
99- $Names = @ ($PartnerAppDiff .$AppId.extraApplicationPermissions.value ) + @ ($PartnerAppDiff .$AppId.extraDelegatedPermissions.value )
100- " $AppId ($ ( $Names -join ' , ' ) )"
101- }
102- $Result += " Extra permissions present on the app that should be reviewed/removed manually: $ ( $ExtraSummary -join ' ; ' ) ."
103- }
104- return $Result
89+ return ' CIPP-SAM permissions reconciled: the applied permission table now contains the CIPP manifest permissions plus any additional permissions.'
10590 } catch {
106- throw " Failed to update permissions: $ ( $_.Exception.Message ) "
91+ throw " Failed to reconcile permissions: $ ( $_.Exception.Message ) "
10792 }
10893}
0 commit comments