@@ -24,106 +24,178 @@ function Add-CIPPScheduledTask {
2424 $Headers
2525 )
2626
27- $Table = Get-CIPPTable - TableName ' ScheduledTasks'
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"
27+ try {
28+
29+ $Table = Get-CIPPTable - TableName ' ScheduledTasks'
30+
31+ if ($RunNow.IsPresent -and $RowKey ) {
32+ try {
33+ $Filter = " PartitionKey eq 'ScheduledTask' and RowKey eq '$ ( $RowKey ) '"
34+ $ExistingTask = (Get-CIPPAzDataTableEntity @Table - Filter $Filter )
35+ $ExistingTask.ScheduledTime = [int64 ](([datetime ]::UtcNow) - (Get-Date ' 1/1/1970' )).TotalSeconds
36+ $ExistingTask.TaskState = ' Planned'
37+ Add-CIPPAzDataTableEntity @Table - Entity $ExistingTask - Force
38+ Write-LogMessage - headers $Headers - API ' RunNow' - message " Task $ ( $ExistingTask.Name ) scheduled to run now" - Sev ' Info' - Tenant $ExistingTask.Tenant
39+ return " Task $ ( $ExistingTask.Name ) scheduled to run now"
40+ } catch {
41+ $ErrorMessage = Get-NormalizedError - Message $_.Exception.Message
42+ Write-LogMessage - headers $Headers - API ' RunNow' - message " Could not run task: $ErrorMessage " - Sev ' Error'
43+ return " Could not run task: $ErrorMessage "
4944 }
50- }
45+ } else {
46+ if ($DisallowDuplicateName ) {
47+ $Filter = " PartitionKey eq 'ScheduledTask' and Name eq '$ ( $Task.Name ) '"
48+ $ExistingTask = (Get-CIPPAzDataTableEntity @Table - Filter $Filter )
49+ if ($ExistingTask ) {
50+ return " Task with name $ ( $Task.Name ) already exists"
51+ }
52+ }
53+
54+ $propertiesToCheck = @ (' Webhook' , ' Email' , ' PSA' )
55+ $PostExecutionObject = ($propertiesToCheck | Where-Object { $task.PostExecution .$_ -eq $true })
56+ $PostExecution = $PostExecutionObject ? ($PostExecutionObject -join ' ,' ) : ($Task.PostExecution.value -join ' ,' )
57+ $Parameters = [System.Collections.Hashtable ]@ {}
58+ foreach ($Key in $task.Parameters.PSObject.Properties.Name ) {
59+ $Param = $task.Parameters .$Key
60+
61+ if ($null -eq $Param -or $Param -eq ' ' -or ($Param | Measure-Object ).Count -eq 0 ) {
62+ continue
63+ }
5164
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
65+ # handle different object types in params
66+ if ($Param -is [System.Collections.IDictionary ] -or $Param [0 ].Key) {
67+ Write-Information " Parameter $Key is a hashtable"
68+ $ht = @ {}
69+ foreach ($p in $Param.GetEnumerator ()) {
70+ $ht [$p.Key ] = $p.Value
71+ }
72+ $Parameters [$Key ] = [PSCustomObject ]$ht
73+ Write-Information " Converted $Key to PSObject $ ( $Parameters [$Key ] | ConvertTo-Json - Compress) "
74+ } elseif ($Param -is [System.Object []] -and -not ($Param -is [string ])) {
75+ Write-Information " Parameter $Key is an enumerable object"
76+ $Param = $Param | ForEach-Object {
77+ if ($null -eq $_ ) {
78+ # Skip null entries
79+ return
80+ }
81+ if ($_ -is [System.Collections.IDictionary ]) {
82+ [PSCustomObject ]$_
83+ } elseif ($_ -is [PSCustomObject ]) {
84+ $_
85+ } else {
86+ $_
87+ }
88+ } | Where-Object { $null -ne $_ }
89+ $Parameters [$Key ] = $Param
90+ } else {
91+ Write-Information " Parameter $Key is a simple value"
92+ $Parameters [$Key ] = $Param
93+ }
94+ }
5895
59- if ($null -eq $Param -or $Param -eq ' ' -or ( $Param | Measure-Object ).Count -eq 0 ) {
60- continue
96+ if ($Headers ) {
97+ $Parameters .Headers = $Headers | Select-Object - Property ' x-forwarded-for ' , ' x-ms-client-principal ' , ' x-ms-client-principal-idp ' , ' x-ms-client-principal-name '
6198 }
62- if ($Param -is [System.Collections.IDictionary ] -or $Param.Key ) {
63- $ht = @ {}
64- foreach ($p in $Param.GetEnumerator ()) {
65- $ht [$p.Key ] = $p.Value
99+
100+ $Parameters = ($Parameters | ConvertTo-Json - Depth 10 - Compress)
101+ $AdditionalProperties = [System.Collections.Hashtable ]@ {}
102+ foreach ($Prop in $task.AdditionalProperties ) {
103+ if ($null -eq $Prop.Value -or $Prop.Value -eq ' ' -or ($Prop.Value | Measure-Object ).Count -eq 0 ) {
104+ continue
66105 }
67- $Parameters [$Key ] = [PSCustomObject ]$ht
106+ $AdditionalProperties [$Prop.Key ] = $Prop.Value
107+ }
108+ $AdditionalProperties = ([PSCustomObject ]$AdditionalProperties | ConvertTo-Json - Compress)
109+ if ($Parameters -eq ' null' ) { $Parameters = ' ' }
110+ if (! $Task.RowKey ) {
111+ $RowKey = (New-Guid ).Guid
68112 } else {
69- $Parameters [ $Key ] = $Param
113+ $RowKey = $Task .RowKey
70114 }
71- }
72115
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- }
116+ $Recurrence = if ([string ]::IsNullOrEmpty($task.Recurrence.value )) {
117+ $task.Recurrence
118+ } else {
119+ $task.Recurrence.value
120+ }
76121
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- }
122+ if ([int64 ]$task.ScheduledTime -eq 0 -or [string ]::IsNullOrEmpty($task.ScheduledTime )) {
123+ $task.ScheduledTime = [int64 ](([datetime ]::UtcNow) - (Get-Date ' 1/1/1970' )).TotalSeconds
124+ }
125+ $excludedTenants = if ($task.excludedTenants.value ) {
126+ $task.excludedTenants.value -join ' ,'
127+ }
89128
90- $Recurrence = if ([string ]::IsNullOrEmpty($task.Recurrence.value )) {
91- $task.Recurrence
92- } else {
93- $task.Recurrence.value
94- }
129+ # Handle tenant filter - support both single tenant and tenant groups
130+ $tenantFilter = $task.TenantFilter.value ? $task.TenantFilter.value : $task.TenantFilter
131+ $originalTenantFilter = $task.TenantFilter
95132
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 "
133+ # If tenant filter is a complex object (from form), extract the value
134+ if ($tenantFilter -is [PSCustomObject ] -and $tenantFilter.value ) {
135+ $originalTenantFilter = $tenantFilter
136+ $tenantFilter = $tenantFilter.value
137+ }
138+
139+ # If tenant filter is a string but still seems to be JSON, try to parse it
140+ if ($tenantFilter -is [string ] -and $tenantFilter.StartsWith (' {' )) {
141+ try {
142+ $parsedTenantFilter = $tenantFilter | ConvertFrom-Json
143+ if ($parsedTenantFilter.value ) {
144+ $originalTenantFilter = $parsedTenantFilter
145+ $tenantFilter = $parsedTenantFilter.value
146+ }
147+ } catch {
148+ # If parsing fails, use the string as is
149+ Write-Warning " Could not parse tenant filter JSON: $tenantFilter "
150+ }
151+ }
152+
153+ $entity = @ {
154+ PartitionKey = [string ]' ScheduledTask'
155+ TaskState = [string ]' Planned'
156+ RowKey = [string ]$RowKey
157+ Tenant = [string ]$tenantFilter
158+ excludedTenants = [string ]$excludedTenants
159+ Name = [string ]$task.Name
160+ Command = [string ]$task.Command.value
161+ Parameters = [string ]$Parameters
162+ ScheduledTime = [string ]$task.ScheduledTime
163+ Recurrence = [string ]$Recurrence
164+ PostExecution = [string ]$PostExecution
165+ AdditionalProperties = [string ]$AdditionalProperties
166+ Hidden = [bool ]$Hidden
167+ Results = ' Planned'
168+ }
169+
170+ # Store the original tenant filter for group expansion during execution
171+ if ($originalTenantFilter -is [PSCustomObject ] -and $originalTenantFilter.type -eq ' Group' ) {
172+ $entity [' TenantGroup' ] = [string ]($originalTenantFilter | ConvertTo-Json - Compress)
173+ } elseif ($originalTenantFilter -is [string ] -and $originalTenantFilter.StartsWith (' {' )) {
174+ # Check if it's a serialized group object
175+ try {
176+ $parsedOriginal = $originalTenantFilter | ConvertFrom-Json
177+ if ($parsedOriginal.type -eq ' Group' ) {
178+ $entity [' TenantGroup' ] = [string ]$originalTenantFilter
179+ }
180+ } catch {
181+ # Not a JSON object, ignore
182+ }
183+ }
184+ if ($SyncType ) {
185+ $entity.SyncType = $SyncType
186+ }
187+ try {
188+ Add-CIPPAzDataTableEntity @Table - Entity $entity - Force
189+ } catch {
190+ $ErrorMessage = Get-NormalizedError - Message $_.Exception.Message
191+ return " Could not add task: $ErrorMessage "
192+ }
193+ return " Successfully added task: $ ( $entity.Name ) "
126194 }
127- return " Successfully added task: $ ( $entity.Name ) "
195+ } catch {
196+ Write-Warning " Failed to add scheduled task: $ ( $_.Exception.Message ) "
197+ Write-Information $_.InvocationInfo.PositionMessage
198+ $ErrorMessage = Get-NormalizedError - Message $_.Exception.Message
199+ throw " Could not add task: $ErrorMessage "
128200 }
129201}
0 commit comments