Skip to content

Commit 0d1f744

Browse files
committed
refactor: streamline user offboarding and improve task scheduling logic
run now makes tasks actually run immediately all user offboardings are now routed through scheduler for proper task tracking and alerting
1 parent 8919863 commit 0d1f744

6 files changed

Lines changed: 98 additions & 68 deletions

File tree

Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,25 @@
11
function Add-CIPPScheduledTask {
2-
[CmdletBinding(DefaultParameterSetName = 'Default')]
2+
[CmdletBinding()]
33
param(
4-
[Parameter(Mandatory = $true, ParameterSetName = 'Default')]
4+
[Parameter(Mandatory = $true)]
55
[pscustomobject]$Task,
66

7-
[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
7+
[Parameter(Mandatory = $false)]
88
[bool]$Hidden,
99

10-
[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
10+
[Parameter(Mandatory = $false)]
1111
$DisallowDuplicateName = $false,
1212

13-
[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
13+
[Parameter(Mandatory = $false)]
1414
[string]$SyncType = $null,
1515

16-
[Parameter(Mandatory = $false, ParameterSetName = 'RunNow')]
16+
[Parameter(Mandatory = $false)]
1717
[switch]$RunNow,
1818

19-
[Parameter(Mandatory = $true, ParameterSetName = 'RunNow')]
20-
[string]$RowKey,
21-
22-
[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
19+
[Parameter(Mandatory = $false)]
2320
[string]$DesiredStartTime = $null,
2421

25-
[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
26-
[Parameter(Mandatory = $false, ParameterSetName = 'RunNow')]
22+
[Parameter(Mandatory = $false)]
2723
$Headers
2824
)
2925

@@ -39,6 +35,9 @@ function Add-CIPPScheduledTask {
3935
$ExistingTask.TaskState = 'Planned'
4036
Add-CIPPAzDataTableEntity @Table -Entity $ExistingTask -Force
4137
Write-LogMessage -headers $Headers -API 'RunNow' -message "Task $($ExistingTask.Name) scheduled to run now" -Sev 'Info' -Tenant $ExistingTask.Tenant
38+
Add-CippQueueMessage -Cmdlet 'Start-UserTasksOrchestrator' -Parameters @{
39+
TaskId = $RowKey
40+
}
4241
return "Task $($ExistingTask.Name) scheduled to run now"
4342
} catch {
4443
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
@@ -192,6 +191,7 @@ function Add-CIPPScheduledTask {
192191
Hidden = [bool]$Hidden
193192
Results = 'Planned'
194193
AlertComment = [string]$task.AlertComment
194+
CustomSubject = [string]$task.CustomSubject
195195
}
196196

197197

@@ -299,6 +299,13 @@ function Add-CIPPScheduledTask {
299299
default { 'less than a minute' }
300300
}
301301

302+
if ($RunNow.IsPresent) {
303+
Add-CippQueueMessage -Cmdlet 'Start-UserTasksOrchestrator' -Parameters @{
304+
TaskId = $RowKey
305+
}
306+
return "Task $($entity.Name) scheduled to run now"
307+
}
308+
302309
return "Successfully added task: $($entity.Name). It will run in $relativeTime."
303310
}
304311
} catch {

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-AddScheduledItem.ps1

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,22 @@ function Invoke-AddScheduledItem {
1818
$HeaderProperties = @('x-ms-client-principal', 'x-ms-client-principal-id', 'x-ms-client-principal-name', 'x-forwarded-for')
1919
$Headers = $Request.Headers | Select-Object -Property $HeaderProperties -ErrorAction SilentlyContinue
2020

21-
if ($Request.Body.RunNow -eq $true) {
22-
try {
23-
$Table = Get-CIPPTable -TableName 'ScheduledTasks'
24-
$Filter = "PartitionKey eq 'ScheduledTask' and RowKey eq '$($Request.Body.RowKey)'"
25-
$ExistingTask = (Get-CIPPAzDataTableEntity @Table -Filter $Filter)
21+
$Table = Get-CIPPTable -TableName 'ScheduledTasks'
2622

27-
if ($ExistingTask) {
28-
$RerunParams = @{
29-
TenantFilter = $ExistingTask.Tenant
30-
Type = 'ScheduledTask'
31-
API = $Request.Body.RowKey
32-
Clear = $true
33-
}
34-
$null = Test-CIPPRerun @RerunParams
35-
$Result = Add-CIPPScheduledTask -RowKey $Request.Body.RowKey -RunNow -Headers $Headers
36-
} else {
37-
$Result = "Task with id $($Request.Body.RowKey) does not exist"
38-
}
39-
} catch {
40-
Write-Warning "Error scheduling task: $($_.Exception.Message)"
41-
Write-Information $_.InvocationInfo.PositionMessage
42-
$Result = "Error scheduling task: $($_.Exception.Message)"
23+
if ($Request.Body.RowKey) {
24+
$Filter = "PartitionKey eq 'ScheduledTask' and RowKey eq '$($Request.Body.RowKey)'"
25+
$ExistingTask = (Get-CIPPAzDataTableEntity @Table -Filter $Filter)
26+
}
27+
28+
if ($ExistingTask -and $Request.Body.RunNow -eq $true) {
29+
$RerunParams = @{
30+
TenantFilter = $ExistingTask.Tenant
31+
Type = 'ScheduledTask'
32+
API = $Request.Body.RowKey
33+
Clear = $true
4334
}
35+
$null = Test-CIPPRerun @RerunParams
36+
$Result = Add-CIPPScheduledTask -RowKey $Request.Body.RowKey -RunNow -Headers $Headers
4437
} else {
4538
$ScheduledTask = @{
4639
Task = $Request.Body
@@ -49,6 +42,9 @@ function Invoke-AddScheduledItem {
4942
DisallowDuplicateName = $DisallowDuplicateName
5043
DesiredStartTime = $Request.Body.DesiredStartTime
5144
}
45+
if ($Request.Body.RunNow -eq $true) {
46+
$ScheduledTask.RunNow = $true
47+
}
5248
$Result = Add-CIPPScheduledTask @ScheduledTask
5349
}
5450
return ([HttpResponseContext]@{

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ListScheduledItems.ps1

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ function Invoke-ListScheduledItems {
1111
$ScheduledItemFilter.Add("PartitionKey eq 'ScheduledTask'")
1212

1313
$Id = $Request.Query.Id ?? $Request.Body.Id
14+
$TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter
15+
1416
if ($Id) {
1517
# Interact with query parameters.
1618
$ScheduledItemFilter.Add("RowKey eq '$($Id)'")
@@ -48,6 +50,10 @@ function Invoke-ListScheduledItems {
4850
$Tasks = $Tasks | Where-Object { $_.command -eq $Type }
4951
}
5052

53+
if ($TenantFilter) {
54+
$Tasks = $Tasks | Where-Object { $_.tenant -eq $TenantFilter -or $TenantFilter -eq 'AllTenants' }
55+
}
56+
5157
if ($SearchTitle) {
5258
$Tasks = $Tasks | Where-Object { $_.Name -like $SearchTitle }
5359
}

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecOffboardUser.ps1

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,41 @@ function Invoke-ExecOffboardUser {
1010
$AllUsers = $Request.Body.user.value
1111
$TenantFilter = $request.Body.tenantFilter.value ? $request.Body.tenantFilter.value : $request.Body.tenantFilter
1212
$OffboardingOptions = $Request.Body | Select-Object * -ExcludeProperty user, tenantFilter, Scheduled
13+
14+
$StatusCode = [HttpStatusCode]::OK
1315
$Results = foreach ($username in $AllUsers) {
1416
try {
15-
$APIName = 'ExecOffboardUser'
1617
$Headers = $Request.Headers
17-
18-
19-
if ($Request.Body.Scheduled.enabled) {
20-
$taskObject = [PSCustomObject]@{
21-
TenantFilter = $TenantFilter
22-
Name = "Offboarding: $Username"
23-
Command = @{
24-
value = 'Invoke-CIPPOffboardingJob'
25-
}
26-
Parameters = [pscustomobject]@{
27-
Username = $Username
28-
APIName = 'Scheduled Offboarding'
29-
options = $OffboardingOptions
30-
RunScheduled = $true
31-
}
32-
ScheduledTime = $Request.Body.Scheduled.date
33-
PostExecution = @{
34-
Webhook = [bool]$Request.Body.PostExecution.webhook
35-
Email = [bool]$Request.Body.PostExecution.email
36-
PSA = [bool]$Request.Body.PostExecution.psa
37-
}
38-
Reference = $Request.Body.reference
18+
$taskObject = [PSCustomObject]@{
19+
TenantFilter = $TenantFilter
20+
Name = "Offboarding: $Username"
21+
Command = @{
22+
value = 'Invoke-CIPPOffboardingJob'
23+
}
24+
Parameters = [pscustomobject]@{
25+
Username = $Username
26+
APIName = 'Scheduled Offboarding'
27+
options = $OffboardingOptions
28+
RunScheduled = $true
3929
}
40-
Add-CIPPScheduledTask -Task $taskObject -hidden $false -Headers $Headers
30+
PostExecution = @{
31+
Webhook = [bool]$Request.Body.PostExecution.webhook
32+
Email = [bool]$Request.Body.PostExecution.email
33+
PSA = [bool]$Request.Body.PostExecution.psa
34+
}
35+
Reference = $Request.Body.reference
36+
}
37+
$Params = @{
38+
Task = $taskObject
39+
hidden = $false
40+
Headers = $Headers
41+
}
42+
if ($Request.Body.Scheduled.enabled) {
43+
$taskObject.ScheduledTime = $Request.Body.Scheduled.date
4144
} else {
42-
Invoke-CIPPOffboardingJob -Username $Username -TenantFilter $TenantFilter -Options $OffboardingOptions -APIName $APIName -Headers $Headers
45+
$Params.RunNow = $true
4346
}
44-
$StatusCode = [HttpStatusCode]::OK
45-
47+
Add-CIPPScheduledTask @Params
4648
} catch {
4749
$StatusCode = [HttpStatusCode]::Forbidden
4850
$_.Exception.message

Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-CIPPOrchestrator.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ function Start-CIPPOrchestrator {
3737

3838
# If already running in processor context (e.g., timer trigger) and we have an InputObject,
3939
# start orchestration directly without queuing
40-
if ($InputObject -and ($env:CIPP_PROCESSOR -eq 'true' -or $CallerIsQueueTrigger.IsPresent)) {
40+
41+
$OrchestratorTriggerDisabled = $env:AzureWebJobs_CIPPOrchestrator_Disabled -eq 'true' -or $env:AzureWebJobs_CIPPOrchestrator_Disabled -eq '1'
42+
43+
if ($InputObject -and -not $OrchestratorTriggerDisabled) {
4144
Write-Information 'Running in processor context - starting orchestration directly'
4245
try {
4346
$InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 10 -Compress)

Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,31 @@ function Start-UserTasksOrchestrator {
77
Entrypoint
88
#>
99
[CmdletBinding(SupportsShouldProcess = $true)]
10-
param()
10+
param(
11+
$TaskId = $null
12+
)
1113

1214
$Table = Get-CippTable -tablename 'ScheduledTasks'
13-
$4HoursAgo = (Get-Date).AddHours(-4).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
14-
$24HoursAgo = (Get-Date).AddHours(-24).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
15-
# Pending = orchestrator queued, Running = actively executing
16-
# Pick up: Planned, Failed-Planned, stuck Pending (>24hr), or stuck Running (>4hr for large AllTenants tasks)
17-
$Filter = "PartitionKey eq 'ScheduledTask' and (TaskState eq 'Planned' or TaskState eq 'Failed - Planned' or (TaskState eq 'Pending' and Timestamp lt datetime'$24HoursAgo') or (TaskState eq 'Running' and Timestamp lt datetime'$4HoursAgo') or (TaskState eq 'Processing' and Timestamp lt datetime'$4HoursAgo'))"
18-
$tasks = Get-CIPPAzDataTableEntity @Table -Filter $Filter
15+
16+
if ($TaskId) {
17+
$Filter = "PartitionKey eq 'ScheduledTask' and RowKey eq '$TaskId'"
18+
$task = Get-CIPPAzDataTableEntity @Table -Filter $Filter
19+
20+
if (-not $task.RowKey) {
21+
Write-Warning "No scheduled task found with ID: $TaskId"
22+
return
23+
} else {
24+
Write-Information "Starting orchestrator for scheduled task: $($task.Name) with ID: $TaskId"
25+
$tasks = @($task)
26+
}
27+
} else {
28+
$4HoursAgo = (Get-Date).AddHours(-4).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
29+
$24HoursAgo = (Get-Date).AddHours(-24).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
30+
# Pending = orchestrator queued, Running = actively executing
31+
# Pick up: Planned, Failed-Planned, stuck Pending (>24hr), or stuck Running (>4hr for large AllTenants tasks)
32+
$Filter = "PartitionKey eq 'ScheduledTask' and (TaskState eq 'Planned' or TaskState eq 'Failed - Planned' or (TaskState eq 'Pending' and Timestamp lt datetime'$24HoursAgo') or (TaskState eq 'Running' and Timestamp lt datetime'$4HoursAgo') or (TaskState eq 'Processing' and Timestamp lt datetime'$4HoursAgo'))"
33+
$tasks = Get-CIPPAzDataTableEntity @Table -Filter $Filter
34+
}
1935

2036
$Batch = [System.Collections.Generic.List[object]]::new()
2137
$TenantList = Get-Tenants -IncludeErrors

0 commit comments

Comments
 (0)