Skip to content

Commit 7bbcb33

Browse files
committed
new $batch endpoint
use for multiple cipp functions currently uses parallel processing, may be slow with large requests, fine tuning probably needed
1 parent 4ee10a2 commit 7bbcb33

File tree

3 files changed

+158
-34
lines changed

3 files changed

+158
-34
lines changed

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListGraphRequest.ps1

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,9 @@ function Invoke-ListGraphRequest {
162162
if ($request.Query.Sort) {
163163
$GraphRequestData.Results = $GraphRequestData.Results | Sort-Object -Property $request.Query.Sort
164164
}
165-
$Outputdata = $GraphRequestData | ConvertTo-Json -Depth 20 -Compress
166165

167166
return ([HttpResponseContext]@{
168167
StatusCode = $StatusCode
169-
Body = $Outputdata
168+
Body = $GraphRequestData
170169
})
171170
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
function New-CippCoreRequest {
2+
<#
3+
.SYNOPSIS
4+
Main entrypoint for all HTTP triggered functions in CIPP
5+
.DESCRIPTION
6+
This function is the main entry point for all HTTP triggered functions in CIPP. It routes requests to the appropriate function based on the CIPPEndpoint parameter in the request.
7+
.FUNCTIONALITY
8+
Internal
9+
#>
10+
[CmdletBinding(SupportsShouldProcess = $true)]
11+
param($Request, $TriggerMetadata)
12+
13+
$FunctionName = 'Invoke-{0}' -f $Request.Params.CIPPEndpoint
14+
Write-Information "API: $($Request.Params.CIPPEndpoint)"
15+
16+
$HttpTrigger = @{
17+
Request = [pscustomobject]($Request)
18+
TriggerMetadata = $TriggerMetadata
19+
}
20+
21+
if ($PSCmdlet.ShouldProcess("Processing request for $($Request.Params.CIPPEndpoint)")) {
22+
if ((Get-Command -Name $FunctionName -ErrorAction SilentlyContinue) -or $FunctionName -eq 'Invoke-Me') {
23+
try {
24+
$Access = Test-CIPPAccess -Request $Request
25+
if ($FunctionName -eq 'Invoke-Me') {
26+
return $Access
27+
}
28+
} catch {
29+
Write-Information "Access denied for $FunctionName : $($_.Exception.Message)"
30+
return ([HttpResponseContext]@{
31+
StatusCode = [HttpStatusCode]::Forbidden
32+
Body = $_.Exception.Message
33+
})
34+
}
35+
36+
try {
37+
Write-Information "Access: $Access"
38+
Write-LogMessage -headers $Headers -API $Request.Params.CIPPEndpoint -message 'Accessed this API' -Sev 'Debug'
39+
if ($Access) {
40+
$Response = & $FunctionName @HttpTrigger
41+
if ($Response.StatusCode) {
42+
return ([HttpResponseContext]$Response)
43+
}
44+
}
45+
} catch {
46+
Write-Warning "Exception occurred on HTTP trigger ($FunctionName): $($_.Exception.Message)"
47+
return ([HttpResponseContext]@{
48+
StatusCode = [HttpStatusCode]::InternalServerError
49+
Body = $_.Exception.Message
50+
})
51+
}
52+
} else {
53+
return ([HttpResponseContext]@{
54+
StatusCode = [HttpStatusCode]::NotFound
55+
Body = 'Endpoint not found'
56+
})
57+
}
58+
} else {
59+
return ([HttpResponseContext]@{
60+
StatusCode = [HttpStatusCode]::PreconditionFailed
61+
Body = 'Request not processed'
62+
})
63+
}
64+
}

Modules/CippEntrypoints/CippEntrypoints.psm1

Lines changed: 93 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -39,51 +39,112 @@ function Receive-CippHttpTrigger {
3939
# Convert the request to a PSCustomObject because the httpContext is case sensitive since 7.3
4040
$Request = $Request | ConvertTo-Json -Depth 100 | ConvertFrom-Json
4141
Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName
42-
$FunctionName = 'Invoke-{0}' -f $Request.Params.CIPPEndpoint
43-
Write-Information "API: $($Request.Params.CIPPEndpoint)"
4442

45-
$HttpTrigger = @{
46-
Request = [pscustomobject]($Request)
47-
TriggerMetadata = $TriggerMetadata
48-
}
49-
50-
if ((Get-Command -Name $FunctionName -ErrorAction SilentlyContinue) -or $FunctionName -eq 'Invoke-Me') {
43+
if ($Request.Params.CIPPEndpoint -eq '$batch') {
44+
# Implement batch processing in the style of graph api $batch
5145
try {
52-
$Access = Test-CIPPAccess -Request $Request
53-
if ($FunctionName -eq 'Invoke-Me') {
54-
Push-OutputBinding -Name Response -Value $Access
46+
$BatchRequests = $Request.Body.requests
47+
if (-not $BatchRequests -or $BatchRequests.Count -eq 0) {
48+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
49+
StatusCode = [HttpStatusCode]::BadRequest
50+
Body = @{ error = @{ message = 'No requests found in batch body' } }
51+
})
5552
return
5653
}
57-
} catch {
58-
Write-Information "Access denied for $FunctionName : $($_.Exception.Message)"
59-
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
60-
StatusCode = [HttpStatusCode]::Forbidden
61-
Body = $_.Exception.Message
62-
})
63-
return
64-
}
6554

66-
try {
67-
Write-Information "Access: $Access"
68-
Write-LogMessage -headers $Headers -API $Request.Params.CIPPEndpoint -message 'Accessed this API' -Sev 'Debug'
69-
if ($Access) {
70-
$Response = & $FunctionName @HttpTrigger
71-
if ($Response.StatusCode) {
72-
Push-OutputBinding -Name Response -Value ([HttpResponseContext]$Response)
55+
# Validate batch request limit (this might need to be fine tuned for SWA timeouts)
56+
if ($BatchRequests.Count -gt 20) {
57+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
58+
StatusCode = [HttpStatusCode]::BadRequest
59+
Body = @{ error = @{ message = 'Batch request limit exceeded. Maximum 20 requests allowed per batch.' } }
60+
})
61+
return
62+
}
63+
64+
# Process batch requests in parallel for better performance
65+
$BatchResponses = $BatchRequests | ForEach-Object -Parallel {
66+
$BatchRequest = $_
67+
$RequestHeaders = $using:Request.Headers
68+
$TriggerMeta = $using:TriggerMetadata
69+
70+
try {
71+
# Import required modules in the parallel thread
72+
Import-Module CIPPCore -Force
73+
Import-Module CippExtensions -Force -ErrorAction SilentlyContinue
74+
Import-Module DNSHealth -Force -ErrorAction SilentlyContinue
75+
Import-Module AzBobbyTables -Force -ErrorAction SilentlyContinue
76+
77+
# Create individual request object for each batch item
78+
$IndividualRequest = @{
79+
Params = @{
80+
CIPPEndpoint = $BatchRequest.url # Use batch request URL as endpoint
81+
}
82+
Body = $BatchRequest.body
83+
Headers = $RequestHeaders
84+
Query = $BatchRequest.query
85+
Method = $BatchRequest.method
86+
}
87+
88+
# Process individual request using New-CippCoreRequest
89+
$IndividualResponse = New-CippCoreRequest -Request $IndividualRequest -TriggerMetadata $TriggerMeta
90+
91+
# Format response in Graph API batch style
92+
$BatchResponse = @{
93+
id = $BatchRequest.id
94+
status = [int]$IndividualResponse.StatusCode
95+
body = $IndividualResponse.Body
96+
}
97+
98+
} catch {
99+
# Handle individual request errors
100+
$BatchResponse = @{
101+
id = $BatchRequest.id
102+
status = 500
103+
body = @{
104+
error = @{
105+
code = 'InternalServerError'
106+
message = $_.Exception.Message
107+
}
108+
}
109+
}
73110
}
111+
112+
return $BatchResponse
113+
} -ThrottleLimit 10
114+
115+
$BodyObj = @{
116+
responses = @($BatchResponses)
74117
}
118+
119+
$Body = ConvertTo-Json -InputObject $BodyObj -Depth 20 -Compress
120+
121+
# Return batch response in Graph API format
122+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
123+
StatusCode = [HttpStatusCode]::OK
124+
Body = $Body
125+
})
126+
75127
} catch {
76-
Write-Warning "Exception occurred on HTTP trigger ($FunctionName): $($_.Exception.Message)"
128+
Write-Warning "Exception occurred during batch processing: $($_.Exception.Message)"
77129
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
78130
StatusCode = [HttpStatusCode]::InternalServerError
79-
Body = $_.Exception.Message
131+
Body = @{
132+
error = @{
133+
code = 'InternalServerError'
134+
message = "Batch processing failed: $($_.Exception.Message)"
135+
}
136+
}
80137
})
81138
}
139+
return
82140
} else {
83-
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
84-
StatusCode = [HttpStatusCode]::NotFound
85-
Body = 'Endpoint not found'
86-
})
141+
$Response = New-CippCoreRequest -Request $Request -TriggerMetadata $TriggerMetadata
142+
if ($Response.StatusCode) {
143+
if ($Response.Body -is [PSCustomObject]) {
144+
$Response.Body = $Response.Body | ConvertTo-Json -Depth 20 -Compress
145+
}
146+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]$Response)
147+
}
87148
}
88149
return
89150
}

0 commit comments

Comments
 (0)