Application Scoping Filter Evaluated Incorrectly by Incremental Provisioning Cycle

Anil Mawji 0 Reputation points
2023-12-17T03:25:33.9333333+00:00

I am using a scoping filter on top of users/groups to control provisioning to an enterprise application called Keeper Password Manager - following this tutorial.

To apply the scoping filter, I deployed a function app which ties a custom extension attribute on each user to membership in a specific security group. The function executes every 5 minutes, and essentially updates the extension attribute on users who are in the group to TRUE, and updates the extension attribute on users who are NOT in the group to NULL.

Intended sequence of steps when a user is added to the security group:

  1. Function executes: extension attribute is set to TRUE for user
  2. Application provisioning cycle runs: scoping filter evaluates to TRUE for user since extension attribute is TRUE for the user
  3. User is granted access to the app

Intended sequence of steps when a user is removed from the security group:

  1. Function executes: extension attribute is set to NULL for user
  2. Application provisioning cycle runs: scoping filter evaluates to FALSE for user since extension attribute is NULL for the user
  3. User is deprovisioned from the app

All steps except steps 2 and 3 of removing a user from the group behave as intended. When I remove a user from the security group, the function sets the extension attribute to NULL as intended, but the scoping filter still passes and thus the user is never actually deprovisioned from the app.

Scoping filter for the app:

User's image

Provisioning details of the app:

User's image

Code for the function app:

Import-Module -Name Microsoft.Graph.Authentication
Import-Module -Name Microsoft.Graph.Users
Import-Module -Name Microsoft.Graph.Groups
Import-Module -Name Microsoft.Graph.Applications

Connect-MgGraph -Identity -NoWelcome

# Name of the extension attribute
$extensionPropertyName = "extension_d21b98d1dac8483bad5684b1cc44ceb8_KeeperLicense"
# Security group object id
$keeperPasswordManagerUsersGroupId = "8e2ace19-422a-4d02-a5d8-1faf01a65777"

$tobelicensedUsers = Get-MgGroupMember -GroupId $keeperPasswordManagerUsersGroupId -All | Foreach-Object { ,$_.Id }
$keeperEnabledUsers = Get-MgUser -Filter "extension_d21b98d1dac8483bad5684b1cc44ceb8_KeeperLicense eq true" -All | Foreach-Object { ,$_.Id }

if ($keeperEnabledUsers -eq $null) { $keeperEnabledUsers = @() }

# Ids of users that need to be provisioned
$add = Compare-Object $tobelicensedUsers $keeperEnabledUsers | Where-Object { $_.SideIndicator -eq '<=' } | Foreach-Object { $_.InputObject }

# Ids of users that need to be deprovisioned
$remove = Compare-Object $tobelicensedUsers $keeperEnabledUsers | Where-Object { $_.SideIndicator -eq '=>' } | Foreach-Object { $_.InputObject }

# Update attribute to true for users that need provisioning
$add | Foreach-Object `
{
    $json = '{ "extension_d21b98d1dac8483bad5684b1cc44ceb8_KeeperLicense": true }'
    Invoke-MgGraphRequest -Method PATCH "https://graph.microsoft.com/v1.0/users/$_" -Body $json -Debug
}
# Update attribute to null for users that need deprovisioning
$remove | Foreach-Object `
{
    $json = '{ "extension_d21b98d1dac8483bad5684b1cc44ceb8_KeeperLicense": null }'
    Invoke-MgGraphRequest -Method PATCH "https://graph.microsoft.com/v1.0/users/$_" -Body $json
}

$groups = Get-MgGroup -All
# Groups that have been assigned to the application
$keeperUsers = $groups | Where-Object { $_.DisplayName -like "Keeper Password Manager Users" }
$projectGroups = $groups | Where-Object { $_.DisplayName -like "PG*" }
$filteredGroups = $keeperUsers + $projectGroups

$applicationObjectId = "ba0bd903-e02c-4bee-b2d8-1ae81c3d0a6c"
$userRoleId = "63a9859a-24d2-4d35-a412-b97549968424"

$existingAssignments = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $applicationObjectId -All | Where-Object { $_.PrincipalType -eq 'Group' } | Foreach-Object { ,$_.PrincipalId }

$toAssign = $filteredGroups | Where { -not $existingAssignments -contains $_.Id }
# Provision users with the user role
$toAssign | ForEach-Object { New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $applicationObjectId -ResourceId $applicationObjectId -PrincipalId $_.Id -AppRoleId $userRoleId }



My user account is not in the security group:

User's image

Function app executes successfully without errors:

User's image

Extension attribute is updated as intended by the function, as tested with the following command:

Get-MgUser -Filter "extension_d21b98d1dac8483bad5684b1cc44ceb8_KeeperLicense eq true" -All | Foreach-Object { ,$_.UserPrincipalName }

  • My user account "******@company.com" is not present in the list as expected

Provisioning logs reveals my user is "Skipped" when I should actually be deprovisioned, because Azure incorrectly thinks that the attribute is still set to TRUE even though it is NOT.User's image

Microsoft Security | Microsoft Entra | Microsoft Entra ID
{count} votes

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.