@@ -24,106 +24,178 @@ function Add-CIPPScheduledTask {
24
24
$Headers
25
25
)
26
26
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 "
49
44
}
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
+ }
51
64
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
+ }
58
95
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 '
61
98
}
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
66
105
}
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
68
112
} else {
69
- $Parameters [ $Key ] = $Param
113
+ $RowKey = $Task .RowKey
70
114
}
71
- }
72
115
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
+ }
76
121
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
+ }
89
128
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
95
132
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 ) "
126
194
}
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 "
128
200
}
129
201
}
0 commit comments