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_Extensions/function.json b/CIPPTimer/function.json similarity index 58% rename from Scheduler_Extensions/function.json rename to CIPPTimer/function.json index c178a68af69f..55e4edb35f11 100644 --- a/Scheduler_Extensions/function.json +++ b/CIPPTimer/function.json @@ -1,8 +1,10 @@ { + "scriptFile": "../Modules/CippEntrypoints/CippEntrypoints.psm1", + "entryPoint": "Receive-CippTimerTrigger", "bindings": [ { "name": "Timer", - "schedule": "0 0 */2 * * *", + "schedule": "0 0/15 * * * *", "direction": "in", "type": "timerTrigger" }, diff --git a/CIPPTimers.json b/CIPPTimers.json new file mode 100644 index 000000000000..9850c2f95444 --- /dev/null +++ b/CIPPTimers.json @@ -0,0 +1,110 @@ +[ + { + "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-BillingTimer", + "Description": "Timer to process billing", + "Cron": "0 0 0 * * *", + "Priority": 12, + "RunOnProcessor": true + }, + { + "Command": "Start-BPAOrchestrator", + "Description": "Orchestrator to process BPA reports", + "Cron": "0 0 3 * * *", + "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", + "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/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 index e74208b8887c..8525ff065e34 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenant.ps1 @@ -36,25 +36,29 @@ 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) { - 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) { + 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)" } } 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/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 index fb2b4f4e7c48..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,11 +8,27 @@ function Invoke-ExecBPA { [CmdletBinding()] param($Request, $TriggerMetadata) - Start-BPAOrchestrator -TenantFilter $Request.Query.TenantFilter + $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/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/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-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/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..f40930a00f7e --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Queue Function/Push-CIPPFunctionProcessor.ps1 @@ -0,0 +1,26 @@ +function Push-CIPPFunctionProcessor { + <# + .SYNOPSIS + Starts a specified function on the processor node + #> + [CmdletBinding()] + param($QueueItem, $TriggerMetadata) + + Write-Information 'Processor - Received message from queue' + + $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') { + return + } + } + + $Parameters = $QueueItem.Parameters | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashtable + if (Get-Command -Name $QueueItem.ProcessorFunction -Module CIPPCore -ErrorAction SilentlyContinue) { + & $QueueItem.ProcessorFunction @Parameters + } else { + Write-Warning "Function $($QueueItem.ProcessorFunction) not found" + } +} 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/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..d0f199c50927 --- /dev/null +++ b/Modules/CIPPCore/Public/Get-CIPPTimerFunctions.ps1 @@ -0,0 +1,101 @@ +function Get-CIPPTimerFunctions { + [CmdletBinding()] + param( + [switch]$All, + [switch]$ResetToDefault + ) + + $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 = [datetime]$Cron.GetNextOccurrence($Now) + } else { + $NextOccurrences = $Cron.GetNextOccurrences($Now.AddMinutes(-15), $Now.AddMinutes(15)) + 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.ToLocalTime() -and $_ -le (Get-Date) } | Select-Object -First 1 + } + } + + if (Get-Command -Name $Orchestrator.Command -Module CIPPCore -ErrorAction SilentlyContinue) { + 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 + NextOccurrence = $NextOccurrence.ToUniversalTime() + LastOccurrence = $Status.LastOccurrence.DateTime + 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/Public/GraphHelper/Write-CippFunctionStats.ps1 b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 index 0e295e96cc46..b8a2b05ed80c 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 @@ -42,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 } 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' } { diff --git a/Modules/CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 b/Modules/CIPPCore/Public/Webhooks/Get-CIPPAuditLogContentBundles.ps1 index 89781ec70190..a9bb8bb0cbce 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 -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 = (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' diff --git a/Modules/CIPPCore/lib/NCrontab.Advanced.dll b/Modules/CIPPCore/lib/NCrontab.Advanced.dll new file mode 100644 index 000000000000..3aff438ecf86 Binary files /dev/null and b/Modules/CIPPCore/lib/NCrontab.Advanced.dll differ diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index aae4096c53a1..e5dd31931fb4 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -9,11 +9,26 @@ 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 -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' + }) + return + } + } + # 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 $FunctionName = 'Invoke-{0}' -f $Request.Params.CIPPEndpoint - Write-Host "Function: $($Request.Params.CIPPEndpoint)" + Write-Information "Function: $($Request.Params.CIPPEndpoint)" $HttpTrigger = @{ Request = [pscustomobject]($Request) @@ -44,7 +59,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 +228,43 @@ 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 + $Statuses = Get-CIPPAzDataTableEntity @Table + + 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 -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 + Write-Warning "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}$') { + $FunctionStatus.OrchestratorId = $Results + $Status = 'Started' + } else { + $Status = 'Completed' + } + } catch { + $Status = 'Failed' + } + $FunctionStatus.LastOccurrence = $UtcNow + $FunctionStatus.Status = $Status + Add-CIPPAzDataTableEntity @Table -Entity $FunctionStatus -Force + } +} + +Export-ModuleMember -Function @('Receive-CippHttpTrigger', 'Receive-CippQueueTrigger', 'Receive-CippOrchestrationTrigger', 'Receive-CippActivityTrigger', 'Receive-CIPPTimerTrigger') 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 - }) 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/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 diff --git a/Scheduler_GetQueue/function.json b/Scheduler_GetQueue/function.json deleted file mode 100644 index 56e4cf0cfda1..000000000000 --- a/Scheduler_GetQueue/function.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "bindings": [ - { - "name": "Timer", - "schedule": "0 0 * * * *", - "direction": "in", - "type": "timerTrigger" - }, - { - "name": "starter", - "type": "durableClient", - "direction": "in" - } - ] -} 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" } ] }