Skip to content

Commit 36767b9

Browse files
committed
fix: duplicate subscription bug in graph
deletes duplicate subs from graph and removes corresponding entities from the table
1 parent af110ce commit 36767b9

1 file changed

Lines changed: 39 additions & 4 deletions

File tree

Modules/CIPPCore/Public/Webhooks/New-CIPPGraphSubscription.ps1

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ function New-CIPPGraphSubscription {
8484
$WebhookFilter = "PartitionKey eq '$($TenantFilter)'"
8585
$ExistingWebhooks = Get-CIPPAzDataTableEntity @WebhookTable -Filter $WebhookFilter
8686
$MatchedWebhook = $ExistingWebhooks | Where-Object { $_.Resource -eq $Resource }
87-
if (($MatchedWebhook | Measure-Object).count -eq 0 -or $Recreate.IsPresent) {
87+
if (($MatchedWebhook | Measure-Object).Count -eq 0 -or $Recreate.IsPresent) {
8888
$expiredate = (Get-Date).AddDays(1).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
8989
$params = @{
9090
changeType = $TypeofSubscription
@@ -98,7 +98,7 @@ function New-CIPPGraphSubscription {
9898
}
9999

100100
$GraphRequest = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/subscriptions' -tenantid $TenantFilter -type POST -body $params -verbose
101-
#If creation is succesfull, we store the GUID in the storage table webhookTable to make sure we can check against this later on.
101+
#If creation is successful, we store the GUID in the storage table webhookTable to make sure we can check against this later on.
102102
#We store the GUID as rowkey, the event type, the resource, and the expiration date as properties, we also add the Tenant name so we can easily find this later on.
103103
#We don't store the return, because Ms decided that a renewal or re-authenticate does not change the url, but does change the id...
104104
$WebhookRow = @{
@@ -115,13 +115,48 @@ function New-CIPPGraphSubscription {
115115
#todo: add remove webhook function, add check webhook function, add list webhooks function
116116
#add refresh webhook function based on table.
117117
Write-LogMessage -headers $Headers -API $APIName -message "Created Graph Webhook subscription for $($TenantFilter)" -Sev 'Info' -tenant $TenantFilter
118+
return "Created Webhook subscription for $($TenantFilter)"
118119
} else {
120+
# Check Graph directly for subscriptions matching this resource
121+
$ExistingSubs = @(New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscriptions' -tenantid $TenantFilter)
122+
$MatchingSubs = @($ExistingSubs | Where-Object { $_.notificationUrl -match [regex]::Escape("https://$BaseURL/API/PublicWebhooks") -and $_.resource -eq $Resource } | Sort-Object -Property expirationDateTime -Descending)
123+
124+
# Keep the newest subscription, delete the rest from Graph and the table
125+
$KeptSub = $MatchingSubs | Select-Object -First 1
126+
$Duplicates = $MatchingSubs | Select-Object -Skip 1
127+
128+
foreach ($Dup in $Duplicates) {
129+
try {
130+
New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/subscriptions/$($Dup.id)" -tenantid $TenantFilter -type DELETE
131+
Write-LogMessage -headers $Headers -API $APIName -message "Deleted duplicate Graph Webhook subscription $($Dup.id) for $($TenantFilter)" -Sev 'Warning' -tenant $TenantFilter
132+
} catch {
133+
Write-LogMessage -headers $Headers -API $APIName -message "Failed to delete duplicate Graph Webhook subscription $($Dup.id): $($_.Exception.Message)" -Sev 'Warning' -tenant $TenantFilter
134+
}
135+
# Remove the corresponding table row by SubscriptionID
136+
$StaleRow = $ExistingWebhooks | Where-Object { $_.SubscriptionID -eq $Dup.id }
137+
foreach ($Row in $StaleRow) {
138+
Remove-AzDataTableEntity @WebhookTable -Entity $Row -Force
139+
Write-LogMessage -headers $Headers -API $APIName -message "Removed stale webhook table entry (RowKey $($Row.RowKey)) for $($TenantFilter)" -Sev 'Warning' -tenant $TenantFilter
140+
}
141+
}
142+
143+
# Remove any remaining table rows whose SubscriptionID doesn't match the kept Graph subscription
144+
$ExistingWebhooks | Where-Object { $KeptSub -and $_.SubscriptionID -ne $KeptSub.id } | ForEach-Object {
145+
try {
146+
Remove-AzDataTableEntity @WebhookTable -Entity $_ -Force
147+
Write-LogMessage -headers $Headers -API $APIName -message "Removed orphaned webhook table entry (RowKey $($_.RowKey)) for $($TenantFilter)" -Sev 'Warning' -tenant $TenantFilter
148+
} catch {
149+
# Entity may have already been removed in the duplicate cleanup pass
150+
}
151+
}
152+
119153
Write-LogMessage -headers $Headers -API $APIName -message "Existing Graph Webhook subscription for $($TenantFilter) found" -Sev 'Info' -tenant $TenantFilter
154+
return "Existing Webhook subscription for $($TenantFilter) found"
120155
}
121156
}
122-
return "Created Webhook subscription for $($TenantFilter)"
157+
123158
} catch {
124159
Write-LogMessage -headers $Headers -API $APIName -message "Failed to create Webhook Subscription: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter
125-
Return "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)"
160+
return "Failed to create Webhook Subscription for $($TenantFilter): $($_.Exception.Message)"
126161
}
127162
}

0 commit comments

Comments
 (0)