@@ -13,87 +13,158 @@ function New-GraphDeltaQuery {
1313 Additional query parameters (e.g., $select, $filter, $top).
1414 . PARAMETER DeltaUrl
1515 Use this parameter to continue a delta query with a specific delta or next link.
16+ . FUNCTIONALITY
17+ Internal
1618 #>
1719 [CmdletBinding (DefaultParameterSetName = ' NewDeltaQuery' )]
1820 param (
1921 [Parameter (Mandatory = $true , ParameterSetName = ' NewDeltaQuery' )]
2022 [Parameter (Mandatory = $true , ParameterSetName = ' DeltaUrl' )]
21- [ string ] $TenantFilter ,
23+ $TenantFilter ,
2224
2325 [Parameter (ParameterSetName = ' NewDeltaQuery' , Mandatory = $true )]
24- [ValidateSet (' users' , ' groups' , ' contacts' , ' devices' , ' applications' , ' servicePrincipals' , ' directoryObjects' , ' administrativeUnits' )]
26+ [ValidateSet (' users' , ' groups' , ' contacts' , ' orgContact ' , ' devices' , ' applications' , ' servicePrincipals' , ' directoryObjects' , ' directoryRole ' , ' administrativeUnits' , ' oAuth2PermissionGrant ' )]
2527 [string ]$Resource ,
2628
2729 [Parameter (ParameterSetName = ' NewDeltaQuery' , Mandatory = $false )]
2830 [hashtable ]$Parameters = @ {},
2931
3032 [Parameter (ParameterSetName = ' DeltaUrl' , Mandatory = $true )]
31- [string ]$DeltaUrl
33+ [string ]$DeltaUrl ,
34+
35+ [Parameter (Mandatory = $false , ParameterSetName = ' NewDeltaQuery' )]
36+ [Parameter (Mandatory = $true , ParameterSetName = ' DeltaUrl' )]
37+ [string ]$PartitionKey
3238 )
3339
34- try {
35- if ($DeltaUrl ) {
36- $GraphQuery = [System.UriBuilder ]$DeltaUrl
40+ if ($TenantFilter -eq ' AllTenants' -or $TenantFilter.type -eq ' Group' ) {
41+ Write-Information ' Creating delta query for all tenants or tenant group.'
42+ if ($TenantFilter.type -eq ' group' ) {
43+ $Tenants = Expand-CIPPTenantGroups - TenantFilter $TenantFilter
3744 } else {
38- $GraphQuery = [System.UriBuilder ](' https://graph.microsoft.com/beta/{0}/delta' -f $Resource )
39- $QueryParams = @ {
40- ' $deltaToken' = ' latest'
45+ $Tenants = Get-Tenants - IncludeErrors
46+ }
47+
48+ if (! $PartitionKey ) {
49+ $ParamJson = $Parameters | ConvertTo-Json - Depth 5 - Compress
50+ $PartitionKey = Get-StringHash - String ($Resource + $ParamJson )
51+ }
52+ # Prepare batch processing for all tenants
53+ $TenantBatch = $Tenants | ForEach-Object {
54+ [PSCustomObject ]@ {
55+ FunctionName = ' GraphDeltaQuery'
56+ TenantFilter = $_.defaultDomainName ?? $_.value
57+ Resource = $Resource
58+ Parameters = $Parameters
59+ PartitionKey = $PartitionKey
4160 }
42- $ParamCollection = [ System.Web.HttpUtility ]::ParseQueryString([ String ]::Empty)
61+ }
4362
44- foreach ($key in $QueryParams.Keys ) {
45- if ($QueryParams [$key ]) {
46- $ParamCollection.Add ($key , $QueryParams [$key ])
47- }
63+ $InputObject = @ {
64+ Batch = @ ($TenantBatch )
65+ OrchestratorName = ' ProcessDeltaQueries'
66+ SkipLog = $true
67+ }
68+ Write-Information " Starting delta query orchestration for $ ( $Tenants.Count ) tenants."
69+ Write-Information " Orchestration Input: $ ( $InputObject | ConvertTo-Json - Compress - Depth 5 ) "
70+ $Orchestration = Start-NewOrchestration - FunctionName CIPPOrchestrator - InputObject ($InputObject | ConvertTo-Json - Compress - Depth 5 )
71+
72+ } else {
73+ $Table = Get-CIPPTable - TableName ' DeltaQueries'
74+
75+ if ($Parameters -and $Resource ) {
76+ $ParamJson = $Parameters | ConvertTo-Json - Depth 5
77+ $ResourceHash = Get-StringHash - String ($Resource + $ParamJson )
78+
79+ $DeltaQuery = @ {
80+ PartitionKey = $PartitionKey ?? $ResourceHash
81+ RowKey = $TenantFilter
82+ Resource = $Resource
83+ Parameters = [string ]($Parameters | ConvertTo-Json - Depth 5 - Compress)
84+ DeltaUrl = $DeltaUrl
4885 }
86+ } elseif ($PartitionKey ) {
87+ $DeltaQuery = Get-AzDataTableEntity @Table - Filter " PartitionKey eq '$PartitionKey ' and RowKey eq '$TenantFilter '"
88+ }
4989
50- foreach ($Item in ($Parameters.GetEnumerator () | Sort-Object - CaseSensitive - Property Key)) {
51- if ($Item.Value -is [System.Boolean ]) {
52- $Item.Value = $Item.Value.ToString ().ToLower()
90+ try {
91+ if ($DeltaUrl ) {
92+ $GraphQuery = [System.UriBuilder ]$DeltaUrl
93+ } else {
94+ $GraphQuery = [System.UriBuilder ](' https://graph.microsoft.com/beta/{0}/delta' -f $Resource )
95+ $QueryParams = @ {
96+ ' $deltaToken' = ' latest'
5397 }
54- if ($Item.Value ) {
55- $ParamCollection.Add ($Item.Key , $Item.Value )
98+ $ParamCollection = [System.Web.HttpUtility ]::ParseQueryString([String ]::Empty)
99+
100+ foreach ($key in $QueryParams.Keys ) {
101+ if ($QueryParams [$key ]) {
102+ $ParamCollection.Add ($key , $QueryParams [$key ])
103+ }
56104 }
57- }
58- $GraphQuery.Query = $ParamCollection.ToString ()
59- }
60-
61- $allResults = [System.Collections.ArrayList ]::new()
62- $nextUrl = $GraphQuery.ToString ()
63- $deltaLink = $null
64-
65- do {
66- $response = New-GraphGetRequest - tenantid $TenantFilter - uri $nextUrl - ReturnRawResponse
67-
68- if ($response.Content ) {
69- $content = $response.Content
70- if ($content -is [string ]) {
71- $content = $content | ConvertFrom-Json
105+
106+ foreach ($Item in ($Parameters.GetEnumerator () | Sort-Object - CaseSensitive - Property Key)) {
107+ if ($Item.Value -is [System.Boolean ]) {
108+ $Item.Value = $Item.Value.ToString ().ToLower()
109+ }
110+ if ($Item.Value ) {
111+ $ParamCollection.Add ($Item.Key , $Item.Value )
112+ }
72113 }
114+ $GraphQuery.Query = $ParamCollection.ToString ()
115+ }
116+
117+ $allResults = [System.Collections.ArrayList ]::new()
118+ $nextUrl = $GraphQuery.ToString ()
119+ $deltaLink = $null
73120
74- # Add results from this page
75- if ($content.value ) {
76- $allResults.AddRange ($content.value )
121+ $DeltaError = $false
122+ do {
123+ try {
124+ $response = New-GraphGetRequest - tenantid $TenantFilter - uri $nextUrl - ReturnRawResponse - extraHeaders @ { Prefer = ' return=minimal' } - ErrorAction Stop
125+ if ($response.Content ) {
126+ $content = $response.Content
127+ if ($content -is [string ]) {
128+ $content = $content | ConvertFrom-Json
129+ }
130+
131+ # Add results from this page
132+ if ($content.value ) {
133+ $allResults.AddRange ($content.value )
134+ }
135+
136+ # Check for next page or delta link
137+ $nextUrl = $content .' @odata.nextLink'
138+ $deltaLink = $content .' @odata.deltaLink'
139+ }
140+ } catch {
141+ Write-Error " Error during Graph Delta Query request for tenant '$TenantFilter ': $ ( Get-NormalizedError - Message $_.Exception.message ) "
142+ $DeltaError = $true
77143 }
144+ } while ($nextUrl -and -not $deltaLink -and -not $DeltaError )
78145
79- # Check for next page or delta link
80- $nextUrl = $content .' @odata.nextLink'
81- $deltaLink = $content .' @odata.deltaLink'
146+ if ($DeltaError ) {
147+ throw " Delta Query failed for tenant '$TenantFilter '."
82148 }
83- } while ($nextUrl -and -not $deltaLink )
149+ $DeltaQuery.RowKey = $TenantFilter
150+ $DeltaQuery.DeltaUrl = $deltaLink
84151
85- # Return results with delta link for future queries
86- $result = @ {
87- value = $allResults.ToArray ()
88- ' @odata.deltaLink' = $deltaLink
89- }
152+ # Return results with delta link for future queries
153+ $result = @ {
154+ value = $allResults.ToArray ()
155+ ' @odata.deltaLink' = $deltaLink
156+ PartitionKey = $PartitionKey
157+ }
158+ # Save link to table
159+ Add-CIPPAzDataTableEntity @Table - Entity $DeltaQuery - Force
90160
91- Write-Information " Delta Query completed for $Resource . Total items: $ ( $allResults.Count ) "
161+ Write-Information " Delta Query created for $ ( $DeltaQuery . Resource) . Total items: $ ( $allResults.Count ) "
92162
93- # Always return full response with deltaLink
94- return $result
95- } catch {
96- Write-Error " Failed to create Delta Query: $ ( Get-NormalizedError - Message $_.Exception.message ) "
97- Write-Warning $_.InvocationInfo.PositionMessage
163+ # Always return full response with deltaLink
164+ return $result
165+ } catch {
166+ Write-Error " Failed to create Delta Query: $ ( Get-NormalizedError - Message $_.Exception.message ) "
167+ Write-Warning $_.InvocationInfo.PositionMessage
168+ }
98169 }
99170}
0 commit comments