Skip to content

Commit 4ee94dc

Browse files
add gdap age group option.
1 parent 99787a0 commit 4ee94dc

3 files changed

Lines changed: 52 additions & 4 deletions

File tree

Modules/CIPPCore/Public/TenantGroups/Test-CIPPDynamicGroupFilter.ps1

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ function Test-CIPPDynamicGroupFilter {
2727
[hashtable]$TenantGroupMembersCache = @{}
2828
)
2929

30-
$AllowedOperators = @('eq', 'ne', 'like', 'notlike', 'in', 'notin', 'contains', 'notcontains')
31-
$AllowedProperties = @('delegatedAccessStatus', 'availableLicense', 'availableServicePlan', 'tenantGroupMember', 'customVariable')
30+
$AllowedOperators = @('eq', 'ne', 'like', 'notlike', 'in', 'notin', 'contains', 'notcontains', 'gt', 'ge', 'lt', 'le')
31+
$AllowedProperties = @('delegatedAccessStatus', 'availableLicense', 'availableServicePlan', 'tenantGroupMember', 'customVariable', 'gdapRelationshipAge')
3232

3333
# Regex for sanitizing string values - block characters that enable code injection
3434
$SafeValueRegex = [regex]'^[^;|`\$\{\}\(\)]*$'
@@ -202,6 +202,22 @@ function Test-CIPPDynamicGroupFilter {
202202
}
203203
}
204204
}
205+
'gdapRelationshipAge' {
206+
if ($OperatorLower -notin @('eq', 'ne', 'gt', 'ge', 'lt', 'le')) {
207+
Write-Warning "Unsupported operator '$OperatorLower' for gdapRelationshipAge"
208+
return $null
209+
}
210+
$RawDays = if ($null -ne $Value.value) { $Value.value } else { $Value }
211+
$Days = 0
212+
if (-not [int]::TryParse([string]$RawDays, [ref]$Days) -or $Days -lt 0) {
213+
Write-Warning "Blocked invalid day count in gdapRelationshipAge rule: '$RawDays'"
214+
return $null
215+
}
216+
# Tenants without an active GDAP relationship have a $null age and never match an
217+
# age rule - without the null guard, '$null -lt 14' would be true and direct
218+
# tenants would permanently land in every 'younger than' group.
219+
return "(`$null -ne `$_.gdapRelationshipAgeDays -and `$_.gdapRelationshipAgeDays -$OperatorLower $Days)"
220+
}
205221
default {
206222
Write-Warning "Unknown property type: $Property"
207223
return $null

Modules/CIPPCore/Public/TenantGroups/Update-CIPPDynamicTenantGroups.ps1

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ function Update-CIPPDynamicTenantGroups {
4343
$TotalMembersAdded = 0
4444
$TotalMembersRemoved = 0
4545
$GroupsProcessed = 0
46+
$GdapAgeByCustomer = $null
47+
$GdapFetchAttempted = $false
4648

4749
# Pre-load tenant group memberships for tenantGroupMember rules
4850
# This creates a cache to avoid repeated table queries during rule evaluation
@@ -69,8 +71,37 @@ function Update-CIPPDynamicTenantGroups {
6971
$RequiresLicense = $Rules.property -contains 'availableLicense'
7072
$RequiresCustomVariables = $Rules.property -contains 'customVariable'
7173
$RequiresServicePlans = $Rules.property -contains 'availableServicePlan'
74+
$RequiresGdapAge = $Rules.property -contains 'gdapRelationshipAge'
7275
Write-Information "Processing $($Rules.Count) rules for group '$($Group.Name)'"
7376

77+
if ($RequiresGdapAge) {
78+
if (-not $GdapFetchAttempted) {
79+
$GdapFetchAttempted = $true
80+
try {
81+
$Relationships = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships?`$top=300" -tenantid $env:TenantID -NoAuthCheck $true -ComplexFilter
82+
$Now = (Get-Date).ToUniversalTime()
83+
$AgeMap = @{}
84+
foreach ($Relationship in $Relationships) {
85+
$RelCustomerId = [string]$Relationship.customer.tenantId
86+
if ([string]::IsNullOrEmpty($RelCustomerId) -or -not $Relationship.activatedDateTime) { continue }
87+
if ($Relationship.status -in @('terminated', 'expired')) { continue }
88+
$AgeDays = [int][math]::Floor(($Now - ([datetime]$Relationship.activatedDateTime).ToUniversalTime()).TotalDays)
89+
# Oldest active relationship wins - relationship age should not reset
90+
# when an additional/replacement relationship is accepted later.
91+
if (-not $AgeMap.ContainsKey($RelCustomerId) -or $AgeMap[$RelCustomerId] -lt $AgeDays) {
92+
$AgeMap[$RelCustomerId] = $AgeDays
93+
}
94+
}
95+
$GdapAgeByCustomer = $AgeMap
96+
} catch {
97+
Write-LogMessage -API 'TenantGroups' -message "Failed to get GDAP relationships for dynamic group evaluation: $((Get-NormalizedError -Message $_.Exception.Message))" -sev Error
98+
}
99+
}
100+
if ($null -eq $GdapAgeByCustomer) {
101+
throw 'Could not retrieve GDAP relationships, skipping this group so existing membership is preserved.'
102+
}
103+
}
104+
74105
$TenantObj = foreach ($Tenant in $AllTenants) {
75106
$LicenseInfo = $null
76107
$SKUId = @()
@@ -126,6 +157,7 @@ function Update-CIPPDynamicTenantGroups {
126157
servicePlans = $ServicePlans
127158
delegatedPrivilegeStatus = $Tenant.delegatedPrivilegeStatus
128159
customVariables = $TenantVariables
160+
gdapRelationshipAgeDays = if ($GdapAgeByCustomer -and $GdapAgeByCustomer.ContainsKey([string]$Tenant.customerId)) { $GdapAgeByCustomer[[string]$Tenant.customerId] } else { $null }
129161
}
130162
}
131163
# Evaluate rules safely using Test-CIPPDynamicGroupFilter with AND/OR logic

Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecTenantGroup.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ function Invoke-ExecTenantGroup {
2626

2727
# Validate dynamic rules to prevent code injection
2828
if ($groupType -eq 'dynamic' -and $dynamicRules) {
29-
$AllowedDynamicOperators = @('eq', 'ne', 'like', 'notlike', 'in', 'notin', 'contains', 'notcontains')
30-
$AllowedDynamicProperties = @('delegatedAccessStatus', 'availableLicense', 'availableServicePlan', 'tenantGroupMember', 'customVariable')
29+
$AllowedDynamicOperators = @('eq', 'ne', 'like', 'notlike', 'in', 'notin', 'contains', 'notcontains', 'gt', 'ge', 'lt', 'le')
30+
$AllowedDynamicProperties = @('delegatedAccessStatus', 'availableLicense', 'availableServicePlan', 'tenantGroupMember', 'customVariable', 'gdapRelationshipAge')
3131
foreach ($rule in $dynamicRules) {
3232
if ($rule.operator -and $rule.operator.ToLower() -notin $AllowedDynamicOperators) {
3333
return ([HttpResponseContext]@{

0 commit comments

Comments
 (0)