From ef08fa3b8b4d6f20540959b9fbd177ba7659502b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 4 Sep 2024 10:48:41 -0400 Subject: [PATCH 01/33] Fix boolean issue with forwarding --- Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 index f404764897db..25bc5b886fee 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPOffboardingJob.ps1 @@ -50,10 +50,11 @@ function Invoke-CIPPOffboardingJob { Set-CIPPOutOfOffice -tenantFilter $tenantFilter -userid $username -InternalMessage $Options.OOO -ExternalMessage $Options.OOO -ExecutingUser $ExecutingUser -APIName $APIName -state 'Enabled' } { $_.'forward' -ne '' } { - if (!$options.keepcopy) { + if (!$Options.keepCopy) { Set-CIPPForwarding -userid $userid -username $username -tenantFilter $Tenantfilter -Forward $Options.forward -ExecutingUser $ExecutingUser -APIName $APIName } else { - Set-CIPPForwarding -userid $userid -username $username -tenantFilter $Tenantfilter -Forward $Options.forward -KeepCopy [boolean]($Options.keepCopy) -ExecutingUser $ExecutingUser -APIName $APIName + $KeepCopy = [boolean]$Options.keepCopy + Set-CIPPForwarding -userid $userid -username $username -tenantFilter $Tenantfilter -Forward $Options.forward -KeepCopy $KeepCopy -ExecutingUser $ExecutingUser -APIName $APIName } } { $_.'RemoveLicenses' -eq 'true' } { From b74bffe6eb52c900eb9ce1b578e55001ef781e14 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 01:12:25 -0400 Subject: [PATCH 02/33] CIPP Timers and cleanup --- .../function.json | 15 --- AddChocoApp_OrchestrationStarterTimer/run.ps1 | 7 -- .../function.json | 15 --- .../run.ps1 | 3 - .../function.json | 4 +- CIPPTimers.json | 96 +++++++++++++++++ Cleanup_OldAuditLogs/function.json | 15 --- Cleanup_OldAuditLogs/run.ps1 | 9 -- .../function.json | 15 --- Domain_OrchestrationStarterTimer/run.ps1 | 8 -- ExecGDAPInviteApproved_Timer/function.json | 15 --- ExecGDAPInviteApproved_Timer/run.ps1 | 5 - ListGenericAllTenants/function.json | 10 -- ListGenericAllTenants/run.ps1 | 42 -------- .../{ => Standards}/Push-CIPPStandard.ps1 | 0 .../Invoke-ListMailboxStatistics.ps1 | 66 ------------ .../Start-AuditLogOrchestrator.ps1 | 34 ++++++ .../Start-SchedulerOrchestrator.ps1 | 63 +++++++++++ .../Start-StandardsOrchestrator.ps1 | 13 +++ .../Start-UpdatePermissionsOrchestrator.ps1 | 35 ++++++ .../Start-UserTasksOrchestrator.ps1 | 83 +++++++++++++++ .../Start-WebhookOrchestrator.ps1 | 40 +++++++ .../Push-CIPPFunctionProcessor.ps1 | 23 ++++ ...tart-CIPPGraphSubscriptionCleanupTimer.ps1 | 19 ++++ ...tart-CIPPGraphSubscriptionRenewalTimer.ps1 | 17 +++ .../Timer Functions/Start-CIPPStatsTimer.ps1 | 30 ++++++ .../Start-UpdateTokensTimer.ps1 | 41 +++++++ .../Public/Get-CIPPTimerFunctions.ps1 | 100 ++++++++++++++++++ .../Public/GraphHelper/New-ExoRequest.ps1 | 3 +- Modules/CIPPCore/lib/NCrontab.Advanced.dll | Bin 0 -> 31232 bytes Modules/CippEntrypoints/CippEntrypoints.psm1 | 29 ++++- Scheduler_GetQueue/run.ps1 | 56 ---------- Scheduler_GetWebhooks/function.json | 15 --- Scheduler_GetWebhooks/run.ps1 | 26 ----- Scheduler_PollAuditLogs/function.json | 15 --- Scheduler_PollAuditLogs/run.ps1 | 34 ------ .../function.json | 10 -- Scheduler_RenewGraphSubscriptions/run.ps1 | 10 -- Scheduler_Standards/function.json | 15 --- Scheduler_Standards/run.ps1 | 6 -- Scheduler_UserTasks/function.json | 15 --- Scheduler_UserTasks/run.ps1 | 75 ------------- SendStats/function.json | 10 -- SendStats/run.ps1 | 23 ---- UpdatePermissions/function.json | 15 --- UpdatePermissions/run.ps1 | 30 ------ UpdateTokens/function.json | 10 -- UpdateTokens/run.ps1 | 33 ------ Z_CIPPHttpTrigger/function.json | 6 -- Z_CIPPQueueTrigger/function.json | 7 +- 50 files changed, 628 insertions(+), 638 deletions(-) delete mode 100644 AddChocoApp_OrchestrationStarterTimer/function.json delete mode 100644 AddChocoApp_OrchestrationStarterTimer/run.ps1 delete mode 100644 BestPracticeAnalyser_OrchestrationStarterTimer/function.json delete mode 100644 BestPracticeAnalyser_OrchestrationStarterTimer/run.ps1 rename {Scheduler_GetQueue => CIPPTimer}/function.json (58%) create mode 100644 CIPPTimers.json delete mode 100644 Cleanup_OldAuditLogs/function.json delete mode 100644 Cleanup_OldAuditLogs/run.ps1 delete mode 100644 Domain_OrchestrationStarterTimer/function.json delete mode 100644 Domain_OrchestrationStarterTimer/run.ps1 delete mode 100644 ExecGDAPInviteApproved_Timer/function.json delete mode 100644 ExecGDAPInviteApproved_Timer/run.ps1 delete mode 100644 ListGenericAllTenants/function.json delete mode 100644 ListGenericAllTenants/run.ps1 rename Modules/CIPPCore/Public/Entrypoints/Activity Triggers/{ => Standards}/Push-CIPPStandard.ps1 (100%) delete mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxStatistics.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-SchedulerOrchestrator.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-StandardsOrchestrator.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UpdatePermissionsOrchestrator.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-WebhookOrchestrator.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPGraphSubscriptionCleanupTimer.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPGraphSubscriptionRenewalTimer.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPStatsTimer.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-UpdateTokensTimer.ps1 create mode 100644 Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 create mode 100644 Modules/CIPPCore/lib/NCrontab.Advanced.dll delete mode 100644 Scheduler_GetQueue/run.ps1 delete mode 100644 Scheduler_GetWebhooks/function.json delete mode 100644 Scheduler_GetWebhooks/run.ps1 delete mode 100644 Scheduler_PollAuditLogs/function.json delete mode 100644 Scheduler_PollAuditLogs/run.ps1 delete mode 100644 Scheduler_RenewGraphSubscriptions/function.json delete mode 100644 Scheduler_RenewGraphSubscriptions/run.ps1 delete mode 100644 Scheduler_Standards/function.json delete mode 100644 Scheduler_Standards/run.ps1 delete mode 100644 Scheduler_UserTasks/function.json delete mode 100644 Scheduler_UserTasks/run.ps1 delete mode 100644 SendStats/function.json delete mode 100644 SendStats/run.ps1 delete mode 100644 UpdatePermissions/function.json delete mode 100644 UpdatePermissions/run.ps1 delete mode 100644 UpdateTokens/function.json delete mode 100644 UpdateTokens/run.ps1 diff --git a/AddChocoApp_OrchestrationStarterTimer/function.json b/AddChocoApp_OrchestrationStarterTimer/function.json deleted file mode 100644 index d96b29b5a0ed..000000000000 --- a/AddChocoApp_OrchestrationStarterTimer/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 0 12 * * *" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} \ No newline at end of file diff --git a/AddChocoApp_OrchestrationStarterTimer/run.ps1 b/AddChocoApp_OrchestrationStarterTimer/run.ps1 deleted file mode 100644 index 85d820ceb522..000000000000 --- a/AddChocoApp_OrchestrationStarterTimer/run.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -param($Timer) - -try { - Start-ApplicationOrchestrator -} catch { - Write-Host "AddChocoApp_OrchestratorStarterTimer Exception: $($_.Exception.Message)" -} diff --git a/BestPracticeAnalyser_OrchestrationStarterTimer/function.json b/BestPracticeAnalyser_OrchestrationStarterTimer/function.json deleted file mode 100644 index cbdf59ee8bba..000000000000 --- a/BestPracticeAnalyser_OrchestrationStarterTimer/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 0 3 * * *" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} \ No newline at end of file diff --git a/BestPracticeAnalyser_OrchestrationStarterTimer/run.ps1 b/BestPracticeAnalyser_OrchestrationStarterTimer/run.ps1 deleted file mode 100644 index 5b9e12f54258..000000000000 --- a/BestPracticeAnalyser_OrchestrationStarterTimer/run.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -param($Timer) - -Start-BPAOrchestrator diff --git a/Scheduler_GetQueue/function.json b/CIPPTimer/function.json similarity index 58% rename from Scheduler_GetQueue/function.json rename to CIPPTimer/function.json index 56e4cf0cfda1..55e4edb35f11 100644 --- a/Scheduler_GetQueue/function.json +++ b/CIPPTimer/function.json @@ -1,8 +1,10 @@ { + "scriptFile": "../Modules/CippEntrypoints/CippEntrypoints.psm1", + "entryPoint": "Receive-CippTimerTrigger", "bindings": [ { "name": "Timer", - "schedule": "0 0 * * * *", + "schedule": "0 0/15 * * * *", "direction": "in", "type": "timerTrigger" }, diff --git a/CIPPTimers.json b/CIPPTimers.json new file mode 100644 index 000000000000..e5f712c834e4 --- /dev/null +++ b/CIPPTimers.json @@ -0,0 +1,96 @@ +[ + { + "Command": "Start-UserTasksOrchestrator", + "Description": "Orchestrator to process user scheduled tasks", + "Cron": "0 */15 * * * *", + "Priority": 1, + "RunOnProcessor": true + }, + { + "Command": "Start-AuditLogOrchestrator", + "Description": "Orchestrator to process audit logs", + "Cron": "0 */15 * * * *", + "Priority": 2, + "RunOnProcessor": true + }, + { + "Command": "Start-WebhookOrchestrator", + "Description": "Orchestrator to process webhooks", + "Cron": "0 */15 * * * *", + "Priority": 3, + "RunOnProcessor": true + }, + { + "Command": "Start-StandardsOrchestrator", + "Description": "Orchestrator to process standards", + "Cron": "0 0 */4 * * *", + "Priority": 4, + "RunOnProcessor": true + }, + { + "Command": "Start-CIPPGraphSubscriptionCleanupTimer", + "Description": "Orchestrator to cleanup old Graph subscriptions", + "Cron": "0 0 0 * * *", + "Priority": 5, + "RunOnProcessor": true + }, + { + "Command": "Start-SchedulerOrchestrator", + "Description": "Orchestrator to process system scheduled tasks", + "Cron": "0 0 * * * *", + "Priority": 6, + "RunOnProcessor": true + }, + { + "Command": "Set-CIPPGDAPInviteGroups", + "Description": "Orchestrator to map the groups for GDAP invites", + "Cron": "0 0 */3 * * *", + "Priority": 5, + "RunOnProcessor": true + }, + { + "Command": "Start-UpdateTokensTimer", + "Description": "Orchestrator to update tokens", + "Cron": "0 0 0 * * 0", + "Priority": 7, + "RunOnProcessor": true, + "IsSystem": true + }, + { + "Command": "Start-CIPPGraphSubscriptionRenewalTimer", + "Description": "Orchestrator to renew Graph subscriptions", + "Cron": "0 10 * * * *", + "Priority": 8, + "RunOnProcessor": true + }, + { + "Command": "Start-DomainOrchestrator", + "Description": "Orchestrator to process domains", + "Cron": "0 0 0 * * *", + "Priority": 10, + "RunOnProcessor": true + }, + { + "Command": "Start-UpdatePermissionsOrchestrator", + "Description": "Orchestrator to update CPV permissions", + "Cron": "0 0 0 * * *", + "Priority": 10, + "RunOnProcessor": true, + "IsSystem": true + }, + { + "Command": "Start-BPAOrchestrator", + "Description": "Orchestrator to process BPA reports", + "Cron": "0 0 3 * * *", + "Priority": 10, + "RunOnProcessor": true + }, + { + "Command": "Start-CIPPStatsTimer", + "Description": "Timer to process CIPP stats", + "Cron": "0 0 0 * * *", + "Priority": 15, + "RunOnProcessor": true, + "IsSystem": true + } +] diff --git a/Cleanup_OldAuditLogs/function.json b/Cleanup_OldAuditLogs/function.json deleted file mode 100644 index 7e97fe568d29..000000000000 --- a/Cleanup_OldAuditLogs/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 0 0 * * *" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} diff --git a/Cleanup_OldAuditLogs/run.ps1 b/Cleanup_OldAuditLogs/run.ps1 deleted file mode 100644 index cc677d77c8da..000000000000 --- a/Cleanup_OldAuditLogs/run.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -# Input bindings are passed in via param block. -param($Timer) - -try { - $Tenants = Get-Tenants -IncludeAll | Where-Object { $_.customerId -ne $env:TenantID -and $_.Excluded -eq $false } - $Tenants | ForEach-Object { - Remove-CIPPGraphSubscription -cleanup $true -TenantFilter $_.defaultDomainName - } -} catch {} diff --git a/Domain_OrchestrationStarterTimer/function.json b/Domain_OrchestrationStarterTimer/function.json deleted file mode 100644 index f5df3587c4c8..000000000000 --- a/Domain_OrchestrationStarterTimer/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 30 4 * * *" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} \ No newline at end of file diff --git a/Domain_OrchestrationStarterTimer/run.ps1 b/Domain_OrchestrationStarterTimer/run.ps1 deleted file mode 100644 index 259b225d9d75..000000000000 --- a/Domain_OrchestrationStarterTimer/run.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -param($Timer) - -if ($env:DEV_SKIP_DOMAIN_TIMER) { - Write-Host 'Skipping DomainAnalyser timer' - exit 0 -} - -Start-DomainOrchestrator diff --git a/ExecGDAPInviteApproved_Timer/function.json b/ExecGDAPInviteApproved_Timer/function.json deleted file mode 100644 index f8904bbb0a7f..000000000000 --- a/ExecGDAPInviteApproved_Timer/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 0 */3 * * *" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} diff --git a/ExecGDAPInviteApproved_Timer/run.ps1 b/ExecGDAPInviteApproved_Timer/run.ps1 deleted file mode 100644 index 08370014869f..000000000000 --- a/ExecGDAPInviteApproved_Timer/run.ps1 +++ /dev/null @@ -1,5 +0,0 @@ -using namespace System.Net - -param($Timer) - -Set-CIPPGDAPInviteGroups diff --git a/ListGenericAllTenants/function.json b/ListGenericAllTenants/function.json deleted file mode 100644 index 4937476839a0..000000000000 --- a/ListGenericAllTenants/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "generalAllTenantQueue" - } - ] -} diff --git a/ListGenericAllTenants/run.ps1 b/ListGenericAllTenants/run.ps1 deleted file mode 100644 index 71023d396ff4..000000000000 --- a/ListGenericAllTenants/run.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -# Input bindings are passed in via param block. -param([string]$QueueItem, $TriggerMetadata) - -# Write out the queue message and metadata to the information log. -Write-Host "PowerShell queue trigger function processed work item: $QueueItem" -$TableURLName = ($QueueItem.tolower().split('?').Split('/') | Select-Object -First 1).toString() -$QueueKey = (Invoke-ListCippQueue | Where-Object -Property Name -EQ $TableURLName | Select-Object -Last 1).RowKey -Update-CippQueueEntry -RowKey $QueueKey -Status 'Started' -$Table = Get-CIPPTable -TableName "cache$TableURLName" -$fullUrl = "https://graph.microsoft.com/beta/$QueueItem" -Get-CIPPAzDataTableEntity @Table | Remove-AzDataTableEntity @table - -$RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module CIPPCore - try { - Write-Host $using:fullUrl - New-GraphGetRequest -uri $using:fullUrl -tenantid $_.defaultDomainName -ComplexFilter -ErrorAction Stop | Select-Object *, @{l = 'Tenant'; e = { $domainName } }, @{l = 'CippStatus'; e = { 'Good' } } - } - catch { - [PSCustomObject]@{ - Tenant = $domainName - CippStatus = "Could not connect to tenant. $($_.Exception.message)" - } - } -} - -Update-CippQueueEntry -RowKey $QueueKey -Status 'Processing' -foreach ($Request in $RawGraphRequest) { - $Json = ConvertTo-Json -Compress -InputObject $request - $GraphRequest = [PSCustomObject]@{ - Tenant = [string]$Request.tenant - RowKey = [string](New-Guid) - PartitionKey = [string]$URL - Data = [string]$Json - - } - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null -} - - -Update-CippQueueEntry -RowKey $QueueKey -Status 'Completed' diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPStandard.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPStandard.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-CIPPStandard.ps1 rename to Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPStandard.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxStatistics.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxStatistics.ps1 deleted file mode 100644 index 795812096c50..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxStatistics.ps1 +++ /dev/null @@ -1,66 +0,0 @@ -using namespace System.Net - -Function Invoke-ListMailboxStatistics { - <# - .FUNCTIONALITY - Entrypoint - .ROLE - Exchange.Mailbox.Read - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - $APIName = $TriggerMetadata.FunctionName - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - - - # Write to the Azure Functions log stream. - Write-Host 'PowerShell HTTP trigger function processed a request.' - - # Interact with query parameters or the body of the request. - $TenantFilter = $Request.Query.TenantFilter - try { - - $GraphRequest = if ($TenantFilter -ne 'AllTenants') { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getMailboxUsageDetail(period='D7')" -tenantid $TenantFilter | ConvertFrom-Csv | Select-Object @{ Name = 'UPN'; Expression = { $_.'User Principal Name' } }, - @{ Name = 'displayName'; Expression = { $_.'Display Name' } }, - @{ Name = 'MailboxType'; Expression = { $_.'Recipient Type' } }, - @{ Name = 'LastActive'; Expression = { $_.'Last Activity Date' } }, - @{ Name = 'UsedGB'; Expression = { [math]::round($_.'Storage Used (Byte)' / 1GB, 2) } }, - @{ Name = 'QuotaGB'; Expression = { [math]::round($_.'Prohibit Send/Receive Quota (Byte)' / 1GB, 2) } }, - @{ Name = 'ItemCount'; Expression = { $_.'Item Count' } }, - @{ Name = 'HasArchive'; Expression = { If (($_.'Has Archive').ToLower() -eq 'true') { [bool]$true } else { [bool]$false } } } - $StatusCode = [HttpStatusCode]::OK - } else { - $Table = Get-CIPPTable -TableName 'cachereports' - $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) - if (!$Rows) { - $Queue = New-CippQueueEntry -Name 'Reports' -Link '/email/reports/mailbox-statistics?customerId=AllTenants' - Push-OutputBinding -Name mailboxstats -Value "reports/getMailboxUsageDetail(period='D7')?`$format=application/json" - [PSCustomObject]@{ - Tenant = 'Loading data for all tenants. Please check back after the job completes' - } - $StatusCode = [HttpStatusCode]::OK - } else { - $Rows.Data | ConvertFrom-Json | Select-Object *, @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, - @{ Name = 'MailboxType'; Expression = { $_.'RecipientType' } }, - @{ Name = 'LastActive'; Expression = { $_.'LastActivityDate' } }, - @{ Name = 'UsedGB'; Expression = { [math]::round($_.'storageUsedInBytes' / 1GB, 2) } }, - @{ Name = 'QuotaGB'; Expression = { [math]::round($_.'prohibitSendReceiveQuotaInBytes' / 1GB, 2) } } - $StatusCode = [HttpStatusCode]::OK - } - } - - - } catch { - $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - $StatusCode = [HttpStatusCode]::Forbidden - $GraphRequest = $ErrorMessage - } - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @($GraphRequest) - }) -clobber - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 new file mode 100644 index 000000000000..b17c0c00f3ad --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 @@ -0,0 +1,34 @@ +function Start-AuditLogOrchestrator { + <# + .SYNOPSIS + Start the Audit Log Polling Orchestrator + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + try { + $webhookTable = Get-CIPPTable -tablename webhookTable + $Webhooks = Get-CIPPAzDataTableEntity @webhookTable -Filter "Version eq '3'" | Where-Object { $_.Resource -match '^Audit' -and $_.Status -ne 'Disabled' } + if (($Webhooks | Measure-Object).Count -eq 0) { + Write-Information 'No webhook subscriptions found. Exiting.' + return + } + + $StartTime = (Get-Date).AddMinutes(-30) + $EndTime = Get-Date + + $Queue = New-CippQueueEntry -Name 'Audit Log Collection' -Reference 'AuditLogCollection' -TotalTasks ($Webhooks | Sort-Object -Property PartitionKey -Unique | Measure-Object).Count + + $Batch = $Webhooks | Sort-Object -Property PartitionKey -Unique | Select-Object @{Name = 'TenantFilter'; Expression = { $_.PartitionKey } }, @{Name = 'QueueId'; Expression = { $Queue.RowKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogTenant' } }, @{Name = 'StartTime'; Expression = { $StartTime } }, @{Name = 'EndTime'; Expression = { $EndTime } } + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'AuditLogs' + Batch = @($Batch) + SkipLog = $true + } + if ($PSCmdlet.ShouldProcess('Start-AuditLogPolling', 'Starting Audit Log Polling')) { + Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) + } + } catch { + Write-LogMessage -API 'Webhooks' -message 'Error processing webhooks' -sev Error -LogData (Get-CippException -Exception $_) + Write-Information ( 'Webhook error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-SchedulerOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-SchedulerOrchestrator.ps1 new file mode 100644 index 000000000000..c7463ec28e08 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-SchedulerOrchestrator.ps1 @@ -0,0 +1,63 @@ +function Start-SchedulerOrchestrator { + <# + .SYNOPSIS + Start the Scheduler Orchestrator + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + + $Table = Get-CIPPTable -TableName SchedulerConfig + $Tenants = Get-CIPPAzDataTableEntity @Table | Where-Object -Property PartitionKey -NE 'WebhookAlert' + + $Tasks = foreach ($Tenant in $Tenants) { + if ($Tenant.tenant -ne 'AllTenants') { + [pscustomobject]@{ + Tenant = $Tenant.tenant + Tag = 'SingleTenant' + TenantID = $Tenant.tenantid + Type = $Tenant.type + RowKey = $Tenant.RowKey + } + } else { + Write-Information 'All tenants, doing them all' + $TenantList = Get-Tenants + foreach ($t in $TenantList) { + [pscustomobject]@{ + Tenant = $t.defaultDomainName + Tag = 'AllTenants' + TenantID = $t.customerId + Type = $Tenant.type + RowKey = $Tenant.RowKey + } + } + } + } + + if (($Tasks | Measure-Object).Count -eq 0) { + return + } + + $Queue = New-CippQueueEntry -Name 'Scheduler' -TotalTasks ($Tasks | Measure-Object).Count + + $Batch = foreach ($Task in $Tasks) { + [pscustomobject]@{ + Tenant = $task.tenant + Tenantid = $task.tenantid + Tag = $task.tag + Type = $task.type + QueueId = $Queue.RowKey + SchedulerRow = $Task.RowKey + QueueName = '{0} - {1}' -f $Task.Type, $task.tenant + FunctionName = "Scheduler$($Task.Type)" + } + } + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'SchedulerOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + + if ($PSCmdlet.ShouldProcess('Start-ScheduleOrchestrator', 'Starting Scheduler Orchestrator')) { + Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-StandardsOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-StandardsOrchestrator.ps1 new file mode 100644 index 000000000000..76e689ef4952 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-StandardsOrchestrator.ps1 @@ -0,0 +1,13 @@ +function Start-StandardsOrchestrator { + <# + .SYNOPSIS + Start the Standards Orchestrator + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + + if ($PSCmdlet.ShouldProcess('Start-StandardsOrchestrator', 'Starting Standards Orchestrator')) { + Write-LogMessage -API 'Standards' -message 'Starting Standards Schedule' -sev Info + Invoke-CIPPStandardsRun -tenantfilter 'allTenants' + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UpdatePermissionsOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UpdatePermissionsOrchestrator.ps1 new file mode 100644 index 000000000000..08495c8fb763 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UpdatePermissionsOrchestrator.ps1 @@ -0,0 +1,35 @@ +function Start-UpdatePermissionsOrchestrator { + <# + .SYNOPSIS + Start the Update Permissions Orchestrator + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + + try { + $Tenants = Get-Tenants -IncludeAll | Where-Object { $_.customerId -ne $env:TenantID -and $_.Excluded -eq $false } + $CPVTable = Get-CIPPTable -TableName cpvtenants + $CPVRows = Get-CIPPAzDataTableEntity @CPVTable + $ModuleRoot = (Get-Module CIPPCore).ModuleBase + $SAMManifest = Get-Item -Path "$ModuleRoot\Public\SAMManifest.json" + $AdditionalPermissions = Get-Item -Path "$ModuleRoot\Public\AdditionalPermissions.json" + $Tenants = $Tenants | ForEach-Object { + $CPVRow = $CPVRows | Where-Object -Property Tenant -EQ $_.customerId + if (!$CPVRow -or $env:ApplicationID -notin $CPVRow.applicationId -or $SAMManifest.LastWriteTime.ToUniversalTime() -gt $CPVRow.Timestamp.DateTime -or $AdditionalPermissions.LastWriteTime.ToUniversalTime() -ge $CPVRow.Timestamp.DateTime -or $CPVRow.Timestamp.DateTime -le (Get-Date).AddDays(-7).ToUniversalTime() -or !$_.defaultDomainName) { + $_ + } + } + $TenantCount = ($Tenants | Measure-Object).Count + if ($TenantCount -gt 0) { + $Queue = New-CippQueueEntry -Name 'Update Permissions' -TotalTasks $TenantCount + $TenantBatch = $Tenants | Select-Object defaultDomainName, customerId, displayName, @{n = 'FunctionName'; exp = { 'UpdatePermissionsQueue' } }, @{n = 'QueueId'; exp = { $Queue.RowKey } } + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'UpdatePermissionsOrchestrator' + Batch = @($TenantBatch) + } + Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) + } else { + Write-Information 'No tenants require permissions update' + } + } catch {} +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 new file mode 100644 index 000000000000..10aebb5c0767 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-UserTasksOrchestrator.ps1 @@ -0,0 +1,83 @@ +function Start-UserTasksOrchestrator { + <# + .SYNOPSIS + Start the User Tasks Orchestrator + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + + $Table = Get-CippTable -tablename 'ScheduledTasks' + $Filter = "TaskState eq 'Planned' or TaskState eq 'Failed - Planned'" + $tasks = Get-CIPPAzDataTableEntity @Table -Filter $Filter + $Batch = [System.Collections.Generic.List[object]]::new() + $TenantList = Get-Tenants -IncludeErrors + foreach ($task in $tasks) { + $tenant = $task.Tenant + $currentUnixTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds + if ($currentUnixTime -ge $task.ScheduledTime) { + try { + $null = Update-AzDataTableEntity @Table -Entity @{ + PartitionKey = $task.PartitionKey + RowKey = $task.RowKey + ExecutedTime = "$currentUnixTime" + TaskState = 'Running' + } + $task.Parameters = $task.Parameters | ConvertFrom-Json -AsHashtable + $task.AdditionalProperties = $task.AdditionalProperties | ConvertFrom-Json + + if (!$task.Parameters) { $task.Parameters = @{} } + $ScheduledCommand = [pscustomobject]@{ + Command = $task.Command + Parameters = $task.Parameters + TaskInfo = $task + FunctionName = 'ExecScheduledCommand' + } + + if ($task.Tenant -eq 'AllTenants') { + $AllTenantCommands = foreach ($Tenant in $TenantList) { + $NewParams = $task.Parameters.Clone() + $NewParams.TenantFilter = $Tenant.defaultDomainName + [pscustomobject]@{ + Command = $task.Command + Parameters = $NewParams + TaskInfo = $task + FunctionName = 'ExecScheduledCommand' + } + } + $Batch.AddRange($AllTenantCommands) + } else { + $ScheduledCommand.Parameters['TenantFilter'] = $task.Tenant + $Batch.Add($ScheduledCommand) + } + } catch { + $errorMessage = $_.Exception.Message + + $null = Update-AzDataTableEntity @Table -Entity @{ + PartitionKey = $task.PartitionKey + RowKey = $task.RowKey + Results = "$errorMessage" + ExecutedTime = "$currentUnixTime" + TaskState = 'Failed' + } + Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Failed to execute task $($task.Name): $errorMessage" -sev Error + } + } + } + if (($Batch | Measure-Object).Count -gt 0) { + # Create queue entry + $Queue = New-CippQueueEntry -Name 'Scheduled Tasks' -TotalTasks ($Batch | Measure-Object).Count + $QueueId = $Queue.RowKey + $Batch = $Batch | Select-Object *, @{Name = 'QueueId'; Expression = { $QueueId } }, @{Name = 'QueueName'; Expression = { '{0} - {1}' -f $_.TaskInfo.Name, ($_.TaskInfo.Tenant -ne 'AllTenants' ? $_.TaskInfo.Tenant : $_.Parameters.TenantFilter) } } + + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'UserTaskOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + #Write-Host ($InputObject | ConvertTo-Json -Depth 10) + + if ($PSCmdlet.ShouldProcess('Start-UserTasksOrchestrator', 'Starting User Tasks Orchestrator')) { + Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 10 -Compress) + } + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-WebhookOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-WebhookOrchestrator.ps1 new file mode 100644 index 000000000000..6f6a6499755e --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-WebhookOrchestrator.ps1 @@ -0,0 +1,40 @@ +function Start-WebhookOrchestrator { + <# + .SYNOPSIS + Start the Webhook Orchestrator + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + try { + $webhookTable = Get-CIPPTable -tablename webhookTable + $Webhooks = Get-CIPPAzDataTableEntity @webhookTable -Property PartitionKey, RowKey + if (($Webhooks | Measure-Object).Count -eq 0) { + Write-Information 'No webhook subscriptions found. Exiting.' + return + } + + $WebhookIncomingTable = Get-CIPPTable -TableName WebhookIncoming + $WebhookIncoming = Get-CIPPAzDataTableEntity @WebhookIncomingTable -Property RowKey + if (($WebhookIncoming | Measure-Object).Count -eq 0) { + Write-Information 'No webhook incoming found. Exiting.' + return + } + + Write-Information 'Processing webhooks' + + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'WebhookOrchestrator' + QueueFunction = @{ + FunctionName = 'GetPendingWebhooks' + } + SkipLog = $true + } + if ($PSCmdlet.ShouldProcess('Start-WebhookOrchestrator', 'Starting Webhook Orchestrator')) { + Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) + + } + } catch { + Write-LogMessage -API 'Webhooks' -message 'Error processing webhooks' -sev Error -LogData (Get-CippException -Exception $_) + Write-Information ( 'Webhook error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 new file mode 100644 index 000000000000..55751e4bb4a6 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 @@ -0,0 +1,23 @@ +function Push-CIPPFunctionProcessor { + <# + .SYNOPSIS + Starts a specified function on the processor node + #> + [CmdletBinding()] + param($QueueItem) + + $ConfigTable = Get-CIPPTable -tablename Config + $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" + + if ($Config -and $Config.state -eq $true) { + if ($env:CIPP_PROCESSOR -ne 'true' -and !$All.IsPresent) { + return + } + } + + if (Get-Command -Name $QueueItem.FunctionName -Module CIPPCore -ErrorAction SilentlyContinue) { + & $QueueItem.FunctionName + } else { + Write-Warning "Function $($QueueItem.FunctionName) not found" + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPGraphSubscriptionCleanupTimer.ps1 b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPGraphSubscriptionCleanupTimer.ps1 new file mode 100644 index 000000000000..65d1ea7c29e1 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPGraphSubscriptionCleanupTimer.ps1 @@ -0,0 +1,19 @@ +function Start-CIPPGraphSubscriptionCleanupTimer { + <# + .SYNOPSIS + Remove CIPP Graph Subscriptions for all tenants except the partner tenant. + + .DESCRIPTION + Remove CIPP Graph Subscriptions for all tenants except the partner tenant. + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + try { + $Tenants = Get-Tenants -IncludeAll | Where-Object { $_.customerId -ne $env:TenantID -and $_.Excluded -eq $false } + $Tenants | ForEach-Object { + if ($PSCmdlet.ShouldProcess($_.defaultDomainName, 'Remove-CIPPGraphSubscription')) { + Remove-CIPPGraphSubscription -cleanup $true -TenantFilter $_.defaultDomainName + } + } + } catch {} +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPGraphSubscriptionRenewalTimer.ps1 b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPGraphSubscriptionRenewalTimer.ps1 new file mode 100644 index 000000000000..ee96995ee6f4 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPGraphSubscriptionRenewalTimer.ps1 @@ -0,0 +1,17 @@ +function Start-CIPPGraphSubscriptionRenewalTimer { + <# + .SYNOPSIS + Start the Graph Subscription Renewal Timer + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + + if ($PSCmdlet.ShouldProcess('Start-CIPPGraphSubscriptionRenewalTimer', 'Starting Graph Subscription Renewal Timer')) { + try { + Write-LogMessage -API 'Scheduler_RenewGraphSubscriptions' -tenant 'none' -message 'Starting Graph Subscription Renewal' -sev Info + Invoke-CippGraphWebhookRenewal + } catch { + Write-LogMessage -API 'Scheduler_RenewGraphSubscriptions' -tenant 'none' -message 'Failed to renew graph subscriptions' -sev Info + } + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPStatsTimer.ps1 b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPStatsTimer.ps1 new file mode 100644 index 000000000000..83cbc4b344a1 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-CIPPStatsTimer.ps1 @@ -0,0 +1,30 @@ +function Start-CIPPStatsTimer { + <# + .SYNOPSIS + Start the CIPP Stats Timer + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + #These stats are sent to a central server to help us understand how many tenants are using the product, and how many are using the latest version, this information allows the CIPP team to make decisions about what features to support, and what features to deprecate. + #We will never ship any data that is related to your instance, all we care about is the number of tenants, and the version of the API you are running, and if you completed setup. + + if ($PSCmdlet.ShouldProcess('Start-CIPPStatsTimer', 'Starting CIPP Stats Timer')) { + if ($ENV:ApplicationID -ne 'LongApplicationID') { + $SetupComplete = $true + } + $TenantCount = (Get-Tenants -IncludeAll).count + + Set-Location (Get-Item $PSScriptRoot).Parent.FullName + $APIVersion = Get-Content 'version_latest.txt' | Out-String + + $SendingObject = [PSCustomObject]@{ + rgid = $env:WEBSITE_SITE_NAME + SetupComplete = $SetupComplete + RunningVersionAPI = $APIVersion.trim() + CountOfTotalTenants = $tenantcount + uid = $env:TenantID + } | ConvertTo-Json + + Invoke-RestMethod -Uri 'https://management.cipp.app/api/stats' -Method POST -Body $SendingObject -ContentType 'application/json' + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-UpdateTokensTimer.ps1 b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-UpdateTokensTimer.ps1 new file mode 100644 index 000000000000..98e4f07891df --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-UpdateTokensTimer.ps1 @@ -0,0 +1,41 @@ +function Start-UpdateTokensTimer { + <# + .SYNOPSIS + Start the Update Tokens Timer + #> + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] + [CmdletBinding(SupportsShouldProcess = $true)] + param() + if ($PSCmdlet.ShouldProcess('Start-UpdateTokensTimer', 'Starting Update Tokens Timer')) { + + # Get the current universal time in the default string format. + $currentUTCtime = (Get-Date).ToUniversalTime() + + $Refreshtoken = (Get-GraphToken -ReturnRefresh $true).Refresh_token + + if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { + $Table = Get-CIPPTable -tablename 'DevSecrets' + $Secret = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'Secret' and RowKey eq 'Secret'" + if ($Secret) { + $Secret.RefreshToken = $Refreshtoken + Add-AzDataTableEntity @Table -Entity $Secret + } else { + Write-LogMessage -message 'Could not update refresh token. Will try again in 7 days.' -sev 'CRITICAL' + } + } else { + if ($env:MSI_SECRET) { + Disable-AzContextAutosave -Scope Process | Out-Null + $AzSession = Connect-AzAccount -Identity + } + $KV = $ENV:WEBSITE_DEPLOYMENT_ID + if ($Refreshtoken) { + Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $Refreshtoken -AsPlainText -Force) + } else { + Write-LogMessage -message 'Could not update refresh token. Will try again in 7 days.' -sev 'CRITICAL' + } + } + + # Write an information log with the current time. + Write-Information "PowerShell timer trigger function ran! TIME: $currentUTCtime" + } +} diff --git a/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 new file mode 100644 index 000000000000..9bf35d33ec71 --- /dev/null +++ b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 @@ -0,0 +1,100 @@ +function Get-CIPPTimerFunctions { + [CmdletBinding()] + param( + [switch]$All + ) + + $ConfigTable = Get-CIPPTable -tablename Config + $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" + + $RunOnProcessor = $true + if ($Config -and $Config.state -eq $true) { + if ($env:CIPP_PROCESSOR -ne 'true' -and !$All.IsPresent) { + $RunOnProcessor = $false + } + } + + $CIPPCoreModuleRoot = Get-Module -Name CIPPCore | Select-Object -ExpandProperty ModuleBase + + if (!('NCronTab.Advanced.CrontabSchedule' -as [type])) { + try { + $NCronTab = Join-Path -Path $CIPPCoreModuleRoot -ChildPath 'lib\Ncrontab.Advanced.dll' + Add-Type -Path $NCronTab + } catch {} + } + + $CIPPRoot = (Get-Item $CIPPCoreModuleRoot).Parent.Parent + $Orchestrators = Get-Content -Path $CIPPRoot\CIPPTimers.json | ConvertFrom-Json | Where-Object { $_.RunOnProcessor -eq $RunOnProcessor } + $Table = Get-CIPPTable -TableName 'CIPPTimers' + $OrchestratorStatus = Get-CIPPAzDataTableEntity @Table -Filter "RunOnProcessor eq $RunOnProcessor" + foreach ($Orchestrator in $Orchestrators) { + $Status = $OrchestratorStatus | Where-Object { $_.RowKey -eq $Orchestrator.Command } + if ($Status.Cron) { + $CronString = $Status.Cron + } else { + $CronString = $Orchestrator.Cron + } + $CronCount = ($CronString -split ' ' | Measure-Object).Count + if ($CronCount -eq 5) { + $Cron = [Ncrontab.Advanced.CrontabSchedule]::Parse($CronString) + } elseif ($CronCount -eq 6) { + $Cron = [Ncrontab.Advanced.CrontabSchedule]::Parse($CronString, [Ncrontab.Advanced.Enumerations.CronStringFormat]::WithSeconds) + } else { + Write-Warning "Invalid cron expression for $($Orchestrator.Command): $($Orchestrator.Cron)" + continue + } + + $Now = Get-Date + if ($All.IsPresent) { + $NextOccurrence = $Cron.GetNextOccurrence($Now) + } else { + $NextOccurrences = $Cron.GetNextOccurrences($Now.AddMinutes(-15), $Now.AddMinutes(15)) + if ($Status.LastOccurrence -eq 'Never') { + $NextOccurrence = $NextOccurrences[0] + } else { + $NextOccurrence = $NextOccurrences | Where-Object { $_ -gt $Status.LastRun } | Select-Object -First 1 + $NextOccurrence = $NextOccurrence + } + } + + if (Get-Command -Name $Orchestrator.Command -Module CIPPCore -ErrorAction SilentlyContinue) { + if (!$Status) { + $Status = [pscustomobject]@{ + PartitionKey = 'Timer' + RowKey = $Orchestrator.Command + Cron = $CronString + LastOccurrence = 'Never' + NextOccurrence = $NextOccurrence.ToUniversalTime() + Status = 'Not Scheduled' + OrchestratorId = '' + RunOnProcessor = $RunOnProcessor + IsSystem = $Orchestrator.IsSystem ?? $false + } + Add-CIPPAzDataTableEntity @Table -Entity $Status + } else { + if ($Orchestrator.IsSystem) { + $Status.Cron = $CronString + } + $Status.NextOccurrence = $NextOccurrence.ToUniversalTime() + Add-CIPPAzDataTableEntity @Table -Entity $Status -Force + } + if ($NextOccurrence) { + [PSCustomObject]@{ + Command = $Orchestrator.Command + Cron = $CronString + NextOccurrence = $NextOccurrence.ToUniversalTime() + LastOccurrence = $Status.LastOccurrence + Status = $Status.Status + OrchestratorId = $Status.OrchestratorId + RunOnProcessor = $Orchestrator.RunOnProcessor + IsSystem = $Orchestrator.IsSystem ?? $false + } + } + } else { + if ($Status) { + Write-Warning "Timer function: $($Orchestrator.Command) does not exist" + Remove-CIPPAzDataTableEntity @Table -Entity $Status + } + } + } +} diff --git a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 index fdded4391dd3..ed2e2b22d3e6 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 @@ -73,8 +73,7 @@ function New-ExoRequest { $OnMicrosoft = $Tenant.initialDomainName } $anchor = "UPN:SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@$($OnMicrosoft)" - if ($cmdlet -in 'Set-AdminAuditLogConfig', 'Get-AdminAuditLogConfig', 'Enable-OrganizationCustomization', 'Get-OrganizationConfig') { $anchor = "UPN:SystemMailbox{8cc370d3-822a-4ab8-a926-bb94bd0641a9}@$($OnMicrosoft)" } - + if ($cmdlet -in 'Set-AdminAuditLogConfig', 'Get-AdminAuditLogConfig', 'Enable-OrganizationCustomization', 'Get-OrganizationConfig', 'Set-OrganizationConfig') { $anchor = "UPN:SystemMailbox{8cc370d3-822a-4ab8-a926-bb94bd0641a9}@$($OnMicrosoft)" } } } #if the anchor is a GUID, try looking up the user. diff --git a/Modules/CIPPCore/lib/NCrontab.Advanced.dll b/Modules/CIPPCore/lib/NCrontab.Advanced.dll new file mode 100644 index 0000000000000000000000000000000000000000..3aff438ecf8649f8b94f9a16f7395829fe6e816b GIT binary patch literal 31232 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 DN_swC literal 0 HcmV?d00001 diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index aae4096c53a1..973c9af5f360 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -44,7 +44,7 @@ function Receive-CippHttpTrigger { function Receive-CippQueueTrigger { Param($QueueItem, $TriggerMetadata) - + Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName $Start = (Get-Date).ToUniversalTime() $APIName = $TriggerMetadata.FunctionName @@ -213,5 +213,30 @@ function Receive-CippActivityTrigger { } } -Export-ModuleMember -Function @('Receive-CippHttpTrigger', 'Receive-CippQueueTrigger', 'Receive-CippOrchestrationTrigger', 'Receive-CippActivityTrigger') +function Receive-CIPPTimerTrigger { + param($Timer) + + $UtcNow = (Get-Date).ToUniversalTime() + $Functions = Get-CIPPTimerFunctions + $Table = Get-CIPPTable -tablename CIPPTimers + $Functions = Get-CIPPAzDataTableEntity @Table + + foreach ($Function in $Functions) { + $Status = $Functions | Where-Object { $_.RowKey -eq $Function.Command } + try { + $Results = & $Function.Command @TimerTrigger + if ($Results -is [guid]) { + $Status.OrchestratorId = $Results + } + $Status = 'Started' + } catch { + $Status = 'Failed' + } + $Status.LastRun = $UtcNow + $Status.Status = $Status + Add-CIPPAzDataTableEntity @Table -Entity $Status -Force + } +} + +Export-ModuleMember -Function @('Receive-CippHttpTrigger', 'Receive-CippQueueTrigger', 'Receive-CippOrchestrationTrigger', 'Receive-CippActivityTrigger', 'Receive-CIPPTimerTrigger') diff --git a/Scheduler_GetQueue/run.ps1 b/Scheduler_GetQueue/run.ps1 deleted file mode 100644 index f359db7de9c4..000000000000 --- a/Scheduler_GetQueue/run.ps1 +++ /dev/null @@ -1,56 +0,0 @@ -param($Timer) - -$Table = Get-CIPPTable -TableName SchedulerConfig -$Tenants = Get-CIPPAzDataTableEntity @Table | Where-Object -Property PartitionKey -NE 'WebhookAlert' - -$Tasks = foreach ($Tenant in $Tenants) { - if ($Tenant.tenant -ne 'AllTenants') { - [pscustomobject]@{ - Tenant = $Tenant.tenant - Tag = 'SingleTenant' - TenantID = $Tenant.tenantid - Type = $Tenant.type - RowKey = $Tenant.RowKey - } - } else { - Write-Information 'All tenants, doing them all' - $TenantList = Get-Tenants - foreach ($t in $TenantList) { - [pscustomobject]@{ - Tenant = $t.defaultDomainName - Tag = 'AllTenants' - TenantID = $t.customerId - Type = $Tenant.type - RowKey = $Tenant.RowKey - } - } - } -} - -if (($Tasks | Measure-Object).Count -eq 0) { - return -} - -$Queue = New-CippQueueEntry -Name 'Scheduler' -TotalTasks ($Tasks | Measure-Object).Count - -$Batch = foreach ($Task in $Tasks) { - [pscustomobject]@{ - Tenant = $task.tenant - Tenantid = $task.tenantid - Tag = $task.tag - Type = $task.type - QueueId = $Queue.RowKey - SchedulerRow = $Task.RowKey - QueueName = '{0} - {1}' -f $Task.Type, $task.tenant - FunctionName = "Scheduler$($Task.Type)" - } -} -$InputObject = [PSCustomObject]@{ - OrchestratorName = 'SchedulerOrchestrator' - Batch = @($Batch) - SkipLog = $true -} -#Write-Information ($InputObject | ConvertTo-Json) -$InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) -Write-Information "Started orchestration with ID = '$InstanceId'" -#$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId diff --git a/Scheduler_GetWebhooks/function.json b/Scheduler_GetWebhooks/function.json deleted file mode 100644 index f30537d11b34..000000000000 --- a/Scheduler_GetWebhooks/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "schedule": "0 */15 * * * *", - "direction": "in", - "type": "timerTrigger" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} diff --git a/Scheduler_GetWebhooks/run.ps1 b/Scheduler_GetWebhooks/run.ps1 deleted file mode 100644 index cef8cfb6d726..000000000000 --- a/Scheduler_GetWebhooks/run.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -param($Timer) - -try { - - $webhookTable = Get-CIPPTable -tablename webhookTable - $Webhooks = Get-CIPPAzDataTableEntity @webhookTable -Property PartitionKey, RowKey - if (($Webhooks | Measure-Object).Count -eq 0) { - Write-Host 'No webhook subscriptions found. Exiting.' - return - } - Write-Host 'Processing webhooks' - - $InputObject = [PSCustomObject]@{ - OrchestratorName = 'WebhookOrchestrator' - QueueFunction = @{ - FunctionName = 'GetPendingWebhooks' - } - SkipLog = $true - } - Write-Host ($InputObject | ConvertTo-Json -Depth 5) - $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) - Write-Host "Started orchestration with ID = '$InstanceId'" -} catch { - Write-LogMessage -API 'Webhooks' -message 'Error processing webhooks' -sev Error -LogData (Get-CippException -Exception $_) - Write-Host ( 'Webhook error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) -} diff --git a/Scheduler_PollAuditLogs/function.json b/Scheduler_PollAuditLogs/function.json deleted file mode 100644 index f30537d11b34..000000000000 --- a/Scheduler_PollAuditLogs/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "schedule": "0 */15 * * * *", - "direction": "in", - "type": "timerTrigger" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} diff --git a/Scheduler_PollAuditLogs/run.ps1 b/Scheduler_PollAuditLogs/run.ps1 deleted file mode 100644 index 3c7e8668355c..000000000000 --- a/Scheduler_PollAuditLogs/run.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -param($Timer) - -try { - $ConfigTable = Get-CIPPTable -tablename Config - $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" - - if ($Config -and $Config.state -eq $true) { - Write-Host 'Offload functions are enabled. Exiting.' - return 0 - } - - $webhookTable = Get-CIPPTable -tablename webhookTable - $Webhooks = Get-CIPPAzDataTableEntity @webhookTable -Filter "Version eq '3'" | Where-Object { $_.Resource -match '^Audit' -and $_.Status -ne 'Disabled' } - if (($Webhooks | Measure-Object).Count -eq 0) { - Write-Host 'No webhook subscriptions found. Exiting.' - return - } - - $StartTime = (Get-Date).AddMinutes(-30) - $EndTime = Get-Date - - $Queue = New-CippQueueEntry -Name 'Audit Log Collection' -Reference 'AuditLogCollection' -TotalTasks ($Webhooks | Sort-Object -Property PartitionKey -Unique | Measure-Object).Count - $Batch = $Webhooks | Sort-Object -Property PartitionKey -Unique | Select-Object @{Name = 'TenantFilter'; Expression = { $_.PartitionKey } }, @{Name = 'QueueId'; Expression = { $Queue.RowKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogTenant' } }, @{Name = 'StartTime'; Expression = { $StartTime } }, @{Name = 'EndTime'; Expression = { $EndTime } } - $InputObject = [PSCustomObject]@{ - OrchestratorName = 'AuditLogs' - Batch = @($Batch) - SkipLog = $true - } - $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) - Write-Host "Started orchestration with ID = '$InstanceId'" -} catch { - Write-LogMessage -API 'Webhooks' -message 'Error processing webhooks' -sev Error -LogData (Get-CippException -Exception $_) - Write-Host ( 'Webhook error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) -} diff --git a/Scheduler_RenewGraphSubscriptions/function.json b/Scheduler_RenewGraphSubscriptions/function.json deleted file mode 100644 index 114e4c0f7aab..000000000000 --- a/Scheduler_RenewGraphSubscriptions/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 10 * * * *" - } - ] -} diff --git a/Scheduler_RenewGraphSubscriptions/run.ps1 b/Scheduler_RenewGraphSubscriptions/run.ps1 deleted file mode 100644 index b688e87e8b17..000000000000 --- a/Scheduler_RenewGraphSubscriptions/run.ps1 +++ /dev/null @@ -1,10 +0,0 @@ -# Input bindings are passed in via param block. -param($Timer) - -# Get the current universal time in the default string format. -try { - Write-LogMessage -API "Scheduler_RenewGraphSubscriptions" -tenant "none" -message "Starting Graph Subscription Renewal" -sev Info - Invoke-CippGraphWebhookRenewal -} catch { - Write-LogMessage -API "Scheduler_RenewGraphSubscriptions" -tenant "none" -message "Failed to renew graph subscriptions" -sev Info -} \ No newline at end of file diff --git a/Scheduler_Standards/function.json b/Scheduler_Standards/function.json deleted file mode 100644 index e071591357a0..000000000000 --- a/Scheduler_Standards/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "schedule": "0 0 */4 * * *", - "direction": "in", - "type": "timerTrigger" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} diff --git a/Scheduler_Standards/run.ps1 b/Scheduler_Standards/run.ps1 deleted file mode 100644 index 7466a60f5338..000000000000 --- a/Scheduler_Standards/run.ps1 +++ /dev/null @@ -1,6 +0,0 @@ -using namespace System.Net - -param($Timer) -Write-LogMessage -API 'Standards' -message 'Starting Standards Schedule' -sev Info -Invoke-CIPPStandardsRun -tenantfilter 'allTenants' -Write-LogMessage -API 'Standards' -message 'Launched all standard jobs' -sev Info \ No newline at end of file diff --git a/Scheduler_UserTasks/function.json b/Scheduler_UserTasks/function.json deleted file mode 100644 index f7af84092121..000000000000 --- a/Scheduler_UserTasks/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "schedule": "0 */15 * * * *", - "direction": "in", - "type": "timerTrigger" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} diff --git a/Scheduler_UserTasks/run.ps1 b/Scheduler_UserTasks/run.ps1 deleted file mode 100644 index 27b975443797..000000000000 --- a/Scheduler_UserTasks/run.ps1 +++ /dev/null @@ -1,75 +0,0 @@ -param($Timer) - -$Table = Get-CippTable -tablename 'ScheduledTasks' -$Filter = "TaskState eq 'Planned' or TaskState eq 'Failed - Planned'" -$tasks = Get-CIPPAzDataTableEntity @Table -Filter $Filter -$Batch = [System.Collections.Generic.List[object]]::new() -$TenantList = Get-Tenants -IncludeErrors -foreach ($task in $tasks) { - $tenant = $task.Tenant - $currentUnixTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds - if ($currentUnixTime -ge $task.ScheduledTime) { - try { - $null = Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - ExecutedTime = "$currentUnixTime" - TaskState = 'Running' - } - $task.Parameters = $task.Parameters | ConvertFrom-Json -AsHashtable - $task.AdditionalProperties = $task.AdditionalProperties | ConvertFrom-Json - - if (!$task.Parameters) { $task.Parameters = @{} } - $ScheduledCommand = [pscustomobject]@{ - Command = $task.Command - Parameters = $task.Parameters - TaskInfo = $task - FunctionName = 'ExecScheduledCommand' - } - - if ($task.Tenant -eq 'AllTenants') { - $AllTenantCommands = foreach ($Tenant in $TenantList) { - $NewParams = $task.Parameters.Clone() - $NewParams.TenantFilter = $Tenant.defaultDomainName - [pscustomobject]@{ - Command = $task.Command - Parameters = $NewParams - TaskInfo = $task - FunctionName = 'ExecScheduledCommand' - } - } - $Batch.AddRange($AllTenantCommands) - } else { - $ScheduledCommand.Parameters['TenantFilter'] = $task.Tenant - $Batch.Add($ScheduledCommand) - } - } catch { - $errorMessage = $_.Exception.Message - - $null = Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - Results = "$errorMessage" - ExecutedTime = "$currentUnixTime" - TaskState = 'Failed' - } - Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Failed to execute task $($task.Name): $errorMessage" -sev Error - } - } -} -if (($Batch | Measure-Object).Count -gt 0) { - # Create queue entry - $Queue = New-CippQueueEntry -Name 'Scheduled Tasks' -TotalTasks ($Batch | Measure-Object).Count - $QueueId = $Queue.RowKey - $Batch = $Batch | Select-Object *, @{Name = 'QueueId'; Expression = { $QueueId } }, @{Name = 'QueueName'; Expression = { '{0} - {1}' -f $_.TaskInfo.Name, ($_.TaskInfo.Tenant -ne 'AllTenants' ? $_.TaskInfo.Tenant : $_.Parameters.TenantFilter) } } - - $InputObject = [PSCustomObject]@{ - OrchestratorName = 'UserTaskOrchestrator' - Batch = @($Batch) - SkipLog = $true - } - #Write-Host ($InputObject | ConvertTo-Json -Depth 10) - $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 10 -Compress) - - Write-Host "Started orchestration with ID = '$InstanceId'" -} diff --git a/SendStats/function.json b/SendStats/function.json deleted file mode 100644 index b47dd4f6ee24..000000000000 --- a/SendStats/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 0 0 * * *" - } - ] -} \ No newline at end of file diff --git a/SendStats/run.ps1 b/SendStats/run.ps1 deleted file mode 100644 index 170e6e7b5bee..000000000000 --- a/SendStats/run.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -# Input bindings are passed in via param block. -param($Timer) - -#These stats are sent to a central server to help us understand how many tenants are using the product, and how many are using the latest version, this information allows the CIPP team to make decisions about what features to support, and what features to deprecate. -#We will never ship any data that is related to your instance, all we care about is the number of tenants, and the version of the API you are running, and if you completed setup. - -if ($ENV:ApplicationID -ne 'LongApplicationID') { - $SetupComplete = $true -} -$TenantCount = (Get-Tenants).count - -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -$APIVersion = Get-Content 'version_latest.txt' | Out-String - -$SendingObject = [PSCustomObject]@{ - rgid = $env:WEBSITE_SITE_NAME - SetupComplete = $SetupComplete - RunningVersionAPI = $APIVersion.trim() - CountOfTotalTenants = $tenantcount - uid = $env:TenantID -} | ConvertTo-Json - -Invoke-RestMethod -Uri 'https://management.cipp.app/api/stats' -Method POST -Body $SendingObject -ContentType 'application/json' diff --git a/UpdatePermissions/function.json b/UpdatePermissions/function.json deleted file mode 100644 index 7e97fe568d29..000000000000 --- a/UpdatePermissions/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 0 0 * * *" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} diff --git a/UpdatePermissions/run.ps1 b/UpdatePermissions/run.ps1 deleted file mode 100644 index 8f9ca5b8652a..000000000000 --- a/UpdatePermissions/run.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -# Input bindings are passed in via param block. -param($Timer) - -try { - $Tenants = Get-Tenants -IncludeAll | Where-Object { $_.customerId -ne $env:TenantID -and $_.Excluded -eq $false } - $CPVTable = Get-CIPPTable -TableName cpvtenants - $CPVRows = Get-CIPPAzDataTableEntity @CPVTable - $ModuleRoot = (Get-Module CIPPCore).ModuleBase - $SAMManifest = Get-Item -Path "$ModuleRoot\Public\SAMManifest.json" - $AdditionalPermissions = Get-Item -Path "$ModuleRoot\Public\AdditionalPermissions.json" - $Tenants = $Tenants | ForEach-Object { - $CPVRow = $CPVRows | Where-Object -Property Tenant -EQ $_.customerId - if (!$CPVRow -or $env:ApplicationID -notin $CPVRow.applicationId -or $SAMManifest.LastWriteTime.ToUniversalTime() -gt $CPVRow.Timestamp.DateTime -or $AdditionalPermissions.LastWriteTime.ToUniversalTime() -ge $CPVRow.Timestamp.DateTime -or $CPVRow.Timestamp.DateTime -le (Get-Date).AddDays(-7).ToUniversalTime() -or !$_.defaultDomainName) { - $_ - } - } - $TenantCount = ($Tenants | Measure-Object).Count - if ($TenantCount -gt 0) { - $Queue = New-CippQueueEntry -Name 'Update Permissions' -TotalTasks $TenantCount - $TenantBatch = $Tenants | Select-Object defaultDomainName, customerId, displayName, @{n = 'FunctionName'; exp = { 'UpdatePermissionsQueue' } }, @{n = 'QueueId'; exp = { $Queue.RowKey } } - $InputObject = [PSCustomObject]@{ - OrchestratorName = 'UpdatePermissionsOrchestrator' - Batch = @($TenantBatch) - } - $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) - Write-Host "Started permissions orchestration with ID = '$InstanceId'" - } else { - Write-Host 'No tenants require permissions update' - } -} catch {} diff --git a/UpdateTokens/function.json b/UpdateTokens/function.json deleted file mode 100644 index 828a2484ee7d..000000000000 --- a/UpdateTokens/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 0 0 * * 0" - } - ] -} \ No newline at end of file diff --git a/UpdateTokens/run.ps1 b/UpdateTokens/run.ps1 deleted file mode 100644 index ed20d6a67fbf..000000000000 --- a/UpdateTokens/run.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -# Input bindings are passed in via param block. -param($Timer) - -# Get the current universal time in the default string format. -$currentUTCtime = (Get-Date).ToUniversalTime() - -$Refreshtoken = (Get-GraphToken -ReturnRefresh $true).Refresh_token - -if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') { - $Table = Get-CIPPTable -tablename 'DevSecrets' - $Secret = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'Secret' and RowKey eq 'Secret'" - if ($Secret) { - $Secret.RefreshToken = $Refreshtoken - Add-AzDataTableEntity @Table -Entity $Secret - } else { - Write-LogMessage -message 'Could not update refresh token. Will try again in 7 days.' -sev 'CRITICAL' - } -} else { - if ($env:MSI_SECRET) { - Disable-AzContextAutosave -Scope Process | Out-Null - $AzSession = Connect-AzAccount -Identity - } - $KV = $ENV:WEBSITE_DEPLOYMENT_ID - if ($Refreshtoken) { - Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $Refreshtoken -AsPlainText -Force) - } else { - Write-LogMessage -message 'Could not update refresh token. Will try again in 7 days.' -sev 'CRITICAL' - } -} - -# Write an information log with the current time. -Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime" - diff --git a/Z_CIPPHttpTrigger/function.json b/Z_CIPPHttpTrigger/function.json index 179d246817ff..bf248646d302 100644 --- a/Z_CIPPHttpTrigger/function.json +++ b/Z_CIPPHttpTrigger/function.json @@ -27,12 +27,6 @@ "name": "Subscription", "queueName": "AlertSubscriptions" }, - { - "type": "queue", - "direction": "out", - "name": "mailboxstats", - "queueName": "generalAllTenantQueue" - }, { "type": "queue", "direction": "out", diff --git a/Z_CIPPQueueTrigger/function.json b/Z_CIPPQueueTrigger/function.json index a400b2e81f43..7f7dde0a4cbd 100644 --- a/Z_CIPPQueueTrigger/function.json +++ b/Z_CIPPQueueTrigger/function.json @@ -9,10 +9,9 @@ "queueName": "CIPPGenericQueue" }, { - "type": "queue", - "direction": "out", - "name": "QueueItemOut", - "queueName": "CIPPGenericQueue" + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } From e643a6dc9dd5d1e606ffde91b1b420fe6a50f57c Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:00:03 -0400 Subject: [PATCH 03/33] Fix CIPPtimer function --- .../Public/Get-CIPPTimerFunctions.ps1 | 49 ++++++++++--------- Modules/CippEntrypoints/CippEntrypoints.psm1 | 1 + 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 index 9bf35d33ec71..a27ac059cf87 100644 --- a/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 @@ -1,7 +1,8 @@ function Get-CIPPTimerFunctions { [CmdletBinding()] param( - [switch]$All + [switch]$All, + [switch]$ResetToDefault ) $ConfigTable = Get-CIPPTable -tablename Config @@ -46,39 +47,39 @@ function Get-CIPPTimerFunctions { $Now = Get-Date if ($All.IsPresent) { - $NextOccurrence = $Cron.GetNextOccurrence($Now) + $NextOccurrence = [datetime]$Cron.GetNextOccurrence($Now) } else { $NextOccurrences = $Cron.GetNextOccurrences($Now.AddMinutes(-15), $Now.AddMinutes(15)) if ($Status.LastOccurrence -eq 'Never') { - $NextOccurrence = $NextOccurrences[0] + $NextOccurrence = $NextOccurrences | Select-Object -First 1 } else { $NextOccurrence = $NextOccurrences | Where-Object { $_ -gt $Status.LastRun } | Select-Object -First 1 - $NextOccurrence = $NextOccurrence } } if (Get-Command -Name $Orchestrator.Command -Module CIPPCore -ErrorAction SilentlyContinue) { - if (!$Status) { - $Status = [pscustomobject]@{ - PartitionKey = 'Timer' - RowKey = $Orchestrator.Command - Cron = $CronString - LastOccurrence = 'Never' - NextOccurrence = $NextOccurrence.ToUniversalTime() - Status = 'Not Scheduled' - OrchestratorId = '' - RunOnProcessor = $RunOnProcessor - IsSystem = $Orchestrator.IsSystem ?? $false - } - Add-CIPPAzDataTableEntity @Table -Entity $Status - } else { - if ($Orchestrator.IsSystem) { - $Status.Cron = $CronString - } - $Status.NextOccurrence = $NextOccurrence.ToUniversalTime() - Add-CIPPAzDataTableEntity @Table -Entity $Status -Force - } if ($NextOccurrence) { + if (!$Status) { + $Status = [pscustomobject]@{ + PartitionKey = 'Timer' + RowKey = $Orchestrator.Command + Cron = $CronString + LastOccurrence = 'Never' + NextOccurrence = $NextOccurrence.ToUniversalTime() + Status = 'Not Scheduled' + OrchestratorId = '' + RunOnProcessor = $RunOnProcessor + IsSystem = $Orchestrator.IsSystem ?? $false + } + Add-CIPPAzDataTableEntity @Table -Entity $Status + } else { + if ($Orchestrator.IsSystem -or $ResetToDefault.IsPresent) { + $Status.Cron = $CronString + } + $Status.NextOccurrence = $NextOccurrence.ToUniversalTime() + Add-CIPPAzDataTableEntity @Table -Entity $Status -Force + } + [PSCustomObject]@{ Command = $Orchestrator.Command Cron = $CronString diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 973c9af5f360..b5c301ba8c48 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -222,6 +222,7 @@ function Receive-CIPPTimerTrigger { $Functions = Get-CIPPAzDataTableEntity @Table foreach ($Function in $Functions) { + Write-Information "CIPPTimer: $($Function.Command) - $($Function.Cron)" $Status = $Functions | Where-Object { $_.RowKey -eq $Function.Command } try { $Results = & $Function.Command @TimerTrigger From 34cfd193f871f7e7023a5139ae59b592b3a009d2 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:06:52 -0400 Subject: [PATCH 04/33] Add offload function check for http trigger --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 13 +++++++ PublicScripts/function.json | 19 ---------- PublicScripts/run.ps1 | 38 -------------------- 3 files changed, 13 insertions(+), 57 deletions(-) delete mode 100644 PublicScripts/function.json delete mode 100644 PublicScripts/run.ps1 diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index b5c301ba8c48..649901f604d9 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -9,6 +9,19 @@ function Receive-CippHttpTrigger { $Request, $TriggerMetadata ) + + $ConfigTable = Get-CIPPTable -tablename Config + $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" + + if ($Config -and $Config.state -eq $true) { + if ($env:CIPP_PROCESSOR -ne 'true') { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::Forbidden + Body = 'API calls are not accepted on this function app' + }) + } + } + # Convert the request to a PSCustomObject because the httpContext is case sensitive since 7.3 $Request = $Request | ConvertTo-Json -Depth 100 | ConvertFrom-Json Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName diff --git a/PublicScripts/function.json b/PublicScripts/function.json deleted file mode 100644 index 306b0c51e560..000000000000 --- a/PublicScripts/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": [ - "get", - "post" - ] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - } - ] -} \ No newline at end of file diff --git a/PublicScripts/run.ps1 b/PublicScripts/run.ps1 deleted file mode 100644 index 0dca13ac3c97..000000000000 --- a/PublicScripts/run.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -$APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' - -# Write to the Azure Functions log stream. -Write-Host 'PowerShell HTTP trigger function processed a request.' - -$Table = Get-CippTable -TableName 'MaintenanceScripts' - -if (![string]::IsNullOrEmpty($Request.Query.Guid)) { - $Filter = "PartitionKey eq 'Maintenance' and RowKey eq '{0}'" -f $Request.Query.Guid - $ScriptRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter - if ($ScriptRow) { - if ($ScriptRow.Timestamp.DateTime -lt (Get-Date).AddMinutes(-5)) { - $Body = 'Write-Host "Link expired"' - } - else { - $Body = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($ScriptRow.ScriptContent)) - } - Remove-AzDataTableEntity @Table -Entity $ScriptRow - } - else { - $Body = 'Write-Host "Invalid Script ID"' - } -} -else { - $Body = 'Write-Host "Script ID is required, generate a link from the Maintenance page"' -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) From a9b1d87ba69ae0e85fff6428920e801fca28047a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:16:42 -0400 Subject: [PATCH 05/33] Update CippEntrypoints.psm1 --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 649901f604d9..99eb03eb6a93 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -246,7 +246,7 @@ function Receive-CIPPTimerTrigger { } catch { $Status = 'Failed' } - $Status.LastRun = $UtcNow + $Status.LastOccurrence = $UtcNow $Status.Status = $Status Add-CIPPAzDataTableEntity @Table -Entity $Status -Force } From 8f67cd89a2daca5bc37bec2286977dbe006acfe3 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:22:16 -0400 Subject: [PATCH 06/33] Update CippEntrypoints.psm1 --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 99eb03eb6a93..7106e48fce27 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -232,11 +232,11 @@ function Receive-CIPPTimerTrigger { $UtcNow = (Get-Date).ToUniversalTime() $Functions = Get-CIPPTimerFunctions $Table = Get-CIPPTable -tablename CIPPTimers - $Functions = Get-CIPPAzDataTableEntity @Table + $Statuses = Get-CIPPAzDataTableEntity @Table foreach ($Function in $Functions) { Write-Information "CIPPTimer: $($Function.Command) - $($Function.Cron)" - $Status = $Functions | Where-Object { $_.RowKey -eq $Function.Command } + $Status = $Statuses | Where-Object { $_.Command -eq $Function.Command } try { $Results = & $Function.Command @TimerTrigger if ($Results -is [guid]) { From 588cfbb93eef8bb43b63865387cdbaf227414d33 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:24:30 -0400 Subject: [PATCH 07/33] Update CippEntrypoints.psm1 --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 7106e48fce27..a58b00cd179a 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -236,19 +236,19 @@ function Receive-CIPPTimerTrigger { foreach ($Function in $Functions) { Write-Information "CIPPTimer: $($Function.Command) - $($Function.Cron)" - $Status = $Statuses | Where-Object { $_.Command -eq $Function.Command } + $FunctionStatus = $Statuses | Where-Object { $_.Command -eq $Function.Command } try { $Results = & $Function.Command @TimerTrigger if ($Results -is [guid]) { - $Status.OrchestratorId = $Results + $FunctionStatus.OrchestratorId = $Results } $Status = 'Started' } catch { $Status = 'Failed' } - $Status.LastOccurrence = $UtcNow - $Status.Status = $Status - Add-CIPPAzDataTableEntity @Table -Entity $Status -Force + $FunctionStatus.LastOccurrence = $UtcNow + $FunctionStatus.Status = $Status + Add-CIPPAzDataTableEntity @Table -Entity $FunctionStatus -Force } } From 4b704a0974034c2824b66934af118241dda233d4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:28:21 -0400 Subject: [PATCH 08/33] Update CippEntrypoints.psm1 --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index a58b00cd179a..7d817925df73 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -236,7 +236,7 @@ function Receive-CIPPTimerTrigger { foreach ($Function in $Functions) { Write-Information "CIPPTimer: $($Function.Command) - $($Function.Cron)" - $FunctionStatus = $Statuses | Where-Object { $_.Command -eq $Function.Command } + $FunctionStatus = $Statuses | Where-Object { $_.RowKey -eq $Function.Command } try { $Results = & $Function.Command @TimerTrigger if ($Results -is [guid]) { From 38b14988a2025a9047d49babf8a32964c8308299 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:40:04 -0400 Subject: [PATCH 09/33] update timer --- Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 | 4 ++-- Modules/CippEntrypoints/CippEntrypoints.psm1 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 index a27ac059cf87..a1f2fa2b9232 100644 --- a/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 @@ -53,7 +53,7 @@ function Get-CIPPTimerFunctions { if ($Status.LastOccurrence -eq 'Never') { $NextOccurrence = $NextOccurrences | Select-Object -First 1 } else { - $NextOccurrence = $NextOccurrences | Where-Object { $_ -gt $Status.LastRun } | Select-Object -First 1 + $NextOccurrence = $NextOccurrences | Where-Object { $_ -gt $Status.LastOccurrence.DateTime } | Select-Object -First 1 } } @@ -84,7 +84,7 @@ function Get-CIPPTimerFunctions { Command = $Orchestrator.Command Cron = $CronString NextOccurrence = $NextOccurrence.ToUniversalTime() - LastOccurrence = $Status.LastOccurrence + LastOccurrence = $Status.LastOccurrence.DateTime Status = $Status.Status OrchestratorId = $Status.OrchestratorId RunOnProcessor = $Orchestrator.RunOnProcessor diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 7d817925df73..1ab5a928dd52 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -238,7 +238,7 @@ function Receive-CIPPTimerTrigger { Write-Information "CIPPTimer: $($Function.Command) - $($Function.Cron)" $FunctionStatus = $Statuses | Where-Object { $_.RowKey -eq $Function.Command } try { - $Results = & $Function.Command @TimerTrigger + $Results = Invoke-Command -ScriptBlock { & $Function.Command } if ($Results -is [guid]) { $FunctionStatus.OrchestratorId = $Results } From 31bfb9f6733373182893e49234d49ee729b8f90a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:46:00 -0400 Subject: [PATCH 10/33] stats and timer --- .../GraphHelper/Write-CippFunctionStats.ps1 | 19 ++++--------------- Modules/CippEntrypoints/CippEntrypoints.psm1 | 2 +- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 index 0e295e96cc46..96b17cceaa62 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 @@ -6,34 +6,23 @@ function Write-CippFunctionStats { Param( [string]$FunctionType, $Entity, - $Start, - $End, + [datetime]$Start, + [datetime]$End, [string]$ErrorMsg = '' ) try { - $Start = Get-Date $Start - $End = Get-Date $End - $Table = Get-CIPPTable -tablename CippFunctionStats $RowKey = [string](New-Guid).Guid $TimeSpan = New-TimeSpan -Start $Start -End $End $Duration = [int]$TimeSpan.TotalSeconds $DurationMS = [int]$TimeSpan.TotalMilliseconds - # if datetime is local, convert to UTC - if ($Start.Kind -eq 'Local') { - $Start = $Start.ToUniversalTime() - } - if ($End.Kind -eq 'Local') { - $End = $End.ToUniversalTime() - } - $StatEntity = @{} # Flatten data to json string $StatEntity.PartitionKey = $FunctionType $StatEntity.RowKey = $RowKey - $StatEntity.Start = $Start - $StatEntity.End = $End + $StatEntity.Start = $Start.ToUniversalTime() + $StatEntity.End = $End.ToUniversalTime() $StatEntity.Duration = $Duration $StatEntity.DurationMS = $DurationMS $StatEntity.ErrorMsg = $ErrorMsg diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 1ab5a928dd52..829a778d6532 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -239,7 +239,7 @@ function Receive-CIPPTimerTrigger { $FunctionStatus = $Statuses | Where-Object { $_.RowKey -eq $Function.Command } try { $Results = Invoke-Command -ScriptBlock { & $Function.Command } - if ($Results -is [guid]) { + if ($Results -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') { $FunctionStatus.OrchestratorId = $Results } $Status = 'Started' From fb0c6e93cdab411d1c78b9a159f17b40883a65af Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:48:53 -0400 Subject: [PATCH 11/33] Update Get-CIPPTimerFunctions.ps1 --- Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 index a1f2fa2b9232..55d3166af174 100644 --- a/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 @@ -51,9 +51,9 @@ function Get-CIPPTimerFunctions { } else { $NextOccurrences = $Cron.GetNextOccurrences($Now.AddMinutes(-15), $Now.AddMinutes(15)) if ($Status.LastOccurrence -eq 'Never') { - $NextOccurrence = $NextOccurrences | Select-Object -First 1 + $NextOccurrence = $NextOccurrences | Where-Object { $_ -le (Get-Date) } | Select-Object -First 1 } else { - $NextOccurrence = $NextOccurrences | Where-Object { $_ -gt $Status.LastOccurrence.DateTime } | Select-Object -First 1 + $NextOccurrence = $NextOccurrences | Where-Object { $_ -gt $Status.LastOccurrence.DateTime -and $_ -le (Get-Date) } | Select-Object -First 1 } } From 07d7085a3047ebf754ab321ecb7ced96efde1cc9 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:52:43 -0400 Subject: [PATCH 12/33] Update Push-AuditLogTenant.ps1 --- .../Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 index e74208b8887c..922d31d9f76b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 @@ -47,8 +47,8 @@ function Push-AuditLogTenant { DefaultDomainName = $TenantFilter ContentType = $Bundle.contentType ContentUri = $Bundle.contentUri - ContentCreated = $Bundle.contentCreated - ContentExpiration = $Bundle.contentExpiration + ContentCreated = [datetime]::SpecifyKind($Bundle.contentCreated, [System.DateTimeKind]::Utc) + ContentExpiration = [datetime]::SpecifyKind($Bundle.contentExpiration, [System.DateTimeKind]::Utc) CIPPURL = [string]$CIPPURL ProcessingStatus = 'Pending' MatchedRules = '' From b55601bb3b87d72348690dc1e33c78c5949568ff Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:56:08 -0400 Subject: [PATCH 13/33] Update CippEntrypoints.psm1 --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 829a778d6532..93baf91744dd 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -14,11 +14,13 @@ function Receive-CippHttpTrigger { $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" if ($Config -and $Config.state -eq $true) { + Write-Information 'No API Calls' if ($env:CIPP_PROCESSOR -ne 'true') { Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::Forbidden Body = 'API calls are not accepted on this function app' }) + return } } @@ -26,7 +28,7 @@ function Receive-CippHttpTrigger { $Request = $Request | ConvertTo-Json -Depth 100 | ConvertFrom-Json Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName $FunctionName = 'Invoke-{0}' -f $Request.Params.CIPPEndpoint - Write-Host "Function: $($Request.Params.CIPPEndpoint)" + Write-Information "Function: $($Request.Params.CIPPEndpoint)" $HttpTrigger = @{ Request = [pscustomobject]($Request) From a926ae96daf23e72c04f3a1846e3f0dc567058ee Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 09:57:14 -0400 Subject: [PATCH 14/33] Update CippEntrypoints.psm1 --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 93baf91744dd..7b599f991fc0 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -15,7 +15,7 @@ function Receive-CippHttpTrigger { if ($Config -and $Config.state -eq $true) { Write-Information 'No API Calls' - if ($env:CIPP_PROCESSOR -ne 'true') { + if ($env:CIPP_PROCESSOR -eq 'true') { Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::Forbidden Body = 'API calls are not accepted on this function app' From cd429b0492966edbb8ffcdb0542fea54bd9fece5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 10:23:03 -0400 Subject: [PATCH 15/33] Update Push-AuditLogTenant.ps1 --- .../Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 index 922d31d9f76b..0847a2e2c79e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 @@ -40,6 +40,8 @@ function Push-AuditLogTenant { $ExistingBundles = Get-CIPPAzDataTableEntity @AuditBundleTable -Filter "PartitionKey eq '$($Item.TenantFilter)' and ContentType eq '$LogType' and Timestamp ge datetime'$($LastHour)'" foreach ($Bundle in $LogBundles) { + Write-Host ($Bundle.contentCreated + ' ' + $Bundle.contentExpiration) + Write-Host ($Bundle.contentCreated.GetType()) if ($ExistingBundles.RowKey -notcontains $Bundle.contentId) { $NewBundles.Add([PSCustomObject]@{ PartitionKey = $TenantFilter From fa1d9bebc6e86a19e90befb8b6618c152dcd01e1 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 10:42:11 -0400 Subject: [PATCH 16/33] function stats --- .../Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 | 4 ++-- .../CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 index 0847a2e2c79e..6df8287a30dc 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 @@ -49,8 +49,8 @@ function Push-AuditLogTenant { DefaultDomainName = $TenantFilter ContentType = $Bundle.contentType ContentUri = $Bundle.contentUri - ContentCreated = [datetime]::SpecifyKind($Bundle.contentCreated, [System.DateTimeKind]::Utc) - ContentExpiration = [datetime]::SpecifyKind($Bundle.contentExpiration, [System.DateTimeKind]::Utc) + ContentCreated = $Bundle.contentCreated + ContentExpiration = $Bundle.contentExpiration CIPPURL = [string]$CIPPURL ProcessingStatus = 'Pending' MatchedRules = '' diff --git a/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 index 96b17cceaa62..b8a2b05ed80c 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 @@ -31,6 +31,8 @@ function Write-CippFunctionStats { if ($Entity.$Property) { if ($Entity.$Property.GetType().Name -in ('Hashtable', 'PSCustomObject', 'OrderedHashtable')) { $StatEntity.$Property = [string]($Entity.$Property | ConvertTo-Json -Compress) + } elseif ($Entity.$Property.GetType().Name -eq 'DateTime' -and $Entity.$Property.Kind -eq 'Local') { + $StatEntity.$Property = $Entity.$Property.ToUniversalTime() } elseif ($Property -notin ('ETag', 'RowKey', 'PartitionKey', 'Timestamp', 'LastRefresh')) { $StatEntity.$Property = $Entity.$Property } From 11da714bca017bee06e200bee8a1687b09cd6c7c Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 11:11:47 -0400 Subject: [PATCH 17/33] Update Get-CIPPTimerFunctions.ps1 --- Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 index 55d3166af174..d0f199c50927 100644 --- a/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 @@ -53,7 +53,7 @@ function Get-CIPPTimerFunctions { if ($Status.LastOccurrence -eq 'Never') { $NextOccurrence = $NextOccurrences | Where-Object { $_ -le (Get-Date) } | Select-Object -First 1 } else { - $NextOccurrence = $NextOccurrences | Where-Object { $_ -gt $Status.LastOccurrence.DateTime -and $_ -le (Get-Date) } | Select-Object -First 1 + $NextOccurrence = $NextOccurrences | Where-Object { $_ -gt $Status.LastOccurrence.DateTime.ToLocalTime() -and $_ -le (Get-Date) } | Select-Object -First 1 } } From 58ab0b837d80f55f949239a26fe35ff020b19ec4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 21:58:48 -0400 Subject: [PATCH 18/33] Fix content bundle query --- .../Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 b/Modules/CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 index 89781ec70190..fb0c95130118 100644 --- a/Modules/CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 +++ b/Modules/CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 @@ -35,11 +35,12 @@ function Get-CIPPAuditLogContentBundles { throw 'AllTenants is not a valid tenant filter for webhooks' } + $Tenant = Get-Tenants -TenantFilter $TenantFilter if (!($TenantFilter -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$')) { $DefaultDomainName = $TenantFilter - $TenantFilter = (Get-Tenants | Where-Object { $_.defaultDomainName -eq $TenantFilter }).customerId + $TenantFilter = $Tenant.customerId } else { - $DefaultDomainName = (Get-Tenants | Where-Object { $_.customerId -eq $TenantFilter }).defaultDomainName + $DefaultDomainName = $Tenant.defaultDomainName } $WebhookTable = Get-CippTable -tablename 'webhookTable' From b536f29feb9aaefe04fa91419fcedc3f44fc2e3c Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 22:05:00 -0400 Subject: [PATCH 19/33] Update Get-CIPPAuditLogContentBundles.ps1 --- .../CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 b/Modules/CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 index fb0c95130118..a9bb8bb0cbce 100644 --- a/Modules/CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 +++ b/Modules/CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 @@ -35,7 +35,7 @@ function Get-CIPPAuditLogContentBundles { throw 'AllTenants is not a valid tenant filter for webhooks' } - $Tenant = Get-Tenants -TenantFilter $TenantFilter + $Tenant = Get-Tenants -TenantFilter $TenantFilter -IncludeErrors if (!($TenantFilter -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$')) { $DefaultDomainName = $TenantFilter $TenantFilter = $Tenant.customerId From 16752312006636a3adac01da602f111374324f29 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 22:07:10 -0400 Subject: [PATCH 20/33] Add exception handling --- .../Webhooks/Push-AuditLogTenant.ps1 | 65 ++++++++++--------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 index 6df8287a30dc..6f5dcc6cf2bd 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 @@ -36,41 +36,44 @@ function Push-AuditLogTenant { StartTime = $Item.StartTime EndTime = $Item.EndTime } - $LogBundles = Get-CIPPAuditLogContentBundles @ContentBundleQuery - $ExistingBundles = Get-CIPPAzDataTableEntity @AuditBundleTable -Filter "PartitionKey eq '$($Item.TenantFilter)' and ContentType eq '$LogType' and Timestamp ge datetime'$($LastHour)'" + try { + $LogBundles = Get-CIPPAuditLogContentBundles @ContentBundleQuery + $ExistingBundles = Get-CIPPAzDataTableEntity @AuditBundleTable -Filter "PartitionKey eq '$($Item.TenantFilter)' and ContentType eq '$LogType' and Timestamp ge datetime'$($LastHour)'" - foreach ($Bundle in $LogBundles) { - Write-Host ($Bundle.contentCreated + ' ' + $Bundle.contentExpiration) - Write-Host ($Bundle.contentCreated.GetType()) - if ($ExistingBundles.RowKey -notcontains $Bundle.contentId) { - $NewBundles.Add([PSCustomObject]@{ - PartitionKey = $TenantFilter - RowKey = $Bundle.contentId - DefaultDomainName = $TenantFilter - ContentType = $Bundle.contentType - ContentUri = $Bundle.contentUri - ContentCreated = $Bundle.contentCreated - ContentExpiration = $Bundle.contentExpiration - CIPPURL = [string]$CIPPURL - ProcessingStatus = 'Pending' - MatchedRules = '' - MatchedLogs = 0 - }) + foreach ($Bundle in $LogBundles) { + Write-Host ($Bundle.contentCreated + ' ' + $Bundle.contentExpiration) + Write-Host ($Bundle.contentCreated.GetType()) + if ($ExistingBundles.RowKey -notcontains $Bundle.contentId) { + $NewBundles.Add([PSCustomObject]@{ + PartitionKey = $TenantFilter + RowKey = $Bundle.contentId + DefaultDomainName = $TenantFilter + ContentType = $Bundle.contentType + ContentUri = $Bundle.contentUri + ContentCreated = $Bundle.contentCreated + ContentExpiration = $Bundle.contentExpiration + CIPPURL = [string]$CIPPURL + ProcessingStatus = 'Pending' + MatchedRules = '' + MatchedLogs = 0 + }) + } } + } catch { + Write-Information "Could not get audit log content bundles for $TenantFilter - $LogType, $($_.Exception.Message)" } - } - if (($NewBundles | Measure-Object).Count -gt 0) { - Add-CIPPAzDataTableEntity @AuditBundleTable -Entity $NewBundles -Force - Write-Information ($NewBundles | ConvertTo-Json -Depth 5 -Compress) + if (($NewBundles | Measure-Object).Count -gt 0) { + Add-CIPPAzDataTableEntity @AuditBundleTable -Entity $NewBundles -Force + Write-Information ($NewBundles | ConvertTo-Json -Depth 5 -Compress) - $Batch = $NewBundles | Select-Object @{Name = 'ContentId'; Expression = { $_.RowKey } }, @{Name = 'TenantFilter'; Expression = { $_.PartitionKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogBundleProcessing' } } - $InputObject = [PSCustomObject]@{ - OrchestratorName = 'AuditLogs' - Batch = @($Batch) - SkipLog = $true + $Batch = $NewBundles | Select-Object @{Name = 'ContentId'; Expression = { $_.RowKey } }, @{Name = 'TenantFilter'; Expression = { $_.PartitionKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogBundleProcessing' } } + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'AuditLogs' + Batch = @($Batch) + SkipLog = $true + } + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) + Write-Host "Started orchestration with ID = '$InstanceId'" } - $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) - Write-Host "Started orchestration with ID = '$InstanceId'" } -} From ff8f219b9aaf7fa3324228ca0492988e3594c861 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 22:10:40 -0400 Subject: [PATCH 21/33] Update Push-AuditLogTenant.ps1 --- .../Webhooks/Push-AuditLogTenant.ps1 | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 index 6f5dcc6cf2bd..5a71ee468f27 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 @@ -62,18 +62,19 @@ function Push-AuditLogTenant { } catch { Write-Information "Could not get audit log content bundles for $TenantFilter - $LogType, $($_.Exception.Message)" } + } - if (($NewBundles | Measure-Object).Count -gt 0) { - Add-CIPPAzDataTableEntity @AuditBundleTable -Entity $NewBundles -Force - Write-Information ($NewBundles | ConvertTo-Json -Depth 5 -Compress) + if (($NewBundles | Measure-Object).Count -gt 0) { + Add-CIPPAzDataTableEntity @AuditBundleTable -Entity $NewBundles -Force + Write-Information ($NewBundles | ConvertTo-Json -Depth 5 -Compress) - $Batch = $NewBundles | Select-Object @{Name = 'ContentId'; Expression = { $_.RowKey } }, @{Name = 'TenantFilter'; Expression = { $_.PartitionKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogBundleProcessing' } } - $InputObject = [PSCustomObject]@{ - OrchestratorName = 'AuditLogs' - Batch = @($Batch) - SkipLog = $true - } - $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) - Write-Host "Started orchestration with ID = '$InstanceId'" + $Batch = $NewBundles | Select-Object @{Name = 'ContentId'; Expression = { $_.RowKey } }, @{Name = 'TenantFilter'; Expression = { $_.PartitionKey } }, @{Name = 'FunctionName'; Expression = { 'AuditLogBundleProcessing' } } + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'AuditLogs' + Batch = @($Batch) + SkipLog = $true } + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) + Write-Host "Started orchestration with ID = '$InstanceId'" } +} From 4e931fe3f5dc4d0f740fc53656971ee2f94774c5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 22:11:08 -0400 Subject: [PATCH 22/33] Update Push-AuditLogTenant.ps1 --- .../Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 index 5a71ee468f27..8525ff065e34 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 @@ -41,8 +41,6 @@ function Push-AuditLogTenant { $ExistingBundles = Get-CIPPAzDataTableEntity @AuditBundleTable -Filter "PartitionKey eq '$($Item.TenantFilter)' and ContentType eq '$LogType' and Timestamp ge datetime'$($LastHour)'" foreach ($Bundle in $LogBundles) { - Write-Host ($Bundle.contentCreated + ' ' + $Bundle.contentExpiration) - Write-Host ($Bundle.contentCreated.GetType()) if ($ExistingBundles.RowKey -notcontains $Bundle.contentId) { $NewBundles.Add([PSCustomObject]@{ PartitionKey = $TenantFilter From 45e5551935fce812abcdf276f18bd4ffe41f2501 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 22:22:20 -0400 Subject: [PATCH 23/33] Add status for no orchestrator run --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 7b599f991fc0..8e2ba4381ad4 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -243,8 +243,10 @@ function Receive-CIPPTimerTrigger { $Results = Invoke-Command -ScriptBlock { & $Function.Command } if ($Results -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') { $FunctionStatus.OrchestratorId = $Results + $Status = 'Started' + } else { + $Status = 'Completed' } - $Status = 'Started' } catch { $Status = 'Failed' } From 244e3e799b8d8f3c860fa6ce522326c3105c0689 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 22:36:35 -0400 Subject: [PATCH 24/33] Add orchestrator rerun protection --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 8e2ba4381ad4..8a321bc3f548 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -239,6 +239,16 @@ function Receive-CIPPTimerTrigger { foreach ($Function in $Functions) { Write-Information "CIPPTimer: $($Function.Command) - $($Function.Cron)" $FunctionStatus = $Statuses | Where-Object { $_.RowKey -eq $Function.Command } + if ($FunctionStatus.OrchestratorId) { + $FunctionName = $env:WEBSITE_SITE_NAME + $InstancesTable = Get-CippTable -TableName ('{0}Instances' -f $FunctionName) + $Instance = Get-CIPPAzDataTableEntity @InstancesTable -Filter "PartitionKey eq '$($FunctionStatus.OrchestratorId)'" + if ($Instance.RuntimeStatus -eq 'Running') { + Write-LogMessage -message "CIPP Timer: $($Function.Command) - $($FunctionStatus.OrchestratorId) is still running" -sev Info + Write-Information "CIPP Timer: $($Function.Command) - $($FunctionStatus.OrchestratorId) is still running, skipping execution" + continue + } + } try { $Results = Invoke-Command -ScriptBlock { & $Function.Command } if ($Results -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') { From eb7ed9ac677c50ce6da67ab086e7f5011e74fc4a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 22:40:33 -0400 Subject: [PATCH 25/33] update logging --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 8a321bc3f548..c8a23179fb63 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -244,8 +244,8 @@ function Receive-CIPPTimerTrigger { $InstancesTable = Get-CippTable -TableName ('{0}Instances' -f $FunctionName) $Instance = Get-CIPPAzDataTableEntity @InstancesTable -Filter "PartitionKey eq '$($FunctionStatus.OrchestratorId)'" if ($Instance.RuntimeStatus -eq 'Running') { - Write-LogMessage -message "CIPP Timer: $($Function.Command) - $($FunctionStatus.OrchestratorId) is still running" -sev Info - Write-Information "CIPP Timer: $($Function.Command) - $($FunctionStatus.OrchestratorId) is still running, skipping execution" + Write-LogMessage -API 'TimerFunction' -message "$($Function.Command) - $($FunctionStatus.OrchestratorId) is still running" -sev Warn -LogData $FunctionStatus + Write-Warning "CIPP Timer: $($Function.Command) - $($FunctionStatus.OrchestratorId) is still running, skipping execution" continue } } From 7749771ba736bc08f9b54f38c0765ceb35aab56a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 22:41:54 -0400 Subject: [PATCH 26/33] Update CippEntrypoints.psm1 --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index c8a23179fb63..599f719f0d7c 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -242,7 +242,7 @@ function Receive-CIPPTimerTrigger { if ($FunctionStatus.OrchestratorId) { $FunctionName = $env:WEBSITE_SITE_NAME $InstancesTable = Get-CippTable -TableName ('{0}Instances' -f $FunctionName) - $Instance = Get-CIPPAzDataTableEntity @InstancesTable -Filter "PartitionKey eq '$($FunctionStatus.OrchestratorId)'" + $Instance = Get-CIPPAzDataTableEntity @InstancesTable -Filter "PartitionKey eq '$($FunctionStatus.OrchestratorId)'" -Property RuntimeStatus if ($Instance.RuntimeStatus -eq 'Running') { Write-LogMessage -API 'TimerFunction' -message "$($Function.Command) - $($FunctionStatus.OrchestratorId) is still running" -sev Warn -LogData $FunctionStatus Write-Warning "CIPP Timer: $($Function.Command) - $($FunctionStatus.OrchestratorId) is still running, skipping execution" From 8e15eb956860b73e7111520284de61b2de2b885a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 22:56:38 -0400 Subject: [PATCH 27/33] Queue trigger for sending commands to proc --- .../Tenant/Standards/Invoke-ExecBPA.ps1 | 29 +++++++++++++++---- .../Push-CIPPFunctionProcessor.ps1 | 6 ++-- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 index fb2b4f4e7c48..ef97a99df975 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 @@ -8,11 +8,28 @@ function Invoke-ExecBPA { [CmdletBinding()] param($Request, $TriggerMetadata) - Start-BPAOrchestrator -TenantFilter $Request.Query.TenantFilter - $Results = [pscustomobject]@{'Results' = 'BPA started' } - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) + $ConfigTable = Get-CIPPTable -tablename Config + $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" + + if ($Config -and $Config.state -eq $true) { + if ($env:CIPP_PROCESSOR -ne 'true') { + $ProcessorFunction = [PSCustomObject]@{ + FunctionName = 'CIPPFunctionProcessor' + ProcessorFunction = 'Start-BPAOrchestrator' + Parameters = [PSCustomObject]@{ + TenantFilter = $Request.Query.TenantFilter + } + } + Push-OutputBinding -Name QueueItem -Value $ProcessorFunction + } + } else { + Start-BPAOrchestrator -TenantFilter $Request.Query.TenantFilter + + $Results = [pscustomobject]@{'Results' = 'BPA started' } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 index 55751e4bb4a6..bc1878ee5026 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 @@ -10,13 +10,15 @@ function Push-CIPPFunctionProcessor { $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" if ($Config -and $Config.state -eq $true) { - if ($env:CIPP_PROCESSOR -ne 'true' -and !$All.IsPresent) { + if ($env:CIPP_PROCESSOR -ne 'true') { return } } + $Parameters = $QueueItem.Parameters | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashtable + if (Get-Command -Name $QueueItem.FunctionName -Module CIPPCore -ErrorAction SilentlyContinue) { - & $QueueItem.FunctionName + & $QueueItem.ProcessorFunction @Parameters } else { Write-Warning "Function $($QueueItem.FunctionName) not found" } From 57c8d49677ac789add9cafd666a8f8bef6c8b063 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 23:00:37 -0400 Subject: [PATCH 28/33] logging --- .../Tenant/Standards/Invoke-ExecBPA.ps1 | 12 ++++++------ .../Queue Function/Push-CIPPFunctionProcessor.ps1 | 2 ++ Modules/CippEntrypoints/CippEntrypoints.psm1 | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 index ef97a99df975..5370a6b7d19e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 @@ -25,11 +25,11 @@ function Invoke-ExecBPA { } } else { Start-BPAOrchestrator -TenantFilter $Request.Query.TenantFilter - - $Results = [pscustomobject]@{'Results' = 'BPA started' } - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) } + $Results = [pscustomobject]@{'Results' = 'BPA started' } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + } diff --git a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 index bc1878ee5026..4d5a61749968 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 @@ -6,6 +6,8 @@ function Push-CIPPFunctionProcessor { [CmdletBinding()] param($QueueItem) + Write-Information 'Processor - Received message from queue' + Write-Information ($QueueItem | ConvertTo-Json) $ConfigTable = Get-CIPPTable -tablename Config $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 599f719f0d7c..1d2f5b880fca 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -14,8 +14,8 @@ function Receive-CippHttpTrigger { $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" if ($Config -and $Config.state -eq $true) { - Write-Information 'No API Calls' if ($env:CIPP_PROCESSOR -eq 'true') { + Write-Information 'No API Calls' Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::Forbidden Body = 'API calls are not accepted on this function app' From b18fdee9d68098b2ffe01824bb3519178d48fe79 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 23:04:14 -0400 Subject: [PATCH 29/33] Update Push-CIPPFunctionProcessor.ps1 --- .../Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 index 4d5a61749968..b1188963a89f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 @@ -4,7 +4,7 @@ function Push-CIPPFunctionProcessor { Starts a specified function on the processor node #> [CmdletBinding()] - param($QueueItem) + param($QueueItem, $TriggerMetadata) Write-Information 'Processor - Received message from queue' Write-Information ($QueueItem | ConvertTo-Json) From 5be7805ca88d44467b03ecae0ece9a279d2c3f5b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 23:07:41 -0400 Subject: [PATCH 30/33] Update Push-CIPPFunctionProcessor.ps1 --- .../Queue Function/Push-CIPPFunctionProcessor.ps1 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 index b1188963a89f..f0b6df820bd2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 @@ -7,7 +7,7 @@ function Push-CIPPFunctionProcessor { param($QueueItem, $TriggerMetadata) Write-Information 'Processor - Received message from queue' - Write-Information ($QueueItem | ConvertTo-Json) + $ConfigTable = Get-CIPPTable -tablename Config $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" @@ -18,10 +18,11 @@ function Push-CIPPFunctionProcessor { } $Parameters = $QueueItem.Parameters | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashtable - - if (Get-Command -Name $QueueItem.FunctionName -Module CIPPCore -ErrorAction SilentlyContinue) { + Write-Information "Running $ProcessorFunction" + Write-Information ($Parameters | ConvertTo-Json) + if (Get-Command -Name $QueueItem.ProcessorFunction -Module CIPPCore -ErrorAction SilentlyContinue) { & $QueueItem.ProcessorFunction @Parameters } else { - Write-Warning "Function $($QueueItem.FunctionName) not found" + Write-Warning "Function $($QueueItem.ProcessorFunction) not found" } } From 7070d1708b996c39c05fb313039b3e6f8f590147 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 23:18:17 -0400 Subject: [PATCH 31/33] add processor queue logic --- .../Tenant/Standards/Invoke-ExecBPA.ps1 | 1 - .../Standards/Invoke-ExecDomainAnalyser.ps1 | 15 +++++++++- .../Standards/Invoke-ExecStandardsRun.ps1 | 29 +++++++++++++++---- .../Push-CIPPFunctionProcessor.ps1 | 2 -- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 index 5370a6b7d19e..faa81a43174d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 @@ -8,7 +8,6 @@ function Invoke-ExecBPA { [CmdletBinding()] param($Request, $TriggerMetadata) - $ConfigTable = Get-CIPPTable -tablename Config $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecDomainAnalyser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecDomainAnalyser.ps1 index 50de572b0729..3cee5d844438 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecDomainAnalyser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecDomainAnalyser.ps1 @@ -8,7 +8,20 @@ function Invoke-ExecDomainAnalyser { [CmdletBinding()] param($Request, $TriggerMetadata) - Start-DomainOrchestrator + $ConfigTable = Get-CIPPTable -tablename Config + $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" + + if ($Config -and $Config.state -eq $true) { + if ($env:CIPP_PROCESSOR -ne 'true') { + $ProcessorFunction = [PSCustomObject]@{ + FunctionName = 'CIPPFunctionProcessor' + ProcessorFunction = 'Start-DomainOrchestrator' + } + Push-OutputBinding -Name QueueItem -Value $ProcessorFunction + } + } else { + Start-DomainOrchestrator + } Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 index 032bdea91f1f..3452d9b33d00 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 @@ -12,11 +12,30 @@ Function Invoke-ExecStandardsRun { $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' $tenantfilter = if ($Request.Query.TenantFilter) { $Request.Query.TenantFilter } else { 'allTenants' } - try { - $null = Invoke-CIPPStandardsRun -Tenantfilter $tenantfilter -Force - $Results = "Successfully Started Standards Run for Tenant $tenantfilter" - } catch { - $Results = "Failed to start standards run for $tenantfilter. Error: $($_.Exception.Message)" + + $ConfigTable = Get-CIPPTable -tablename Config + $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" + + if ($Config -and $Config.state -eq $true) { + if ($env:CIPP_PROCESSOR -ne 'true') { + $ProcessorFunction = [PSCustomObject]@{ + FunctionName = 'CIPPFunctionProcessor' + ProcessorFunction = 'Invoke-CIPPStandardsRun' + Parameters = [PSCustomObject]@{ + TenantFilter = $tenantfilter + Force = $true + } + } + Push-OutputBinding -Name QueueItem -Value $ProcessorFunction + $Results = "Successfully Queued Standards Run for Tenant $tenantfilter" + } + } else { + try { + $null = Invoke-CIPPStandardsRun -Tenantfilter $tenantfilter -Force + $Results = "Successfully Started Standards Run for Tenant $tenantfilter" + } catch { + $Results = "Failed to start standards run for $tenantfilter. Error: $($_.Exception.Message)" + } } $Results = [pscustomobject]@{'Results' = "$results" } diff --git a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 index f0b6df820bd2..f40930a00f7e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 @@ -18,8 +18,6 @@ function Push-CIPPFunctionProcessor { } $Parameters = $QueueItem.Parameters | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashtable - Write-Information "Running $ProcessorFunction" - Write-Information ($Parameters | ConvertTo-Json) if (Get-Command -Name $QueueItem.ProcessorFunction -Module CIPPCore -ErrorAction SilentlyContinue) { & $QueueItem.ProcessorFunction @Parameters } else { From e6c1aba8c6795d3c37c5a7afbf2619b8b92409e6 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 23:30:49 -0400 Subject: [PATCH 32/33] Migrate more functions to CIPPTimers --- CIPPTimers.json | 14 +++++++++ .../Applications/Invoke-ExecAppUpload.ps1 | 21 ++++++++++--- .../Start-ExtensionOrchestrator.ps1 | 23 ++++++++++++++ .../Timer Functions/Start-BillingTimer.ps1 | 30 +++++++++++++++++++ Scheduler_Billing/function.json | 10 ------- Scheduler_Billing/run.ps1 | 22 -------------- Scheduler_Extensions/function.json | 15 ---------- Scheduler_Extensions/run.ps1 | 14 --------- 8 files changed, 84 insertions(+), 65 deletions(-) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-ExtensionOrchestrator.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-BillingTimer.ps1 delete mode 100644 Scheduler_Billing/function.json delete mode 100644 Scheduler_Billing/run.ps1 delete mode 100644 Scheduler_Extensions/function.json delete mode 100644 Scheduler_Extensions/run.ps1 diff --git a/CIPPTimers.json b/CIPPTimers.json index e5f712c834e4..9850c2f95444 100644 --- a/CIPPTimers.json +++ b/CIPPTimers.json @@ -78,6 +78,13 @@ "RunOnProcessor": true, "IsSystem": true }, + { + "Command": "Start-BillingTimer", + "Description": "Timer to process billing", + "Cron": "0 0 0 * * *", + "Priority": 12, + "RunOnProcessor": true + }, { "Command": "Start-BPAOrchestrator", "Description": "Orchestrator to process BPA reports", @@ -85,6 +92,13 @@ "Priority": 10, "RunOnProcessor": true }, + { + "Command": "Start-ExtensionOrchestrator", + "Description": "Orchestrator to process extensions", + "Cron": "0 0 */2 * * *", + "Priority": 12, + "RunOnProcessor": true + }, { "Command": "Start-CIPPStatsTimer", "Description": "Timer to process CIPP stats", diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ExecAppUpload.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ExecAppUpload.ps1 index 366da173390e..528f2c66c40d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ExecAppUpload.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ExecAppUpload.ps1 @@ -8,10 +8,23 @@ function Invoke-ExecAppUpload { [CmdletBinding()] param($Request, $TriggerMetadata) - try { - Start-ApplicationOrchestrator - } catch { - Write-Host "orchestrator error: $($_.Exception.Message)" + $ConfigTable = Get-CIPPTable -tablename Config + $Config = Get-CIPPAzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'OffloadFunctions' and RowKey eq 'OffloadFunctions'" + + if ($Config -and $Config.state -eq $true) { + if ($env:CIPP_PROCESSOR -ne 'true') { + $ProcessorFunction = [PSCustomObject]@{ + FunctionName = 'CIPPFunctionProcessor' + ProcessorFunction = 'Start-ApplicationOrchestrator' + } + Push-OutputBinding -Name QueueItem -Value $ProcessorFunction + } + } else { + try { + Start-ApplicationOrchestrator + } catch { + Write-Host "orchestrator error: $($_.Exception.Message)" + } } $Results = [pscustomobject]@{'Results' = 'Started application queue' } diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-ExtensionOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-ExtensionOrchestrator.ps1 new file mode 100644 index 000000000000..55126c5080ca --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-ExtensionOrchestrator.ps1 @@ -0,0 +1,23 @@ +function Start-ExtensionOrchestrator { + <# + .SYNOPSIS + Start the Extension Orchestrator + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + + $Table = Get-CIPPTable -TableName Extensionsconfig + + $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json) + + Write-Host 'Started Scheduler for Extensions' + + # NinjaOne Extension + if ($Configuration.NinjaOne.Enabled -eq $True) { + if ($PSCmdlet.ShouldProcess('Invoke-NinjaOneExtensionScheduler')) { + Invoke-NinjaOneExtensionScheduler + } + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-BillingTimer.ps1 b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-BillingTimer.ps1 new file mode 100644 index 000000000000..99500af4ed9f --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-BillingTimer.ps1 @@ -0,0 +1,30 @@ +function Start-BillingTimer { + <# + .SYNOPSIS + Start the Billing Timer + .DESCRIPTION + This function starts the Billing Timner + .FUNCTIONALITY + Entrypoint + #> + + [CmdletBinding(SupportsShouldProcess = $true)] + param() + try { + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message 'Starting billing processing.' -sev Info + + $Table = Get-CIPPTable -TableName Extensionsconfig + $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 + foreach ($ConfigItem in $Configuration.psobject.properties.name) { + switch ($ConfigItem) { + 'Gradient' { + If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) { + New-GradientServiceSyncRun + } + } + } + } + } catch { + Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message "Could not start billing processing $($_.Exception.Message)" -sev Error + } +} diff --git a/Scheduler_Billing/function.json b/Scheduler_Billing/function.json deleted file mode 100644 index d2e7f34face4..000000000000 --- a/Scheduler_Billing/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "0 0 0 * * *" - } - ] -} diff --git a/Scheduler_Billing/run.ps1 b/Scheduler_Billing/run.ps1 deleted file mode 100644 index 4c14defea81b..000000000000 --- a/Scheduler_Billing/run.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -# Input bindings are passed in via param block. -param($Timer) - -# Get the current universal time in the default string format. -try { - Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Starting billing processing." -sev Info - - $Table = Get-CIPPTable -TableName Extensionsconfig - $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 - foreach ($ConfigItem in $Configuration.psobject.properties.name) { - switch ($ConfigItem) { - "Gradient" { - If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) { - New-GradientServiceSyncRun - } - } - } - } -} -catch { - Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Could not start billing processing $($_.Exception.Message)" -sev Error -} \ No newline at end of file diff --git a/Scheduler_Extensions/function.json b/Scheduler_Extensions/function.json deleted file mode 100644 index c178a68af69f..000000000000 --- a/Scheduler_Extensions/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "schedule": "0 0 */2 * * *", - "direction": "in", - "type": "timerTrigger" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} diff --git a/Scheduler_Extensions/run.ps1 b/Scheduler_Extensions/run.ps1 deleted file mode 100644 index 58e228ebbe1d..000000000000 --- a/Scheduler_Extensions/run.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -using namespace System.Net - -param($Timer) - -$Table = Get-CIPPTable -TableName Extensionsconfig - -$Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json) - -Write-Host 'Started Scheduler for Extensions' - -# NinjaOne Extension -if ($Configuration.NinjaOne.Enabled -eq $True) { - Invoke-NinjaOneExtensionScheduler -} \ No newline at end of file From ddb3aa5d91aa6017bb96f8f1ecb13d3ec9373ef5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 23:39:34 -0400 Subject: [PATCH 33/33] Update CippEntrypoints.psm1 --- Modules/CippEntrypoints/CippEntrypoints.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 1d2f5b880fca..e5dd31931fb4 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -241,7 +241,7 @@ function Receive-CIPPTimerTrigger { $FunctionStatus = $Statuses | Where-Object { $_.RowKey -eq $Function.Command } if ($FunctionStatus.OrchestratorId) { $FunctionName = $env:WEBSITE_SITE_NAME - $InstancesTable = Get-CippTable -TableName ('{0}Instances' -f $FunctionName) + $InstancesTable = Get-CippTable -TableName ('{0}Instances' -f ($FunctionName -replace '-', '')) $Instance = Get-CIPPAzDataTableEntity @InstancesTable -Filter "PartitionKey eq '$($FunctionStatus.OrchestratorId)'" -Property RuntimeStatus if ($Instance.RuntimeStatus -eq 'Running') { Write-LogMessage -API 'TimerFunction' -message "$($Function.Command) - $($FunctionStatus.OrchestratorId) is still running" -sev Warn -LogData $FunctionStatus