Skip to content

Commit e40ccce

Browse files
authored
Merge pull request #206 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 1b7d1fb + 4827d90 commit e40ccce

File tree

6 files changed

+335
-143
lines changed

6 files changed

+335
-143
lines changed
Lines changed: 108 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,129 @@
11
function Add-CIPPScheduledTask {
2-
[CmdletBinding()]
2+
[CmdletBinding(DefaultParameterSetName = 'Default')]
33
param(
4+
[Parameter(Mandatory = $true, ParameterSetName = 'Default')]
45
[pscustomobject]$Task,
6+
7+
[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
58
[bool]$Hidden,
9+
10+
[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
611
$DisallowDuplicateName = $false,
12+
13+
[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
714
[string]$SyncType = $null,
15+
16+
[Parameter(Mandatory = $false, ParameterSetName = 'RunNow')]
17+
[switch]$RunNow,
18+
19+
[Parameter(Mandatory = $true, ParameterSetName = 'RunNow')]
20+
[string]$RowKey,
21+
22+
[Parameter(Mandatory = $false, ParameterSetName = 'Default')]
23+
[Parameter(Mandatory = $false, ParameterSetName = 'RunNow')]
824
$Headers
925
)
1026

1127
$Table = Get-CIPPTable -TableName 'ScheduledTasks'
12-
if ($DisallowDuplicateName) {
13-
$Filter = "PartitionKey eq 'ScheduledTask' and Name eq '$($Task.Name)'"
14-
$ExistingTask = (Get-CIPPAzDataTableEntity @Table -Filter $Filter)
15-
if ($ExistingTask) {
16-
return "Task with name $($Task.Name) already exists"
28+
29+
if ($RunNow.IsPresent -and $RowKey) {
30+
try {
31+
$Filter = "PartitionKey eq 'ScheduledTask' and RowKey eq '$($RowKey)'"
32+
$ExistingTask = (Get-CIPPAzDataTableEntity @Table -Filter $Filter)
33+
$ExistingTask.ScheduledTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds
34+
$ExistingTask.TaskState = 'Planned'
35+
Add-CIPPAzDataTableEntity @Table -Entity $ExistingTask -Force
36+
Write-LogMessage -headers $Headers -API 'RunNow' -message "Task $($ExistingTask.Name) scheduled to run now" -Sev 'Info' -Tenant $ExistingTask.Tenant
37+
return "Task $($ExistingTask.Name) scheduled to run now"
38+
} catch {
39+
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
40+
Write-LogMessage -headers $Headers -API 'RunNow' -message "Could not run task: $ErrorMessage" -Sev 'Error'
41+
return "Could not run task: $ErrorMessage"
42+
}
43+
} else {
44+
if ($DisallowDuplicateName) {
45+
$Filter = "PartitionKey eq 'ScheduledTask' and Name eq '$($Task.Name)'"
46+
$ExistingTask = (Get-CIPPAzDataTableEntity @Table -Filter $Filter)
47+
if ($ExistingTask) {
48+
return "Task with name $($Task.Name) already exists"
49+
}
1750
}
18-
}
1951

20-
$propertiesToCheck = @('Webhook', 'Email', 'PSA')
21-
$PostExecutionObject = ($propertiesToCheck | Where-Object { $task.PostExecution.$_ -eq $true })
22-
$PostExecution = $PostExecutionObject ? ($PostExecutionObject -join ',') : ($Task.PostExecution.value -join ',')
23-
$Parameters = [System.Collections.Hashtable]@{}
24-
foreach ($Key in $task.Parameters.PSObject.Properties.Name) {
25-
$Param = $task.Parameters.$Key
52+
$propertiesToCheck = @('Webhook', 'Email', 'PSA')
53+
$PostExecutionObject = ($propertiesToCheck | Where-Object { $task.PostExecution.$_ -eq $true })
54+
$PostExecution = $PostExecutionObject ? ($PostExecutionObject -join ',') : ($Task.PostExecution.value -join ',')
55+
$Parameters = [System.Collections.Hashtable]@{}
56+
foreach ($Key in $task.Parameters.PSObject.Properties.Name) {
57+
$Param = $task.Parameters.$Key
2658

27-
if ($null -eq $Param -or $Param -eq '' -or ($Param | Measure-Object).Count -eq 0) {
28-
continue
29-
}
30-
if ($Param -is [System.Collections.IDictionary] -or $Param.Key) {
31-
$ht = @{}
32-
foreach ($p in $Param.GetEnumerator()) {
33-
$ht[$p.Key] = $p.Value
59+
if ($null -eq $Param -or $Param -eq '' -or ($Param | Measure-Object).Count -eq 0) {
60+
continue
61+
}
62+
if ($Param -is [System.Collections.IDictionary] -or $Param.Key) {
63+
$ht = @{}
64+
foreach ($p in $Param.GetEnumerator()) {
65+
$ht[$p.Key] = $p.Value
66+
}
67+
$Parameters[$Key] = [PSCustomObject]$ht
68+
} else {
69+
$Parameters[$Key] = $Param
3470
}
35-
$Parameters[$Key] = [PSCustomObject]$ht
36-
} else {
37-
$Parameters[$Key] = $Param
3871
}
39-
}
4072

41-
if ($Headers) {
42-
$Parameters.Headers = $Headers | Select-Object -Property 'x-forwarded-for', 'x-ms-client-principal', 'x-ms-client-principal-idp', 'x-ms-client-principal-name'
43-
}
73+
if ($Headers) {
74+
$Parameters.Headers = $Headers | Select-Object -Property 'x-forwarded-for', 'x-ms-client-principal', 'x-ms-client-principal-idp', 'x-ms-client-principal-name'
75+
}
4476

45-
$Parameters = ($Parameters | ConvertTo-Json -Depth 10 -Compress)
46-
$AdditionalProperties = [System.Collections.Hashtable]@{}
47-
foreach ($Prop in $task.AdditionalProperties) {
48-
$AdditionalProperties[$Prop.Key] = $Prop.Value
49-
}
50-
$AdditionalProperties = ([PSCustomObject]$AdditionalProperties | ConvertTo-Json -Compress)
51-
if ($Parameters -eq 'null') { $Parameters = '' }
52-
if (!$Task.RowKey) {
53-
$RowKey = (New-Guid).Guid
54-
} else {
55-
$RowKey = $Task.RowKey
56-
}
77+
$Parameters = ($Parameters | ConvertTo-Json -Depth 10 -Compress)
78+
$AdditionalProperties = [System.Collections.Hashtable]@{}
79+
foreach ($Prop in $task.AdditionalProperties) {
80+
$AdditionalProperties[$Prop.Key] = $Prop.Value
81+
}
82+
$AdditionalProperties = ([PSCustomObject]$AdditionalProperties | ConvertTo-Json -Compress)
83+
if ($Parameters -eq 'null') { $Parameters = '' }
84+
if (!$Task.RowKey) {
85+
$RowKey = (New-Guid).Guid
86+
} else {
87+
$RowKey = $Task.RowKey
88+
}
5789

58-
$Recurrence = if ([string]::IsNullOrEmpty($task.Recurrence.value)) {
59-
$task.Recurrence
60-
} else {
61-
$task.Recurrence.value
62-
}
90+
$Recurrence = if ([string]::IsNullOrEmpty($task.Recurrence.value)) {
91+
$task.Recurrence
92+
} else {
93+
$task.Recurrence.value
94+
}
6395

64-
if ([int64]$task.ScheduledTime -eq 0 -or [string]::IsNullOrEmpty($task.ScheduledTime)) {
65-
$task.ScheduledTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds
66-
}
67-
$excludedTenants = if ($task.excludedTenants.value) {
68-
$task.excludedTenants.value -join ','
69-
}
70-
$entity = @{
71-
PartitionKey = [string]'ScheduledTask'
72-
TaskState = [string]'Planned'
73-
RowKey = [string]$RowKey
74-
Tenant = $task.TenantFilter.value ? "$($task.TenantFilter.value)" : "$($task.TenantFilter)"
75-
excludedTenants = [string]$excludedTenants
76-
Name = [string]$task.Name
77-
Command = [string]$task.Command.value
78-
Parameters = [string]$Parameters
79-
ScheduledTime = [string]$task.ScheduledTime
80-
Recurrence = [string]$Recurrence
81-
PostExecution = [string]$PostExecution
82-
AdditionalProperties = [string]$AdditionalProperties
83-
Hidden = [bool]$Hidden
84-
Results = 'Planned'
85-
}
86-
if ($SyncType) {
87-
$entity.SyncType = $SyncType
88-
}
89-
try {
90-
Add-CIPPAzDataTableEntity @Table -Entity $entity -Force
91-
} catch {
92-
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
93-
return "Could not add task: $ErrorMessage"
96+
if ([int64]$task.ScheduledTime -eq 0 -or [string]::IsNullOrEmpty($task.ScheduledTime)) {
97+
$task.ScheduledTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds
98+
}
99+
$excludedTenants = if ($task.excludedTenants.value) {
100+
$task.excludedTenants.value -join ','
101+
}
102+
$entity = @{
103+
PartitionKey = [string]'ScheduledTask'
104+
TaskState = [string]'Planned'
105+
RowKey = [string]$RowKey
106+
Tenant = $task.TenantFilter.value ? "$($task.TenantFilter.value)" : "$($task.TenantFilter)"
107+
excludedTenants = [string]$excludedTenants
108+
Name = [string]$task.Name
109+
Command = [string]$task.Command.value
110+
Parameters = [string]$Parameters
111+
ScheduledTime = [string]$task.ScheduledTime
112+
Recurrence = [string]$Recurrence
113+
PostExecution = [string]$PostExecution
114+
AdditionalProperties = [string]$AdditionalProperties
115+
Hidden = [bool]$Hidden
116+
Results = 'Planned'
117+
}
118+
if ($SyncType) {
119+
$entity.SyncType = $SyncType
120+
}
121+
try {
122+
Add-CIPPAzDataTableEntity @Table -Entity $entity -Force
123+
} catch {
124+
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
125+
return "Could not add task: $ErrorMessage"
126+
}
127+
return "Successfully added task: $($entity.Name)"
94128
}
95-
return "Successfully added task: $($entity.Name)"
96129
}

Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,14 @@ function Push-ExecScheduledCommand {
8282
}
8383

8484
if ($StoredResults.Length -gt 64000 -or $task.Tenant -eq 'AllTenants') {
85-
$StoredResults = @{ Results = 'The results for this query are too long to store in this table, or the query was meant for All Tenants. Please use the options to send the results to another target to be able to view the results. ' } | ConvertTo-Json -Compress
85+
$TaskResultsTable = Get-CippTable -tablename 'ScheduledTaskResults'
86+
$TaskResults = @{
87+
PartitionKey = $task.RowKey
88+
RowKey = $Tenant
89+
Results = [string](ConvertTo-Json -Compress -Depth 20 $results)
90+
}
91+
$null = Add-AzDataTableEntity @TaskResultsTable -Entity $TaskResults -Force
92+
$StoredResults = @{ Results = 'Completed, details are available in the More Info pane' } | ConvertTo-Json -Compress
8693
}
8794
} catch {
8895
$errorMessage = $_.Exception.Message
@@ -119,42 +126,47 @@ function Push-ExecScheduledCommand {
119126
}
120127
Write-Host 'Sent the results to the target. Updating the task state.'
121128

122-
if ($task.Recurrence -eq '0' -or [string]::IsNullOrEmpty($task.Recurrence)) {
123-
Write-Host 'Recurrence empty or 0. Task is not recurring. Setting task state to completed.'
124-
Update-AzDataTableEntity -Force @Table -Entity @{
125-
PartitionKey = $task.PartitionKey
126-
RowKey = $task.RowKey
127-
Results = "$StoredResults"
128-
TaskState = 'Completed'
129-
}
130-
} else {
131-
#if recurrence is just a number, add it in days.
132-
if ($task.Recurrence -match '^\d+$') {
133-
$task.Recurrence = $task.Recurrence + 'd'
134-
}
135-
$secondsToAdd = switch -Regex ($task.Recurrence) {
136-
'(\d+)m$' { [int64]$matches[1] * 60 }
137-
'(\d+)h$' { [int64]$matches[1] * 3600 }
138-
'(\d+)d$' { [int64]$matches[1] * 86400 }
139-
default { throw "Unsupported recurrence format: $($task.Recurrence)" }
140-
}
129+
try {
130+
if ($task.Recurrence -eq '0' -or [string]::IsNullOrEmpty($task.Recurrence)) {
131+
Write-Host 'Recurrence empty or 0. Task is not recurring. Setting task state to completed.'
132+
Update-AzDataTableEntity -Force @Table -Entity @{
133+
PartitionKey = $task.PartitionKey
134+
RowKey = $task.RowKey
135+
Results = "$StoredResults"
136+
TaskState = 'Completed'
137+
}
138+
} else {
139+
#if recurrence is just a number, add it in days.
140+
if ($task.Recurrence -match '^\d+$') {
141+
$task.Recurrence = $task.Recurrence + 'd'
142+
}
143+
$secondsToAdd = switch -Regex ($task.Recurrence) {
144+
'(\d+)m$' { [int64]$matches[1] * 60 }
145+
'(\d+)h$' { [int64]$matches[1] * 3600 }
146+
'(\d+)d$' { [int64]$matches[1] * 86400 }
147+
default { 0 }
148+
}
141149

142-
if ($secondsToAdd -gt 0) {
143-
$unixtimeNow = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds
144-
if ([int64]$task.ScheduledTime -lt ($unixtimeNow - $secondsToAdd)) {
145-
$task.ScheduledTime = $unixtimeNow
150+
if ($secondsToAdd -gt 0) {
151+
$unixtimeNow = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds
152+
if ([int64]$task.ScheduledTime -lt ($unixtimeNow - $secondsToAdd)) {
153+
$task.ScheduledTime = $unixtimeNow
154+
}
146155
}
147-
}
148156

149-
$nextRunUnixTime = [int64]$task.ScheduledTime + [int64]$secondsToAdd
150-
Write-Host "The job is recurring. It was scheduled for $($task.ScheduledTime). The next runtime should be $nextRunUnixTime"
151-
Update-AzDataTableEntity -Force @Table -Entity @{
152-
PartitionKey = $task.PartitionKey
153-
RowKey = $task.RowKey
154-
Results = "$StoredResults"
155-
TaskState = 'Planned'
156-
ScheduledTime = "$nextRunUnixTime"
157+
$nextRunUnixTime = [int64]$task.ScheduledTime + [int64]$secondsToAdd
158+
Write-Host "The job is recurring. It was scheduled for $($task.ScheduledTime). The next runtime should be $nextRunUnixTime"
159+
Update-AzDataTableEntity -Force @Table -Entity @{
160+
PartitionKey = $task.PartitionKey
161+
RowKey = $task.RowKey
162+
Results = "$StoredResults"
163+
TaskState = 'Planned'
164+
ScheduledTime = "$nextRunUnixTime"
165+
}
157166
}
167+
} catch {
168+
Write-Warning "Failed to update task state: $($_.Exception.Message)"
169+
Write-Information $_.InvocationInfo.PositionMessage
158170
}
159171
if ($TaskType -ne 'Alert') {
160172
Write-LogMessage -API 'Scheduler_UserTasks' -tenant $Tenant -tenantid $TenantInfo.customerId -message "Successfully executed task: $($task.Name)" -sev Info
Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using namespace System.Net
22

3-
Function Invoke-AddScheduledItem {
3+
function Invoke-AddScheduledItem {
44
<#
55
.FUNCTIONALITY
66
Entrypoint
@@ -9,17 +9,33 @@ Function Invoke-AddScheduledItem {
99
#>
1010
[CmdletBinding()]
1111
param($Request, $TriggerMetadata)
12-
if ($null -eq $Request.query.hidden) {
12+
if ($null -eq $Request.Query.hidden) {
1313
$hidden = $false
1414
} else {
1515
$hidden = $true
1616
}
17-
$Result = Add-CIPPScheduledTask -Task $Request.body -Headers $Request.Headers -hidden $hidden -DisallowDuplicateName $Request.query.DisallowDuplicateName
18-
Write-LogMessage -headers $Request.Headers -API $APINAME -message $Result -Sev 'Info'
1917

18+
if ($Request.Body.RunNow -eq $true) {
19+
try {
20+
$Table = Get-CIPPTable -TableName 'ScheduledTasks'
21+
$Filter = "PartitionKey eq 'ScheduledTask' and RowKey eq '$($Request.Body.RowKey)'"
22+
$ExistingTask = (Get-CIPPAzDataTableEntity @Table -Filter $Filter)
23+
if ($ExistingTask) {
24+
$Result = Add-CIPPScheduledTask -RowKey $Request.Body.RowKey -RunNow -Headers $Request.Headers
25+
} else {
26+
$Result = "Task with id $($Request.Body.RowKey) does not exist"
27+
}
28+
} catch {
29+
Write-Warning "Error scheduling task: $($_.Exception.Message)"
30+
Write-Information $_.InvocationInfo.PositionMessage
31+
$Result = "Error scheduling task: $($_.Exception.Message)"
32+
}
33+
} else {
34+
$Result = Add-CIPPScheduledTask -Task $Request.Body -Headers $Request.Headers -hidden $hidden -DisallowDuplicateName $Request.Query.DisallowDuplicateName
35+
Write-LogMessage -headers $Request.Headers -API $APINAME -message $Result -Sev 'Info'
36+
}
2037
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
2138
StatusCode = [HttpStatusCode]::OK
2239
Body = @{ Results = $Result }
2340
})
24-
2541
}

0 commit comments

Comments
 (0)