From 4e4a4affbc3a46c391c2a863761f296bf18966f7 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 27 Feb 2024 13:53:26 +0100 Subject: [PATCH 01/45] improvements to mailbox auditing to make it faster --- .../Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 index 3a9e7ef1eacd..6d959f03d8d9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 @@ -21,7 +21,7 @@ function Invoke-CIPPStandardEnableMailboxAuditing { } # Check for mailbox audit on all mailboxes. Enable for all that it's not enabled for - $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ResultSize = 'Unlimited' } | Where-Object { $_.AuditEnabled -ne $true } + $Mailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{filter = "auditenabled -eq 'False'" } -useSystemMailbox $true -Select 'AuditEnabled,UserPrincipalName' $Mailboxes | ForEach-Object { try { New-ExoRequest -tenantid $Tenant -cmdlet 'Set-Mailbox' -cmdParams @{Identity = $_.UserPrincipalName; AuditEnabled = $true } -Anchor $_.UserPrincipalName @@ -32,7 +32,8 @@ function Invoke-CIPPStandardEnableMailboxAuditing { } # Disable audit bypass for all mailboxes that have it enabled - $BypassMailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxAuditBypassAssociation' -cmdParams @{ResultSize = 'Unlimited' } | Where-Object { $_.AuditBypassEnabled -eq $true } + + $BypassMailboxes = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxAuditBypassAssociation' -select 'GUID, AuditBypassEnabled, Name' -useSystemMailbox $true | Where-Object { $_.AuditBypassEnabled -eq $true } $BypassMailboxes | ForEach-Object { try { New-ExoRequest -tenantid $Tenant -cmdlet 'Set-MailboxAuditBypassAssociation' -cmdParams @{Identity = $_.Guid; AuditBypassEnabled = $false } -UseSystemMailbox $true From a268f187576296efdea790342b5fc21c88c96d95 Mon Sep 17 00:00:00 2001 From: BNWEIN Date: Tue, 27 Feb 2024 18:12:14 +0000 Subject: [PATCH 02/45] Update run.ps1 added DKIM Records to table and export --- DomainAnalyser_All/run.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DomainAnalyser_All/run.ps1 b/DomainAnalyser_All/run.ps1 index d0da4a3e09da..468962b0ec20 100644 --- a/DomainAnalyser_All/run.ps1 +++ b/DomainAnalyser_All/run.ps1 @@ -52,6 +52,7 @@ $Result = [PSCustomObject]@{ DNSSECPresent = '' MailProvider = '' DKIMEnabled = '' + DKIMRecords = '' Score = '' MaximumScore = 160 ScorePercentage = '' @@ -218,6 +219,7 @@ try { if ($DkimRecordCount -gt 0 -and $DkimFailCount -eq 0) { $Result.DKIMEnabled = $true $ScoreDomain += $Scores.DKIMActiveAndWorking + $Result.DKIMRecords = $DkimRecord.Records | Select-Object Selector, Record } else { $Result.DKIMEnabled = $false $ScoreExplanation.Add('DKIM Not Configured') | Out-Null From c0739fb224f5c64e8be74ad95f891b19e4431477 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 28 Feb 2024 14:41:21 +0100 Subject: [PATCH 03/45] fixes sign in log --- .../Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 index d4fe27a93764..d421b79441d5 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSigninLogs.ps1 @@ -40,9 +40,14 @@ Function Invoke-ListUserSigninLogs { @{ Name = 'FailureReason'; Expression = { $_.status.failureReason } }, @{ Name = 'FullDetails'; Expression = { $_ } } # Associate values to output bindings by calling 'Push-OutputBinding'. + if ($GraphRequest.FullDetails -eq $null) { + $GraphRequest = $null + } else { + $GraphRequest = @($GraphRequest) + } Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK - Body = @($GraphRequest) + Body = $GraphRequest }) } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to retrieve Sign In report: $($_.Exception.message) " -Sev 'Error' -tenant $TenantFilter From aadb9b79e5d6a4a80e6c1456737af0b938dc8671 Mon Sep 17 00:00:00 2001 From: BNWEIN Date: Wed, 28 Feb 2024 14:29:22 +0000 Subject: [PATCH 04/45] Added custom thresholds for SharePoint and Mailbox Quota alerts Added custom thresholds for SharePoint and Mailbox Quota alerts --- .../CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 | 4 ++-- .../Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 | 9 +++++++-- .../Entrypoints/Push-CIPPAlertSharepointQuota.ps1 | 9 +++++++-- .../Public/Entrypoints/Push-SchedulerAlert.ps1 | 11 ++++++----- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 index 9bc6df14e424..ad22d9aef8b1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddAlert.ps1 @@ -32,7 +32,7 @@ Function Invoke-AddAlert { MFAAlertUsers = [bool]$Request.body.MFAAlertUsers NewGA = [bool]$Request.body.NewGA NewRole = [bool]$Request.body.NewRole - QuotaUsed = [bool]$Request.body.QuotaUsed + QuotaUsed = [int]$Request.body.QuotaUsedQuota UnusedLicenses = [bool]$Request.body.UnusedLicenses OverusedLicenses = [bool]$Request.body.OverusedLicenses AppSecretExpiry = [bool]$Request.body.AppSecretExpiry @@ -41,7 +41,7 @@ Function Invoke-AddAlert { DepTokenExpiry = [bool]$Request.body.DepTokenExpiry NoCAConfig = [bool]$Request.body.NoCAConfig SecDefaultsUpsell = [bool]$Request.body.SecDefaultsUpsell - SharePointQuota = [bool]$Request.body.SharePointQuota + SharePointQuota = [int]$Request.body.SharePointQuotaQuota ExpiringLicenses = [bool]$Request.body.ExpiringLicenses type = 'Alert' RowKey = $TenantID diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 index 47d455057c19..1d008a4c784d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 @@ -11,8 +11,13 @@ function Push-CIPPAlertQuotaUsed { New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getMailboxUsageDetail(period='D7')?`$format=application/json" -tenantid $QueueItem.tenant | ForEach-Object { if ($_.StorageUsedInBytes -eq 0) { continue } $PercentLeft = [math]::round($_.StorageUsedInBytes / $_.prohibitSendReceiveQuotaInBytes * 100) - if ($PercentLeft -gt 90) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.UserPrincipalName): Mailbox has less than 10% space left. Mailbox is $PercentLeft% full" + if ($QueueItem.value -eq $true) { + if ($PercentLeft -gt 90) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.UserPrincipalName): Mailbox is more than $($QueueItem.value)% full. Mailbox is $PercentLeft% full" + } + } + elseif ($PercentLeft -gt $QueueItem.value) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.UserPrincipalName): Mailbox is more than $($QueueItem.value)% full. Mailbox is $PercentLeft% full" } } } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 index 0f010b2bb59c..d96dd801aedb 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 @@ -13,8 +13,13 @@ function Push-CIPPAlertSharepointQuota { $sharepointQuota = (Invoke-RestMethod -Method 'GET' -Headers $sharepointToken -Uri "https://$($tenantName)-admin.sharepoint.com/_api/StorageQuotas()?api-version=1.3.2" -ErrorAction Stop).value if ($sharepointQuota) { $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100) - if ($UsedStoragePercentage -gt 90) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%" + if ($QueueItem.value -eq $true){ + if ($UsedStoragePercentage -gt 90) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%. Your alert threshold is 90%" + } + } + elseif ($UsedStoragePercentage -gt $QueueItem.value) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%. Your alert threshold is $($QueueItem.value)%" } } } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 index 5b32b6fb3c51..30a4b2583cde 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 @@ -15,22 +15,23 @@ function Push-SchedulerAlert { $IgnoreList = @('Etag', 'PartitionKey', 'Timestamp', 'RowKey', 'tenantid', 'tenant', 'type') $alertList = $Alerts | Select-Object * -ExcludeProperty $IgnoreList - foreach ($task in ($AlertList.psobject.members | Where-Object { $_.MemberType -EQ 'NoteProperty' -and $_.value -eq $True }).name) { + foreach ($task in ($AlertList.psobject.members | Where-Object { $_.MemberType -EQ 'NoteProperty' -and $_.value -ne $false })) { $Table = Get-CIPPTable -TableName AlertRunCheck - $Filter = "PartitionKey eq '{0}' and RowKey eq '{1}' and Timestamp ge datetime'{2}'" -f $tenant.tenant, $task, (Get-Date).AddMinutes(-10).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss') + $Filter = "PartitionKey eq '{0}' and RowKey eq '{1}' and Timestamp ge datetime'{2}'" -f $tenant.tenant, $task.Name, (Get-Date).AddMinutes(-10).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss') $ExistingMessage = Get-CIPPAzDataTableEntity @Table -Filter $Filter if (!$ExistingMessage) { $QueueItem = [pscustomobject]@{ tenant = $tenant.tenant tenantid = $tenant.tenantid - FunctionName = "CIPPAlert$($Task)" + FunctionName = "CIPPAlert$($Task.Name)" + value = $Task.value } Push-OutputBinding -Name QueueItemOut -Value $QueueItem - $QueueItem | Add-Member -MemberType NoteProperty -Name 'RowKey' -Value $task -Force + $QueueItem | Add-Member -MemberType NoteProperty -Name 'RowKey' -Value $task.Name -Force $QueueItem | Add-Member -MemberType NoteProperty -Name 'PartitionKey' -Value $tenant.tenant -Force Add-CIPPAzDataTableEntity @Table -Entity $QueueItem -Force } else { - Write-Host ('ALERTS: Duplicate run found. Ignoring. Tenant: {0}, Task: {1}' -f $tenant.tenant, $task) + Write-Host ('ALERTS: Duplicate run found. Ignoring. Tenant: {0}, Task: {1}' -f $tenant.tenant, $task.Name) } } From e4032a8005eb3790926ce5b5a45b9b24dfdf297e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 28 Feb 2024 11:24:35 -0500 Subject: [PATCH 05/45] Tweak ExecScheduledCommand --- ExecScheduledCommand/run.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ExecScheduledCommand/run.ps1 b/ExecScheduledCommand/run.ps1 index 180df3368b87..d0031f771c5c 100644 --- a/ExecScheduledCommand/run.ps1 +++ b/ExecScheduledCommand/run.ps1 @@ -19,7 +19,7 @@ try { if ($results -is [String]) { $results = @{ Results = $results } } - if ($results -is [array]) { + if ($results -is [array] -and $results[0] -is [string]) { $results = $results | Where-Object { $_ -is [string] } $results = $results | ForEach-Object { @{ Results = $_ } } } From a825f3ad8c5ac79f68aeeae4d7e68287fb6a0606 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 28 Feb 2024 22:57:59 +0100 Subject: [PATCH 06/45] fixes api --- .../Entrypoints/Push-CIPPAlertSharepointQuota.ps1 | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 index d96dd801aedb..7eb8f351a6ca 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 @@ -12,16 +12,14 @@ function Push-CIPPAlertSharepointQuota { $sharepointToken.Add('accept', 'application/json') $sharepointQuota = (Invoke-RestMethod -Method 'GET' -Headers $sharepointToken -Uri "https://$($tenantName)-admin.sharepoint.com/_api/StorageQuotas()?api-version=1.3.2" -ErrorAction Stop).value if ($sharepointQuota) { + if ($QueueItem.value) { $Value = $QueueItem.value } else { $Value = 90 } $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100) - if ($QueueItem.value -eq $true){ - if ($UsedStoragePercentage -gt 90) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%. Your alert threshold is 90%" - } - } - elseif ($UsedStoragePercentage -gt $QueueItem.value) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%. Your alert threshold is $($QueueItem.value)%" + if ($UsedStoragePercentage -gt $Value) { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%. Your alert threshold is $($Value)%" } } } catch { } -} + + +} \ No newline at end of file From 0f6ebaef80cac288ad48c539fbaa6ebf4b24c45c Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 28 Feb 2024 22:59:28 +0100 Subject: [PATCH 07/45] fixes mailbox quota --- .../CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 index 1d008a4c784d..ecb2aaf7b4ad 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 @@ -12,13 +12,11 @@ function Push-CIPPAlertQuotaUsed { if ($_.StorageUsedInBytes -eq 0) { continue } $PercentLeft = [math]::round($_.StorageUsedInBytes / $_.prohibitSendReceiveQuotaInBytes * 100) if ($QueueItem.value -eq $true) { + if ($QueueItem.value) { $Value = $QueueItem.value } else { $Value = 90 } if ($PercentLeft -gt 90) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.UserPrincipalName): Mailbox is more than $($QueueItem.value)% full. Mailbox is $PercentLeft% full" + Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.UserPrincipalName): Mailbox is more than $($value)% full. Mailbox is $PercentLeft% full" } } - elseif ($PercentLeft -gt $QueueItem.value) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.UserPrincipalName): Mailbox is more than $($QueueItem.value)% full. Mailbox is $PercentLeft% full" - } } } catch { } From be4fc52697665ffc848041e543e542d382555793 Mon Sep 17 00:00:00 2001 From: Roel van der Wegen Date: Sat, 2 Mar 2024 20:24:15 +0100 Subject: [PATCH 08/45] Account for people using ; as delimiter --- Modules/CIPPCore/Public/Send-CIPPAlert.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 b/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 index a253311fae29..55d7e9db9803 100644 --- a/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 +++ b/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 @@ -18,7 +18,7 @@ function Send-CIPPAlert { Write-Host "Trying to send email" try { if ($Config.email -like '*@*') { - $Recipients = $Config.email.split(",").trim() | ForEach-Object { if ($_ -like '*@*') { [pscustomobject]@{EmailAddress = @{Address = $_ } } } } + $Recipients = $Config.email.split($(if ($Config.email -like "*,*") { ',' } else { ';' })).trim() | ForEach-Object { if ($_ -like '*@*') { [pscustomobject]@{EmailAddress = @{Address = $_ } } } } $PowerShellBody = [PSCustomObject]@{ message = @{ subject = $Title From 7118b825d0d67649f982bcec407a2c8450d4998a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 4 Mar 2024 14:25:12 -0500 Subject: [PATCH 09/45] Update Entra Device actions --- .../Public/Entrypoints/Invoke-ExecDeviceDelete.ps1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeviceDelete.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeviceDelete.ps1 index 843764a6d5d4..25e64be478b3 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeviceDelete.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecDeviceDelete.ps1 @@ -14,13 +14,16 @@ Function Invoke-ExecDeviceDelete { # Interact with query parameters or the body of the request. - try { + try { $url = "https://graph.microsoft.com/beta/devices/$($request.query.id)" if ($Request.query.action -eq 'delete') { $ActionResult = New-GraphPOSTRequest -uri $url -type DELETE -tenantid $Request.Query.TenantFilter - } else { + } elseif ($Request.query.action -eq 'disable') { $ActionResult = New-GraphPOSTRequest -uri $url -type PATCH -tenantid $Request.Query.TenantFilter -body '{"accountEnabled": false }' + } elseif ($Request.query.action -eq 'enable') { + $ActionResult = New-GraphPOSTRequest -uri $url -type PATCH -tenantid $Request.Query.TenantFilter -body '{"accountEnabled": true }' } + Write-Host $ActionResult $body = [pscustomobject]@{'Results' = "Executed action $($Request.query.action) on $($Request.query.id)" } } catch { $body = [pscustomobject]@{'Results' = "Failed to queue action $($Request.query.action) on $($request.query.id): $($_.Exception.Message)" } From c7140d873e728bd4d35bedbddce961aee4017cdd Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 4 Mar 2024 14:25:44 -0500 Subject: [PATCH 10/45] Add entrypoints for generic durables --- CIPPActivityFunction/function.json | 11 ++++ CIPPOrchestrator/function.json | 11 ++++ Modules/CippEntrypoints/CippEntrypoints.psm1 | 58 +++++++++++++++++++- 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 CIPPActivityFunction/function.json create mode 100644 CIPPOrchestrator/function.json diff --git a/CIPPActivityFunction/function.json b/CIPPActivityFunction/function.json new file mode 100644 index 000000000000..b4007235d549 --- /dev/null +++ b/CIPPActivityFunction/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1", + "entryPoint": "Receive-CippActivityTrigger", + "bindings": [ + { + "name": "name", + "type": "activityTrigger", + "direction": "in" + } + ] +} diff --git a/CIPPOrchestrator/function.json b/CIPPOrchestrator/function.json new file mode 100644 index 000000000000..011113dbc618 --- /dev/null +++ b/CIPPOrchestrator/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1", + "entryPoint": "Receive-CippOrchestrationTrigger", + "bindings": [ + { + "name": "Context", + "type": "orchestrationTrigger", + "direction": "in" + } + ] +} diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index d6d80cdcf143..119187c412d0 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -19,7 +19,7 @@ function Receive-CippHttpTrigger { function Receive-CippQueueTrigger { Param($QueueItem, $TriggerMetadata) - + $Start = (Get-Date).ToUniversalTime() $APIName = $TriggerMetadata.FunctionName Write-Host "#### Running $APINAME" @@ -48,5 +48,59 @@ function Receive-CippQueueTrigger { Write-CippFunctionStats @Stats } -Export-ModuleMember -Function @('Receive-CippHttpTrigger', 'Receive-CippQueueTrigger') +function Receive-CippOrchestrationTrigger { + param($Context) + + $DurableRetryOptions = @{ + FirstRetryInterval = (New-TimeSpan -Seconds 5) + MaxNumberOfAttempts = 3 + BackoffCoefficient = 2 + } + $RetryOptions = New-DurableRetryOptions @DurableRetryOptions + Write-LogMessage -API $Context.Input.OrchestratorName -tenant $Context.Input.TenantFilter -message "Started $($Context.Input.OrchestratorName)" -sev info + + if (!$Context.Input.Batch -or ($Context.Input.Batch | Measure-Object).Count -eq 0) { + $Batch = (Invoke-ActivityFunction -FunctionName 'CIPPActivityFunction' -Input $Context.Input.QueueFunction) + } else { + $Batch = $Context.Input.Batch + } + + foreach ($Item in $Batch) { + Invoke-DurableActivity -FunctionName 'CIPPActivityFunction' -Input $Item -NoWait -RetryOptions $RetryOptions + } + + Write-LogMessage -API $Context.Input.OrchestratorName -tenant $tenant -message "Finished $($Context.Input.OrchestratorName)" -sev Info +} + +function Receive-CippActivityTrigger { + Param($Item) + + $Start = (Get-Date).ToUniversalTime() + Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName + + if ($Item.FunctionName) { + $FunctionName = 'Push-{0}' -f $Item.FunctionName + try { + & $FunctionName @Item + } catch { + $ErrorMsg = $_.Exception.Message + } + } else { + $ErrorMsg = 'Function not provided' + } + + $End = (Get-Date).ToUniversalTime() + + $Stats = @{ + FunctionType = 'Durable' + Entity = $Item + Start = $Start + End = $End + ErrorMsg = $ErrorMsg + } + Write-Information '####### Adding stats' + Write-CippFunctionStats @Stats +} + +Export-ModuleMember -Function @('Receive-CippHttpTrigger', 'Receive-CippQueueTrigger', 'Receive-CippOrchestrationTrigger', 'Receive-CippActivityTrigger') From 5c87c669c71f068d91f0b85d815d172a99c10d03 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 4 Mar 2024 15:47:40 -0500 Subject: [PATCH 11/45] Standards to durable --- CIPPActivityFunction/function.json | 2 +- .../Public/Entrypoints/Push-CIPPStandard.ps1 | 10 +++--- .../GraphHelper/Write-CippFunctionStats.ps1 | 2 +- .../Public/Invoke-CIPPStandardsRun.ps1 | 32 ++++++++++++------- Modules/CippEntrypoints/CippEntrypoints.psm1 | 1 + Scheduler_Standards/function.json | 5 +++ 6 files changed, 33 insertions(+), 19 deletions(-) diff --git a/CIPPActivityFunction/function.json b/CIPPActivityFunction/function.json index b4007235d549..a9529e73be54 100644 --- a/CIPPActivityFunction/function.json +++ b/CIPPActivityFunction/function.json @@ -3,7 +3,7 @@ "entryPoint": "Receive-CippActivityTrigger", "bindings": [ { - "name": "name", + "name": "Item", "type": "activityTrigger", "direction": "in" } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 index 89a9024a9389..2431afb0c8e6 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 @@ -1,15 +1,15 @@ function Push-CIPPStandard { param ( - $QueueItem, $TriggerMetadata + $Tenant, + $Standard, + $Settings ) - Write-Host "Received queue item for $($QueueItem.Tenant) and standard $($QueueItem.Standard)." - $Tenant = $QueueItem.Tenant - $Standard = $QueueItem.Standard + Write-Host "Received queue item for $Tenant and standard $Standard." $FunctionName = 'Invoke-CIPPStandard{0}' -f $Standard Write-Host "We'll be running $FunctionName" try { - & $FunctionName -Tenant $Tenant -Settings $QueueItem.Settings -ErrorAction Stop + & $FunctionName -Tenant $Tenant -Settings $Settings -ErrorAction Stop } catch { throw $_.Exception.Message } diff --git a/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 index cac4bf0a3173..e4102cd31521 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 @@ -15,7 +15,7 @@ function Write-CippFunctionStats { $RowKey = [string](New-Guid).Guid $TimeSpan = New-TimeSpan -Start $Start -End $End $Duration = [int]$TimeSpan.TotalSeconds - + # Flatten data to json string $Entity.PartitionKey = $FunctionType $Entity.RowKey = $RowKey diff --git a/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 index 353551cdaadf..37d85bba93b4 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 @@ -1,4 +1,4 @@ - + function Invoke-CIPPStandardsRun { [CmdletBinding()] param( @@ -20,7 +20,7 @@ function Invoke-CIPPStandardsRun { $OldStd = $_ $OldStd.standards.psobject.properties.name | ForEach-Object { if ($_ -eq 'MailContacts') { - $OldStd.Standards.$_ = [pscustomobject]@{ + $OldStd.Standards.$_ = [pscustomobject]@{ GeneralContact = $OldStd.Standards.MailContacts.GeneralContact.Mail SecurityContact = $OldStd.Standards.MailContacts.SecurityContact.Mail MarketingContact = $OldStd.Standards.MailContacts.MarketingContact.Mail @@ -28,16 +28,16 @@ function Invoke-CIPPStandardsRun { remediate = $true } } else { - if ($OldStd.Standards.$_ -eq $true -and $_ -ne 'v2.1') { - $OldStd.Standards.$_ = @{ remediate = $true } - } else { - $OldStd.Standards.$_ | Add-Member -NotePropertyName 'remediate' -NotePropertyValue $true -Force + if ($OldStd.Standards.$_ -eq $true -and $_ -ne 'v2.1') { + $OldStd.Standards.$_ = @{ remediate = $true } + } else { + $OldStd.Standards.$_ | Add-Member -NotePropertyName 'remediate' -NotePropertyValue $true -Force } - + } } $OldStd | Add-Member -NotePropertyName 'v2.1' -NotePropertyValue $true -PassThru -Force - $Entity = @{ + $Entity = @{ PartitionKey = 'standards' RowKey = "$($OldStd.Tenant)" JSON = "$($OldStd | ConvertTo-Json -Depth 10)" @@ -76,15 +76,23 @@ function Invoke-CIPPStandardsRun { } } - #For each item in our object, run the queue. + #For each item in our object, run the queue. - foreach ($task in $object | Where-Object -Property Standard -NotLike 'v2*') { - $QueueItem = [pscustomobject]@{ + $Batch = foreach ($task in $object | Where-Object -Property Standard -NotLike 'v2*') { + [PSCustomObject]@{ Tenant = $task.Tenant Standard = $task.Standard Settings = $task.Settings FunctionName = 'CIPPStandard' } - Push-OutputBinding -Name QueueItem -Value $QueueItem } + + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'Standards' + Batch = @($Batch) + } + + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject $InputObject + Write-Host "Started orchestration with ID = '$InstanceId'" + $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId } \ No newline at end of file diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 119187c412d0..3d9d1f631186 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -98,6 +98,7 @@ function Receive-CippActivityTrigger { End = $End ErrorMsg = $ErrorMsg } + Write-Information '####### Adding stats' Write-CippFunctionStats @Stats } diff --git a/Scheduler_Standards/function.json b/Scheduler_Standards/function.json index 35ec29f027f7..81d53b9a1598 100644 --- a/Scheduler_Standards/function.json +++ b/Scheduler_Standards/function.json @@ -11,6 +11,11 @@ "direction": "out", "name": "QueueItem", "queueName": "CIPPGenericQueue" + }, + { + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } From 575ae066f56ae5364a0d82eafc9656b749c8817b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 4 Mar 2024 18:54:56 -0500 Subject: [PATCH 12/45] tweak orchestrator allow for json input for larger depth --- .../Public/Entrypoints/Push-CIPPStandard.ps1 | 10 +++++----- .../Public/Invoke-CIPPStandardsRun.ps1 | 2 +- Modules/CippEntrypoints/CippEntrypoints.psm1 | 18 ++++++++++++------ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 index 2431afb0c8e6..dd9849da8514 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPStandard.ps1 @@ -1,15 +1,15 @@ function Push-CIPPStandard { param ( - $Tenant, - $Standard, - $Settings + $Item ) - Write-Host "Received queue item for $Tenant and standard $Standard." + Write-Host "Received queue item for $($Item.Tenant) and standard $($Item.Standard)." + $Tenant = $Item.Tenant + $Standard = $Item.Standard $FunctionName = 'Invoke-CIPPStandard{0}' -f $Standard Write-Host "We'll be running $FunctionName" try { - & $FunctionName -Tenant $Tenant -Settings $Settings -ErrorAction Stop + & $FunctionName -Tenant $Item.Tenant -Settings $Item.Settings -ErrorAction Stop } catch { throw $_.Exception.Message } diff --git a/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 index 37d85bba93b4..dd4acff806e8 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 @@ -92,7 +92,7 @@ function Invoke-CIPPStandardsRun { Batch = @($Batch) } - $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject $InputObject + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) Write-Host "Started orchestration with ID = '$InstanceId'" $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId } \ No newline at end of file diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 3d9d1f631186..a651d072339f 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -56,20 +56,26 @@ function Receive-CippOrchestrationTrigger { MaxNumberOfAttempts = 3 BackoffCoefficient = 2 } + if (Test-Json -Json $Context.Input) { + $OrchestratorInput = $Context.Input | ConvertFrom-Json + } else { + $OrchestratorInput = $Context.Input + } + Write-Host ($Context | ConvertTo-Json -Depth 10) $RetryOptions = New-DurableRetryOptions @DurableRetryOptions - Write-LogMessage -API $Context.Input.OrchestratorName -tenant $Context.Input.TenantFilter -message "Started $($Context.Input.OrchestratorName)" -sev info + Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $OrchestratorInput.TenantFilter -message "Started $($OrchestratorInput.OrchestratorName)" -sev info - if (!$Context.Input.Batch -or ($Context.Input.Batch | Measure-Object).Count -eq 0) { - $Batch = (Invoke-ActivityFunction -FunctionName 'CIPPActivityFunction' -Input $Context.Input.QueueFunction) + if (!$OrchestratorInput.Batch -or ($OrchestratorInput.Batch | Measure-Object).Count -eq 0) { + $Batch = (Invoke-ActivityFunction -FunctionName 'CIPPActivityFunction' -Input $OrchestratorInput.QueueFunction) } else { - $Batch = $Context.Input.Batch + $Batch = $OrchestratorInput.Batch } foreach ($Item in $Batch) { Invoke-DurableActivity -FunctionName 'CIPPActivityFunction' -Input $Item -NoWait -RetryOptions $RetryOptions } - Write-LogMessage -API $Context.Input.OrchestratorName -tenant $tenant -message "Finished $($Context.Input.OrchestratorName)" -sev Info + Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $tenant -message "Finished $($OrchestratorInput.OrchestratorName)" -sev Info } function Receive-CippActivityTrigger { @@ -81,7 +87,7 @@ function Receive-CippActivityTrigger { if ($Item.FunctionName) { $FunctionName = 'Push-{0}' -f $Item.FunctionName try { - & $FunctionName @Item + & $FunctionName -Item $Item } catch { $ErrorMsg = $_.Exception.Message } From d4d09f9cbffdb611d8fb2d5ba890a4b2220653ee Mon Sep 17 00:00:00 2001 From: Mo Date: Thu, 7 Mar 2024 11:47:05 +0000 Subject: [PATCH 13/45] Update adding Connectors with the correct text --- .../CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 index 3686d66c124d..b3246cea773d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddExConnector.ps1 @@ -18,12 +18,12 @@ Function Invoke-AddExConnector { $Result = foreach ($Tenantfilter in $tenants) { try { $GraphRequest = New-ExoRequest -tenantid $Tenantfilter -cmdlet "New-$($ConnectorType)connector" -cmdParams $RequestParams - "Successfully created transport rule for $Tenantfilter." - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Tenantfilter -message "Created transport rule for $($Tenantfilter)" -sev 'Info' + "Successfully created Connector for $Tenantfilter." + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Tenantfilter -message "Created Connector for $($Tenantfilter)" -sev 'Info' } catch { - "Could not create created transport rule for $($Tenantfilter): $($_.Exception.message)" - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Tenantfilter -message "Could not create created transport rule for $($Tenantfilter): $($_.Exception.message)" -sev 'Error' + "Could not create created Connector for $($Tenantfilter): $($_.Exception.message)" + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Tenantfilter -message "Could not create created Connector for $($Tenantfilter): $($_.Exception.message)" -sev 'Error' } } From 24b0eaab6457c65e35411567ae09f0bbf19a8a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 9 Mar 2024 14:57:31 +0100 Subject: [PATCH 14/45] Handle rare error case and change to use v1.0 endpoint --- .../Invoke-CIPPStandardPasswordExpireDisabled.ps1 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 index e9182bb95717..47cdc60712c8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 @@ -4,7 +4,7 @@ function Invoke-CIPPStandardPasswordExpireDisabled { Internal #> param($Tenant, $Settings) - $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $Tenant + $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant $DomainswithoutPassExpire = $GraphRequest | Where-Object -Property passwordValidityPeriodInDays -NE '2147483647' If ($Settings.remediate) { @@ -12,7 +12,13 @@ function Invoke-CIPPStandardPasswordExpireDisabled { if ($DomainswithoutPassExpire) { $DomainswithoutPassExpire | ForEach-Object { try { - New-GraphPostRequest -type Patch -tenantid $Tenant -uri "https://graph.microsoft.com/beta/domains/$($_.id)" -body '{"passwordValidityPeriodInDays": 2147483647 }' + if ( $null -eq $_.passwordNotificationWindowInDays ) { + $Body = '{"passwordValidityPeriodInDays": 2147483647, "passwordNotificationWindowInDays": 14 }' + Write-Host "PasswordNotificationWindowInDays is null for $($_.id). Setting to the default of 14 days." + } else { + $Body = '{"passwordValidityPeriodInDays": 2147483647 }' + } + New-GraphPostRequest -type Patch -tenantid $Tenant -uri "https://graph.microsoft.com/v1.0/domains/$($_.id)" -body $Body Write-LogMessage -API 'Standards' -tenant $tenant -message "Disabled Password Expiration for $($_.id)." -sev Info } catch { Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to disable Password Expiration for $($_.id). Error: $($_.exception.message)" -sev Error From 7c1c526ab9a2ee2403c3e64eea5d4e2d8689092b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sat, 9 Mar 2024 11:37:38 -0800 Subject: [PATCH 15/45] Alerts Durable --- CIPPActivityFunction/function.json | 5 ++ .../Push-CIPPAlertAdminPassword.ps1 | 11 ++-- .../Push-CIPPAlertApnCertExpiry.ps1 | 11 ++-- .../Push-CIPPAlertAppSecretExpiry.ps1 | 17 +++--- .../Push-CIPPAlertDefenderMalware.ps1 | 9 ++- .../Push-CIPPAlertDefenderStatus.ps1 | 10 ++-- .../Push-CIPPAlertDepTokenExpiry.ps1 | 11 ++-- .../Push-CIPPAlertExpiringLicenses.ps1 | 7 +-- .../Entrypoints/Push-CIPPAlertMFAAdmins.ps1 | 15 +++-- .../Push-CIPPAlertMFAAlertUsers.ps1 | 11 ++-- .../Entrypoints/Push-CIPPAlertNewRole.ps1 | 13 ++-- .../Entrypoints/Push-CIPPAlertNoCAConfig.ps1 | 11 ++-- .../Push-CIPPAlertOverusedLicenses.ps1 | 9 ++- .../Entrypoints/Push-CIPPAlertQuotaUsed.ps1 | 13 ++-- .../Push-CIPPAlertSecDefaultsUpsell.ps1 | 11 ++-- .../Push-CIPPAlertSharepointQuota.ps1 | 11 ++-- .../Push-CIPPAlertUnusedLicenses.ps1 | 9 ++- .../Push-CIPPAlertVppTokenExpiry.ps1 | 13 ++-- .../Entrypoints/Push-SchedulerAlert.ps1 | 51 ++++++++++------ .../GraphHelper/Write-CippFunctionStats.ps1 | 19 +++--- .../Public/Invoke-CIPPStandardsRun.ps1 | 2 +- .../Standards/Invoke-CIPPStandardAuditLog.ps1 | 10 ++-- Modules/CippEntrypoints/CippEntrypoints.psm1 | 59 +++++++++++-------- Scheduler_GetQueue/function.json | 5 ++ Scheduler_GetQueue/run.ps1 | 25 ++++---- 25 files changed, 198 insertions(+), 170 deletions(-) diff --git a/CIPPActivityFunction/function.json b/CIPPActivityFunction/function.json index a9529e73be54..e8a29dde00c3 100644 --- a/CIPPActivityFunction/function.json +++ b/CIPPActivityFunction/function.json @@ -6,6 +6,11 @@ "name": "Item", "type": "activityTrigger", "direction": "in" + }, + { + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 index af03360c1973..63f371b10aa6 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAdminPassword.ps1 @@ -3,17 +3,16 @@ function Push-CIPPAlertAdminPassword { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - [pscustomobject]$QueueItem, - $TriggerMetadata + [pscustomobject]$Item ) try { - New-GraphGETRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'&`$expand=principal" -tenantid $($QueueItem.tenant) | Where-Object { ($_.principalOrganizationId -EQ $QueueItem.tenantid) -and ($_.principal.'@odata.type' -eq '#microsoft.graph.user') } | ForEach-Object { - $LastChanges = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.principalId)?`$select=UserPrincipalName,lastPasswordChangeDateTime" -tenant $($QueueItem.tenant) + New-GraphGETRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'&`$expand=principal" -tenantid $($Item.tenant) | Where-Object { ($_.principalOrganizationId -EQ $Item.tenantid) -and ($_.principal.'@odata.type' -eq '#microsoft.graph.user') } | ForEach-Object { + $LastChanges = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.principalId)?`$select=UserPrincipalName,lastPasswordChangeDateTime" -tenant $($Item.tenant) if ($LastChanges.LastPasswordChangeDateTime -gt (Get-Date).AddDays(-1)) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Admin password has been changed for $($LastChanges.UserPrincipalName) in last 24 hours" + Write-AlertMessage -tenant $($Item.tenant) -message "Admin password has been changed for $($LastChanges.UserPrincipalName) in last 24 hours" } } } catch { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get admin password changes for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + Write-AlertMessage -tenant $($Item.tenant) -message "Could not get admin password changes for $($Item.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 index 07571db760aa..9ef318273f8d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 @@ -2,26 +2,25 @@ function Push-CIPPAlertApnCertExpiry { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) $LastRunTable = Get-CIPPTable -Table AlertLastRun try { - $Filter = "RowKey eq 'ApnCertExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $Filter = "RowKey eq 'ApnCertExpiry' and PartitionKey eq '{0}'" -f $Item.tenantid $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter $Yesterday = (Get-Date).AddDays(-1) if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { try { - $Apn = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/applePushNotificationCertificate' -tenantid $QueueItem.tenant + $Apn = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/applePushNotificationCertificate' -tenantid $Item.tenant if ($Apn.expirationDateTime -lt (Get-Date).AddDays(30) -and $Apn.expirationDateTime -gt (Get-Date).AddDays(-7)) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Intune: Apple Push Notification certificate for {0} is expiring on {1}' -f $Apn.appleIdentifier, $Apn.expirationDateTime) + Write-AlertMessage -tenant $($Item.tenant) -message ('Intune: Apple Push Notification certificate for {0} is expiring on {1}' -f $Apn.appleIdentifier, $Apn.expirationDateTime) } } catch {} } $LastRun = @{ RowKey = 'ApnCertExpiry' - PartitionKey = $QueueItem.tenantid + PartitionKey = $Item.tenantid } Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 index 82bcde9bb74f..06477e708ff7 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 @@ -2,26 +2,25 @@ function Push-CIPPAlertAppSecretExpiry { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) $LastRunTable = Get-CIPPTable -Table AlertLastRun - + try { - $Filter = "RowKey eq 'AppSecretExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $Filter = "RowKey eq 'AppSecretExpiry' and PartitionKey eq '{0}'" -f $Item.tenantid $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter $Yesterday = (Get-Date).AddDays(-1) if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { - Write-Host "Checking app expire for $($QueueItem.tenant)" - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/applications?`$select=appId,displayName,passwordCredentials" -tenantid $QueueItem.tenant | ForEach-Object { + Write-Host "Checking app expire for $($Item.tenant)" + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/applications?`$select=appId,displayName,passwordCredentials" -tenantid $Item.tenant | ForEach-Object { foreach ($App in $_) { Write-Host "checking $($App.displayName)" if ($App.passwordCredentials) { foreach ($Credential in $App.passwordCredentials) { if ($Credential.endDateTime -lt (Get-Date).AddDays(30) -and $Credential.endDateTime -gt (Get-Date).AddDays(-7)) { Write-Host ("Application '{0}' has secrets expiring on {1}" -f $App.displayName, $Credential.endDateTime) - Write-AlertMessage -tenant $($QueueItem.tenant) -message ("Application '{0}' has secrets expiring on {1}" -f $App.displayName, $Credential.endDateTime) + Write-AlertMessage -tenant $($Item.tenant) -message ("Application '{0}' has secrets expiring on {1}" -f $App.displayName, $Credential.endDateTime) } } } @@ -29,12 +28,12 @@ function Push-CIPPAlertAppSecretExpiry { } $LastRun = @{ RowKey = 'AppSecretExpiry' - PartitionKey = $QueueItem.tenantid + PartitionKey = $Item.tenantid } Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } } catch { - + } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 index b69ed1b50ab2..0d4d1c7b02ab 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderMalware.ps1 @@ -3,14 +3,13 @@ function Push-CIPPAlertDefenderMalware { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsDeviceMalwareStates?`$top=999&`$filter=tenantId eq '$($QueueItem.tenantid)'" | Where-Object { $_.malwareThreatState -eq 'Active' } | ForEach-Object { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.managedDeviceName): Malware found and active. Severity: $($_.MalwareSeverity). Malware name: $($_.MalwareDisplayName)" + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsDeviceMalwareStates?`$top=999&`$filter=tenantId eq '$($Item.tenantid)'" | Where-Object { $_.malwareThreatState -eq 'Active' } | ForEach-Object { + Write-AlertMessage -tenant $($Item.tenant) -message "$($_.managedDeviceName): Malware found and active. Severity: $($_.MalwareSeverity). Malware name: $($_.MalwareDisplayName)" } } catch { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get malware data for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + Write-AlertMessage -tenant $($Item.tenant) -message "Could not get malware data for $($Item.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderStatus.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderStatus.ps1 index e9d4e06adae0..7b42affa4e00 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderStatus.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDefenderStatus.ps1 @@ -1,16 +1,14 @@ - function Push-CIPPAlertDefenderStatus { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsProtectionStates?`$top=999&`$filter=tenantId eq '$($QueueItem.tenantid)'" | Where-Object { $_.realTimeProtectionEnabled -eq $false -or $_.MalwareprotectionEnabled -eq $false } | ForEach-Object { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.managedDeviceName) - Real Time Protection: $($_.realTimeProtectionEnabled) & Malware Protection: $($_.MalwareprotectionEnabled)" + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/windowsProtectionStates?`$top=999&`$filter=tenantId eq '$($Item.tenantid)'" | Where-Object { $_.realTimeProtectionEnabled -eq $false -or $_.MalwareprotectionEnabled -eq $false } | ForEach-Object { + Write-AlertMessage -tenant $($Item.tenant) -message "$($_.managedDeviceName) - Real Time Protection: $($_.realTimeProtectionEnabled) & Malware Protection: $($_.MalwareprotectionEnabled)" } } catch { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get defender status for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + Write-AlertMessage -tenant $($Item.tenant) -message "Could not get defender status for $($Item.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 index 804750e60705..4246a5364ef3 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 @@ -2,27 +2,26 @@ function Push-CIPPAlertDepTokenExpiry { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) $LastRunTable = Get-CIPPTable -Table AlertLastRun try { - $Filter = "RowKey eq 'DepTokenExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $Filter = "RowKey eq 'DepTokenExpiry' and PartitionKey eq '{0}'" -f $Item.tenantid $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter $Yesterday = (Get-Date).AddDays(-1) if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { try { - $DepTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings' -tenantid $QueueItem.tenant).value + $DepTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings' -tenantid $Item.tenant).value foreach ($Dep in $DepTokens) { if ($Dep.tokenExpirationDateTime -lt (Get-Date).AddDays(30) -and $Dep.tokenExpirationDateTime -gt (Get-Date).AddDays(-7)) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Apple Device Enrollment Program token expiring on {0}' -f $Dep.tokenExpirationDateTime) + Write-AlertMessage -tenant $($Item.tenant) -message ('Apple Device Enrollment Program token expiring on {0}' -f $Dep.tokenExpirationDateTime) } } } catch {} $LastRun = @{ RowKey = 'DepTokenExpiry' - PartitionKey = $QueueItem.tenantid + PartitionKey = $Item.tenantid } Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 index 99a861bfb5d7..6e2a704b9ba3 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertExpiringLicenses.ps1 @@ -2,15 +2,14 @@ function Push-CIPPAlertExpiringLicenses { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { - Get-CIPPLicenseOverview -TenantFilter $QueueItem.tenant | ForEach-Object { + Get-CIPPLicenseOverview -TenantFilter $Item.tenant | ForEach-Object { $timeTorenew = [int64]$_.TimeUntilRenew if ($timeTorenew -lt 30 -and $_.TimeUntilRenew -gt 0) { Write-Host "$($_.License) will expire in $($_.TimeUntilRenew) days. The estimated term is $($_.EstTerm)" - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.License) will expire in $($_.TimeUntilRenew) days. The estimated term is $($_.EstTerm)" + Write-AlertMessage -tenant $($Item.tenant) -message "$($_.License) will expire in $($_.TimeUntilRenew) days. The estimated term is $($_.EstTerm)" } } } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 index b0c8056e1f03..66685982d956 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 @@ -2,26 +2,25 @@ function Push-CIPPAlertMFAAdmins { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { - $CAPolicies = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies?$top=999' -tenantid $QueueItem.tenant -ErrorAction Stop) + $CAPolicies = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies?$top=999' -tenantid $Item.tenant -ErrorAction Stop) foreach ($Policy in $CAPolicies) { if ($policy.grantControls.customAuthenticationFactors -eq 'RequireDuoMfa') { $DuoActive = $true } } if (!$DuoActive) { - $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$top=999&$filter=IsAdmin eq true' -tenantid $($QueueItem.tenant) | Where-Object -Property 'isMfaRegistered' -EQ $false + $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$top=999&$filter=IsAdmin eq true' -tenantid $($Item.tenant) | Where-Object -Property 'isMfaRegistered' -EQ $false if ($users) { - Write-AlertMessage -tenant $QueueItem.tenant -message "The following admins do not have MFA registered: $($users.UserPrincipalName -join ', ')" + Write-AlertMessage -tenant $Item.tenant -message "The following admins do not have MFA registered: $($users.UserPrincipalName -join ', ')" } } else { - Write-LogMessage -message 'Potentially using Duo for MFA, could not check MFA status for Admins with 100% accuracy' -API 'MFA Alerts - Informational' -tenant $QueueItem.tenant -sev Info - } + Write-LogMessage -message 'Potentially using Duo for MFA, could not check MFA status for Admins with 100% accuracy' -API 'MFA Alerts - Informational' -tenant $Item.tenant -sev Info + } } catch { - Write-LogMessage -message "Failed to check MFA status for Admins: $($_.exception.message)" -API 'MFA Alerts - Informational' -tenant $QueueItem.tenant -sev Error + Write-LogMessage -message "Failed to check MFA status for Admins: $($_.exception.message)" -API 'MFA Alerts - Informational' -tenant $Item.tenant -sev Error } } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 index 3537616d02ed..a02d2afcdc34 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 @@ -2,18 +2,17 @@ function Push-CIPPAlertMFAAlertUsers { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { - $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$filter=isMfaRegistered eq false and userType eq ''member''&$select=userPrincipalName,lastUpdatedDateTime,isMfaRegistered' -tenantid $($QueueItem.tenant) + $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$filter=isMfaRegistered eq false and userType eq ''member''&$select=userPrincipalName,lastUpdatedDateTime,isMfaRegistered' -tenantid $($Item.tenant) if ($users) { - Write-AlertMessage -tenant $QueueItem.tenant -message "The following $($users.Count) users do not have MFA registered: $($users.UserPrincipalName -join ', ')" + Write-AlertMessage -tenant $Item.tenant -message "The following $($users.Count) users do not have MFA registered: $($users.UserPrincipalName -join ', ')" } - + } catch { - Write-LogMessage -message "Failed to check MFA status for all users: $($_.exception.message)" -API 'MFA Alerts - Informational' -tenant $QueueItem.tenant -sev Info + Write-LogMessage -message "Failed to check MFA status for all users: $($_.exception.message)" -API 'MFA Alerts - Informational' -tenant $Item.tenant -sev Info } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 index 504bb3ea3153..8ef2727ebb66 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNewRole.ps1 @@ -2,14 +2,13 @@ function Push-CIPPAlertNewRole { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) $Deltatable = Get-CIPPTable -Table DeltaCompare try { - $Filter = "PartitionKey eq 'AdminDelta' and RowKey eq '{0}'" -f $QueueItem.tenantid + $Filter = "PartitionKey eq 'AdminDelta' and RowKey eq '{0}'" -f $Item.tenantid $AdminDelta = (Get-CIPPAzDataTableEntity @Deltatable -Filter $Filter).delta | ConvertFrom-Json -ErrorAction SilentlyContinue - $NewDelta = (New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/directoryRoles?`$expand=members" -tenantid $QueueItem.tenant) | Select-Object displayname, Members | ForEach-Object { + $NewDelta = (New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/directoryRoles?`$expand=members" -tenantid $Item.tenant) | Select-Object displayname, Members | ForEach-Object { @{ GroupName = $_.displayname Members = $_.Members.UserPrincipalName @@ -18,7 +17,7 @@ function Push-CIPPAlertNewRole { $NewDeltatoSave = $NewDelta | ConvertTo-Json -Depth 10 -Compress -ErrorAction SilentlyContinue | Out-String $DeltaEntity = @{ PartitionKey = 'AdminDelta' - RowKey = [string]$QueueItem.tenantid + RowKey = [string]$Item.tenantid delta = "$NewDeltatoSave" } Add-CIPPAzDataTableEntity @DeltaTable -Entity $DeltaEntity -Force @@ -27,11 +26,11 @@ function Push-CIPPAlertNewRole { foreach ($Group in $NewDelta) { $OldDelta = $AdminDelta | Where-Object { $_.GroupName -eq $Group.GroupName } $Group.members | Where-Object { $_ -notin $OldDelta.members } | ForEach-Object { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$_ has been added to the $($Group.GroupName) Role" + Write-AlertMessage -tenant $($Item.tenant) -message "$_ has been added to the $($Group.GroupName) Role" } } } } catch { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Could not get get role changes for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + Write-AlertMessage -tenant $($Item.tenant) -message "Could not get get role changes for $($Item.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 index 17b5363a1c54..c9b8309963ca 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertNoCAConfig.ps1 @@ -2,20 +2,19 @@ function Push-CIPPAlertNoCAConfig { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { - $CAAvailable = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $QueueItem.Tenant -erroraction stop).serviceplans + $CAAvailable = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $Item.Tenant -erroraction stop).serviceplans if ('AAD_PREMIUM' -in $CAAvailable.servicePlanName) { - $CAPolicies = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies' -tenantid $QueueItem.Tenant) + $CAPolicies = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies' -tenantid $Item.Tenant) if (!$CAPolicies.id) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message 'Conditional Access is available, but no policies could be found.' + Write-AlertMessage -tenant $($Item.tenant) -message 'Conditional Access is available, but no policies could be found.' } } } catch { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Conditional Access Config Alert: Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + Write-AlertMessage -tenant $($Item.tenant) -message "Conditional Access Config Alert: Error occurred: $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 index af90000fa4d0..d314a064a88a 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertOverusedLicenses.ps1 @@ -2,26 +2,25 @@ function Push-CIPPAlertOverusedLicenses { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { $LicenseTable = Get-CIPPTable -TableName ExcludedLicenses $ExcludedSkuList = Get-CIPPAzDataTableEntity @LicenseTable - New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $QueueItem.tenant | ForEach-Object { + New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $Item.tenant | ForEach-Object { $skuid = $_ foreach ($sku in $skuid) { if ($sku.skuId -in $ExcludedSkuList.GUID) { continue } $PrettyName = ($ConvertTable | Where-Object { $_.GUID -eq $sku.skuid }).'Product_Display_Name' | Select-Object -Last 1 if (!$PrettyName) { $PrettyName = $sku.skuPartNumber } if ($sku.prepaidUnits.enabled - $sku.consumedUnits -lt 0) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$PrettyName has Overused licenses. Using $($_.consumedUnits) of $($_.prepaidUnits.enabled)." + Write-AlertMessage -tenant $($Item.tenant) -message "$PrettyName has Overused licenses. Using $($_.consumedUnits) of $($_.prepaidUnits.enabled)." } } } } catch { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Overused Licenses Alert Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + Write-AlertMessage -tenant $($Item.tenant) -message "Overused Licenses Alert Error occurred: $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 index ecb2aaf7b4ad..49ae4105071c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertQuotaUsed.ps1 @@ -2,19 +2,18 @@ function Push-CIPPAlertQuotaUsed { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getMailboxUsageDetail(period='D7')?`$format=application/json" -tenantid $QueueItem.tenant | ForEach-Object { + New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getMailboxUsageDetail(period='D7')?`$format=application/json" -tenantid $Item.tenant | ForEach-Object { if ($_.StorageUsedInBytes -eq 0) { continue } $PercentLeft = [math]::round($_.StorageUsedInBytes / $_.prohibitSendReceiveQuotaInBytes * 100) - if ($QueueItem.value -eq $true) { - if ($QueueItem.value) { $Value = $QueueItem.value } else { $Value = 90 } - if ($PercentLeft -gt 90) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$($_.UserPrincipalName): Mailbox is more than $($value)% full. Mailbox is $PercentLeft% full" + if ($Item.value -eq $true) { + if ($Item.value) { $Value = $Item.value } else { $Value = 90 } + if ($PercentLeft -gt 90) { + Write-AlertMessage -tenant $($Item.tenant) -message "$($_.UserPrincipalName): Mailbox is more than $($value)% full. Mailbox is $PercentLeft% full" } } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 index 1380b73b4233..b89347a828a8 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSecDefaultsUpsell.ps1 @@ -2,26 +2,25 @@ function Push-CIPPAlertSecDefaultsUpsell { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) $LastRunTable = Get-CIPPTable -Table AlertLastRun try { - $Filter = "RowKey eq 'SecDefaultsUpsell' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $Filter = "RowKey eq 'SecDefaultsUpsell' and PartitionKey eq '{0}'" -f $Item.tenantid $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter $Yesterday = (Get-Date).AddDays(-1) if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { try { - $SecDefaults = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $QueueItem.tenant) + $SecDefaults = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $Item.tenant) if ($SecDefaults.isEnabled -eq $false -and $SecDefaults.securityDefaultsUpsell.action -in @('autoEnable', 'autoEnabledNotify')) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Security Defaults will be automatically enabled on {0}' -f $SecDefaults.securityDefaultsUpsell.dueDateTime) + Write-AlertMessage -tenant $($Item.tenant) -message ('Security Defaults will be automatically enabled on {0}' -f $SecDefaults.securityDefaultsUpsell.dueDateTime) } } catch {} $LastRun = @{ RowKey = 'SecDefaultsUpsell' - PartitionKey = $QueueItem.tenantid + PartitionKey = $Item.tenantid } Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 index 7eb8f351a6ca..9614e59d340c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 @@ -3,19 +3,18 @@ function Push-CIPPAlertSharepointQuota { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) Try { - $tenantName = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $QueueItem.Tenant | Where-Object { $_.isInitial -eq $true }).id.Split('.')[0] - $sharepointToken = (Get-GraphToken -scope "https://$($tenantName)-admin.sharepoint.com/.default" -tenantid $QueueItem.Tenant) + $tenantName = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains' -tenantid $Item.Tenant | Where-Object { $_.isInitial -eq $true }).id.Split('.')[0] + $sharepointToken = (Get-GraphToken -scope "https://$($tenantName)-admin.sharepoint.com/.default" -tenantid $Item.Tenant) $sharepointToken.Add('accept', 'application/json') $sharepointQuota = (Invoke-RestMethod -Method 'GET' -Headers $sharepointToken -Uri "https://$($tenantName)-admin.sharepoint.com/_api/StorageQuotas()?api-version=1.3.2" -ErrorAction Stop).value if ($sharepointQuota) { - if ($QueueItem.value) { $Value = $QueueItem.value } else { $Value = 90 } + if ($Item.value) { $Value = $Item.value } else { $Value = 90 } $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100) if ($UsedStoragePercentage -gt $Value) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%. Your alert threshold is $($Value)%" + Write-AlertMessage -tenant $($Item.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%. Your alert threshold is $($Value)%" } } } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 index 2bd58f8b6178..74be1a6e4030 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertUnusedLicenses.ps1 @@ -2,26 +2,25 @@ function Push-CIPPAlertUnusedLicenses { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) try { $LicenseTable = Get-CIPPTable -TableName ExcludedLicenses $ExcludedSkuList = Get-CIPPAzDataTableEntity @LicenseTable - New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $QueueItem.tenant | ForEach-Object { + New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $Item.tenant | ForEach-Object { $skuid = $_ foreach ($sku in $skuid) { if ($sku.skuId -in $ExcludedSkuList.GUID) { continue } $PrettyName = ($ConvertTable | Where-Object { $_.GUID -eq $sku.skuid }).'Product_Display_Name' | Select-Object -Last 1 if (!$PrettyName) { $PrettyName = $sku.skuPartNumber } if ($sku.prepaidUnits.enabled - $sku.consumedUnits -gt 0) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "$PrettyName has unused licenses. Using $($_.consumedUnits) of $($_.prepaidUnits.enabled)." + Write-AlertMessage -tenant $($Item.tenant) -message "$PrettyName has unused licenses. Using $($_.consumedUnits) of $($_.prepaidUnits.enabled)." } } } } catch { - Write-AlertMessage -tenant $($QueueItem.tenant) -message "Unused Licenses Alert Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + Write-AlertMessage -tenant $($Item.tenant) -message "Unused Licenses Alert Error occurred: $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 index d18dd28dd11d..d9a2e70d6531 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertVppTokenExpiry.ps1 @@ -2,31 +2,30 @@ function Push-CIPPAlertVppTokenExpiry { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] - $QueueItem, - $TriggerMetadata + $Item ) $LastRunTable = Get-CIPPTable -Table AlertLastRun try { - $Filter = "RowKey eq 'VppTokenExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid + $Filter = "RowKey eq 'VppTokenExpiry' and PartitionKey eq '{0}'" -f $Item.tenantid $LastRun = Get-CIPPAzDataTableEntity @LastRunTable -Filter $Filter $Yesterday = (Get-Date).AddDays(-1) if (-not $LastRun.Timestamp.DateTime -or ($LastRun.Timestamp.DateTime -le $Yesterday)) { try { - $VppTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceAppManagement/vppTokens' -tenantid $QueueItem.tenant).value + $VppTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceAppManagement/vppTokens' -tenantid $Item.tenant).value foreach ($Vpp in $VppTokens) { if ($Vpp.state -ne 'valid') { - Write-AlertMessage -tenant $($QueueItem.tenant) -message 'Apple Volume Purchase Program Token is not valid, new token required' + Write-AlertMessage -tenant $($Item.tenant) -message 'Apple Volume Purchase Program Token is not valid, new token required' } if ($Vpp.expirationDateTime -lt (Get-Date).AddDays(30) -and $Vpp.expirationDateTime -gt (Get-Date).AddDays(-7)) { - Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Apple Volume Purchase Program token expiring on {0}' -f $Vpp.expirationDateTime) + Write-AlertMessage -tenant $($Item.tenant) -message ('Apple Volume Purchase Program token expiring on {0}' -f $Vpp.expirationDateTime) } } } catch {} $LastRun = @{ RowKey = 'VppTokenExpiry' - PartitionKey = $QueueItem.tenantid + PartitionKey = $Item.tenantid } Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 index 30a4b2583cde..48b5ac8f474b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 @@ -1,42 +1,59 @@ function Push-SchedulerAlert { param ( - $QueueItem, $TriggerMetadata + $Item ) - $Tenant = $QueueItem + try { $Table = Get-CIPPTable -Table SchedulerConfig - if ($Tenant.tag -eq 'AllTenants') { + if ($Item.Tag -eq 'AllTenants') { $Filter = "RowKey eq 'AllTenants' and PartitionKey eq 'Alert'" } else { - $Filter = "RowKey eq '{0}' and PartitionKey eq 'Alert'" -f $Tenant.tenantid + $Filter = "RowKey eq '{0}' and PartitionKey eq 'Alert'" -f $Item.Tenantid } $Alerts = Get-CIPPAzDataTableEntity @Table -Filter $Filter - $IgnoreList = @('Etag', 'PartitionKey', 'Timestamp', 'RowKey', 'tenantid', 'tenant', 'type') - $alertList = $Alerts | Select-Object * -ExcludeProperty $IgnoreList - foreach ($task in ($AlertList.psobject.members | Where-Object { $_.MemberType -EQ 'NoteProperty' -and $_.value -ne $false })) { + $AlertList = $Alerts | Select-Object * -ExcludeProperty $IgnoreList + $Batch = foreach ($task in ($AlertList.psobject.members | Where-Object { $_.MemberType -EQ 'NoteProperty' -and $_.value -ne $false })) { $Table = Get-CIPPTable -TableName AlertRunCheck - $Filter = "PartitionKey eq '{0}' and RowKey eq '{1}' and Timestamp ge datetime'{2}'" -f $tenant.tenant, $task.Name, (Get-Date).AddMinutes(-10).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss') + $Filter = "PartitionKey eq '{0}' and RowKey eq '{1}' and Timestamp ge datetime'{2}'" -f $Item.Tenant, $task.Name, (Get-Date).AddMinutes(-10).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss') $ExistingMessage = Get-CIPPAzDataTableEntity @Table -Filter $Filter if (!$ExistingMessage) { - $QueueItem = [pscustomobject]@{ - tenant = $tenant.tenant - tenantid = $tenant.tenantid + [pscustomobject]@{ + Tenant = $Item.Tenant + Tenantid = $Item.Tenantid FunctionName = "CIPPAlert$($Task.Name)" value = $Task.value } - Push-OutputBinding -Name QueueItemOut -Value $QueueItem - $QueueItem | Add-Member -MemberType NoteProperty -Name 'RowKey' -Value $task.Name -Force - $QueueItem | Add-Member -MemberType NoteProperty -Name 'PartitionKey' -Value $tenant.tenant -Force - Add-CIPPAzDataTableEntity @Table -Entity $QueueItem -Force + #Push-OutputBinding -Name QueueItemOut -Value $Item + $Item | Add-Member -MemberType NoteProperty -Name 'RowKey' -Value $task.Name -Force + $Item | Add-Member -MemberType NoteProperty -Name 'PartitionKey' -Value $Item.Tenant -Force + + try { + $null = Add-CIPPAzDataTableEntity @Table -Entity $Item -Force -ErrorAction Stop + } catch { + Write-Host "################### Error updating alert $($_.Exception.Message) - $($Item | ConvertTo-Json)" + } } else { - Write-Host ('ALERTS: Duplicate run found. Ignoring. Tenant: {0}, Task: {1}' -f $tenant.tenant, $task.Name) + Write-Host ('ALERTS: Duplicate run found. Ignoring. Tenant: {0}, Task: {1}' -f $Item.tenant, $task.Name) } } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'Alerts' + SkipLog = $true + Batch = @($Batch) + } + #Write-Host ($Batch | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started alert orchestration with ID = '$InstanceId'" + #$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId + } else { + Write-Host 'No alerts to process' + } } catch { $Message = 'Exception on line {0} - {1}' -f $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message - Write-LogMessage -message $Message -API 'Alerts' -tenant $tenant.tenant -sev Error + Write-LogMessage -message $Message -API 'Alerts' -tenant $Item.tenant -sev Error } } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 index e4102cd31521..f302cfcfb306 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 @@ -16,20 +16,23 @@ function Write-CippFunctionStats { $TimeSpan = New-TimeSpan -Start $Start -End $End $Duration = [int]$TimeSpan.TotalSeconds + $StatEntity = @{} # Flatten data to json string - $Entity.PartitionKey = $FunctionType - $Entity.RowKey = $RowKey - $Entity.Start = $Start - $Entity.End = $End - $Entity.Duration = $Duration - $Entity.ErrorMsg = $ErrorMsg + $StatEntity.PartitionKey = $FunctionType + $StatEntity.RowKey = $RowKey + $StatEntity.Start = $Start + $StatEntity.End = $End + $StatEntity.Duration = $Duration + $StatEntity.ErrorMsg = $ErrorMsg $Entity = [PSCustomObject]$Entity foreach ($Property in $Entity.PSObject.Properties.Name) { if ($Entity.$Property.GetType().Name -in ('Hashtable', 'PSCustomObject')) { - $Entity.$Property = [string]($Entity.$Property | ConvertTo-Json -Compress) + $StatEntity.$Property = [string]($Entity.$Property | ConvertTo-Json -Compress) } } - Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force + $StatsEntity = [PSCustomObject]$StatsEntity + Write-Host ($StatEntity | ConvertTo-Json) + Add-CIPPAzDataTableEntity @Table -Entity $StatsEntity -Force } catch { Write-Host "Exception logging stats $($_.Exception.Message)" } diff --git a/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 b/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 index dd4acff806e8..1c7e177e3555 100644 --- a/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Invoke-CIPPStandardsRun.ps1 @@ -94,5 +94,5 @@ function Invoke-CIPPStandardsRun { $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) Write-Host "Started orchestration with ID = '$InstanceId'" - $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId + #$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuditLog.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuditLog.ps1 index 1dcd16e89cdb..e3682928503c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuditLog.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuditLog.ps1 @@ -4,13 +4,13 @@ function Invoke-CIPPStandardAuditLog { Internal #> param($Tenant, $Settings) - - $AuditLogEnabled = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AdminAuditLogConfig').UnifiedAuditLogIngestionEnabled + Write-Host ($Settings | ConvertTo-Json) + $AuditLogEnabled = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-AdminAuditLogConfig' -Select UnifiedAuditLogIngestionEnabled).UnifiedAuditLogIngestionEnabled If ($Settings.remediate) { Write-Host 'Time to remediate' - - $DehydratedTenant = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').IsDehydrated + + $DehydratedTenant = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' -Select IsDehydrated).IsDehydrated if ($DehydratedTenant) { try { New-ExoRequest -tenantid $Tenant -cmdlet 'Enable-OrganizationCustomization' @@ -20,7 +20,7 @@ function Invoke-CIPPStandardAuditLog { Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable organization customization. Error: $ErrorMessage" -sev Debug } } - + try { if ($AuditLogEnabled) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Unified Audit Log already enabled.' -sev Info diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index a651d072339f..67a480956b2d 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -51,31 +51,44 @@ function Receive-CippQueueTrigger { function Receive-CippOrchestrationTrigger { param($Context) - $DurableRetryOptions = @{ - FirstRetryInterval = (New-TimeSpan -Seconds 5) - MaxNumberOfAttempts = 3 - BackoffCoefficient = 2 - } - if (Test-Json -Json $Context.Input) { - $OrchestratorInput = $Context.Input | ConvertFrom-Json - } else { - $OrchestratorInput = $Context.Input - } - Write-Host ($Context | ConvertTo-Json -Depth 10) - $RetryOptions = New-DurableRetryOptions @DurableRetryOptions - Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $OrchestratorInput.TenantFilter -message "Started $($OrchestratorInput.OrchestratorName)" -sev info + try { - if (!$OrchestratorInput.Batch -or ($OrchestratorInput.Batch | Measure-Object).Count -eq 0) { - $Batch = (Invoke-ActivityFunction -FunctionName 'CIPPActivityFunction' -Input $OrchestratorInput.QueueFunction) - } else { - $Batch = $OrchestratorInput.Batch - } + if (Test-Json -Json $Context.Input) { + $OrchestratorInput = $Context.Input | ConvertFrom-Json + } else { + $OrchestratorInput = $Context.Input + } - foreach ($Item in $Batch) { - Invoke-DurableActivity -FunctionName 'CIPPActivityFunction' -Input $Item -NoWait -RetryOptions $RetryOptions - } + $DurableRetryOptions = @{ + FirstRetryInterval = (New-TimeSpan -Seconds 5) + MaxNumberOfAttempts = if ($OrchestratorInput.MaxAttempts) { $OrchestratorInput.MaxAttempts } else { 3 } + BackoffCoefficient = 2 + } + #Write-Host ($OrchestratorInput | ConvertTo-Json -Depth 10) + $RetryOptions = New-DurableRetryOptions @DurableRetryOptions - Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $tenant -message "Finished $($OrchestratorInput.OrchestratorName)" -sev Info + if ($Context.IsReplaying -ne $true -and -not $Context.Input.SkipLog) { + Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $OrchestratorInput.TenantFilter -message "Started $($OrchestratorInput.OrchestratorName)" -sev info + } + + if (!$OrchestratorInput.Batch -or ($OrchestratorInput.Batch | Measure-Object).Count -eq 0) { + $Batch = (Invoke-ActivityFunction -FunctionName 'CIPPActivityFunction' -Input $OrchestratorInput.QueueFunction -ErrorAction Stop) + } else { + $Batch = $OrchestratorInput.Batch + } + + if (($Batch | Measure-Object).Count -gt 0) { + foreach ($Item in $Batch) { + $null = Invoke-DurableActivity -FunctionName 'CIPPActivityFunction' -Input $Item -NoWait -RetryOptions $RetryOptions -ErrorAction Stop + } + } + + if ($Context.IsReplaying -ne $true -and -not $Context.Input.SkipLog) { + Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $tenant -message "Finished $($OrchestratorInput.OrchestratorName)" -sev Info + } + } catch { + Write-Host "Orchestrator error $($_.Exception.Message)" + } } function Receive-CippActivityTrigger { @@ -105,7 +118,7 @@ function Receive-CippActivityTrigger { ErrorMsg = $ErrorMsg } - Write-Information '####### Adding stats' + #Write-Information '####### Adding stats' Write-CippFunctionStats @Stats } diff --git a/Scheduler_GetQueue/function.json b/Scheduler_GetQueue/function.json index d0f59a682e3c..122f86c71d70 100644 --- a/Scheduler_GetQueue/function.json +++ b/Scheduler_GetQueue/function.json @@ -11,6 +11,11 @@ "direction": "out", "name": "QueueItem", "queueName": "CIPPGenericQueue" + }, + { + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/Scheduler_GetQueue/run.ps1 b/Scheduler_GetQueue/run.ps1 index f14ccc274dd4..2e80dfd588a1 100644 --- a/Scheduler_GetQueue/run.ps1 +++ b/Scheduler_GetQueue/run.ps1 @@ -5,7 +5,7 @@ $Tenants = Get-CIPPAzDataTableEntity @Table | Where-Object -Property PartitionKe $Tasks = foreach ($Tenant in $Tenants) { if ($Tenant.tenant -ne 'AllTenants') { - [pscustomobject]@{ + [pscustomobject]@{ Tenant = $Tenant.tenant Tag = 'SingleTenant' TenantID = $Tenant.tenantid @@ -15,7 +15,7 @@ $Tasks = foreach ($Tenant in $Tenants) { Write-Host 'All tenants, doing them all' $TenantList = Get-Tenants foreach ($t in $TenantList) { - [pscustomobject]@{ + [pscustomobject]@{ Tenant = $t.defaultDomainName Tag = 'AllTenants' TenantID = $t.customerId @@ -23,19 +23,22 @@ $Tasks = foreach ($Tenant in $Tenants) { } } } -} +} -foreach ($Task in $Tasks) { - $QueueItem = [pscustomobject]@{ +$Batch = foreach ($Task in $Tasks) { + [pscustomobject]@{ Tenant = $task.tenant Tenantid = $task.tenantid Tag = $task.tag Type = $task.type FunctionName = "Scheduler$($Task.Type)" } - try { - Push-OutputBinding -Name QueueItem -Value $QueueItem - } catch { - Write-Host "Could not launch queue item for $($Task.tenant): $($_.Exception.Message)" - } -} \ No newline at end of file +} +$InputObject = [PSCustomObject]@{ + OrchestratorName = 'Scheduler' + Batch = @($Batch) +} +#Write-Host ($InputObject | ConvertTo-Json) +$InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) +Write-Host "Started orchestration with ID = '$InstanceId'" +#$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId \ No newline at end of file From 9ec149fa2dedb4388761c436516e3b02470f0013 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sat, 9 Mar 2024 11:40:06 -0800 Subject: [PATCH 16/45] up version --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index 804440660c71..fb467b15735a 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -5.2.1 \ No newline at end of file +5.2.2 \ No newline at end of file From 2d20479b728517d98f2b148195bb782b59dac5b0 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sun, 10 Mar 2024 21:30:20 -0400 Subject: [PATCH 17/45] Alerts tweak --- Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 | 2 +- Modules/CippEntrypoints/CippEntrypoints.psm1 | 5 ++--- Scheduler_GetQueue/run.ps1 | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 index 48b5ac8f474b..0e4b50c8f9f2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-SchedulerAlert.ps1 @@ -41,7 +41,7 @@ function Push-SchedulerAlert { } if (($Batch | Measure-Object).Count -gt 0) { $InputObject = [PSCustomObject]@{ - OrchestratorName = 'Alerts' + OrchestratorName = 'AlertsOrchestrator' SkipLog = $true Batch = @($Batch) } diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 67a480956b2d..749a0a41a674 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -52,7 +52,6 @@ function Receive-CippOrchestrationTrigger { param($Context) try { - if (Test-Json -Json $Context.Input) { $OrchestratorInput = $Context.Input | ConvertFrom-Json } else { @@ -67,7 +66,7 @@ function Receive-CippOrchestrationTrigger { #Write-Host ($OrchestratorInput | ConvertTo-Json -Depth 10) $RetryOptions = New-DurableRetryOptions @DurableRetryOptions - if ($Context.IsReplaying -ne $true -and -not $Context.Input.SkipLog) { + if ($Context.IsReplaying -ne $true -and $Context.Input.SkipLog -ne $true) { Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $OrchestratorInput.TenantFilter -message "Started $($OrchestratorInput.OrchestratorName)" -sev info } @@ -83,7 +82,7 @@ function Receive-CippOrchestrationTrigger { } } - if ($Context.IsReplaying -ne $true -and -not $Context.Input.SkipLog) { + if ($Context.IsReplaying -ne $true -and $Context.Input.SkipLog -ne $true) { Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $tenant -message "Finished $($OrchestratorInput.OrchestratorName)" -sev Info } } catch { diff --git a/Scheduler_GetQueue/run.ps1 b/Scheduler_GetQueue/run.ps1 index 2e80dfd588a1..6d0553001e1e 100644 --- a/Scheduler_GetQueue/run.ps1 +++ b/Scheduler_GetQueue/run.ps1 @@ -35,7 +35,7 @@ $Batch = foreach ($Task in $Tasks) { } } $InputObject = [PSCustomObject]@{ - OrchestratorName = 'Scheduler' + OrchestratorName = 'SchedulerOrchestrator' Batch = @($Batch) } #Write-Host ($InputObject | ConvertTo-Json) From 502e3046d60775958b174b294cb05e5a12b5ec87 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sun, 10 Mar 2024 21:32:48 -0400 Subject: [PATCH 18/45] update init dev env --- Tools/Initialize-DevEnvironment.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/Initialize-DevEnvironment.ps1 b/Tools/Initialize-DevEnvironment.ps1 index 4f4f8f55aa58..e8b67a373ae5 100644 --- a/Tools/Initialize-DevEnvironment.ps1 +++ b/Tools/Initialize-DevEnvironment.ps1 @@ -13,3 +13,4 @@ Import-Module "$CippRoot\Modules\AzBobbyTables" Import-Module "$CippRoot\Modules\DNSHealth" Import-Module "$CippRoot\Modules\CippQueue" Import-Module "$CippRoot\Modules\CippCore" +Get-CIPPAuthentication \ No newline at end of file From 5113751dc0b7f6b612007de569f99f695b2718f3 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 12 Mar 2024 11:05:29 +0100 Subject: [PATCH 19/45] check for existence of upn --- Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 | 2 +- .../CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 index 66685982d956..6b933fbf64a0 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAdmins.ps1 @@ -13,7 +13,7 @@ function Push-CIPPAlertMFAAdmins { } if (!$DuoActive) { $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$top=999&$filter=IsAdmin eq true' -tenantid $($Item.tenant) | Where-Object -Property 'isMfaRegistered' -EQ $false - if ($users) { + if ($users.UserPrincipalName) { Write-AlertMessage -tenant $Item.tenant -message "The following admins do not have MFA registered: $($users.UserPrincipalName -join ', ')" } } else { diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 index a02d2afcdc34..e6401a7b117f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertMFAAlertUsers.ps1 @@ -7,7 +7,7 @@ function Push-CIPPAlertMFAAlertUsers { try { $users = New-GraphGETRequest -uri 'https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$filter=isMfaRegistered eq false and userType eq ''member''&$select=userPrincipalName,lastUpdatedDateTime,isMfaRegistered' -tenantid $($Item.tenant) - if ($users) { + if ($users.UserPrincipalName) { Write-AlertMessage -tenant $Item.tenant -message "The following $($users.Count) users do not have MFA registered: $($users.UserPrincipalName -join ', ')" } From 8fcf2f4a95134edca8416d2e7b83790fb4310d75 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 12 Mar 2024 11:53:26 +0100 Subject: [PATCH 20/45] fixes sharepoint quota --- .../Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 index 9614e59d340c..58dedd4888a8 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertSharepointQuota.ps1 @@ -11,7 +11,7 @@ function Push-CIPPAlertSharepointQuota { $sharepointToken.Add('accept', 'application/json') $sharepointQuota = (Invoke-RestMethod -Method 'GET' -Headers $sharepointToken -Uri "https://$($tenantName)-admin.sharepoint.com/_api/StorageQuotas()?api-version=1.3.2" -ErrorAction Stop).value if ($sharepointQuota) { - if ($Item.value) { $Value = $Item.value } else { $Value = 90 } + if ($Item.value -Is [Boolean]) { $Value = 90 } else { $Value = $Item.value } $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100) if ($UsedStoragePercentage -gt $Value) { Write-AlertMessage -tenant $($Item.tenant) -message "SharePoint Storage is at $($UsedStoragePercentage)%. Your alert threshold is $($Value)%" From 55ccf3ce8943fdb079bdf992b79c69f42b85a2d5 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 12 Mar 2024 12:24:10 +0100 Subject: [PATCH 21/45] fixes depth issues --- .../Public/Entrypoints/Invoke-ExecUserSettings.ps1 | 9 ++++----- .../Public/Entrypoints/Invoke-ListUserSettings.ps1 | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUserSettings.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUserSettings.ps1 index 76469b49b477..ab9092f13c1d 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUserSettings.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecUserSettings.ps1 @@ -11,18 +11,17 @@ function Invoke-ExecUserSettings { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' try { - $object = $request.body.currentSettings | Select-Object * -ExcludeProperty CurrentTenant, pageSizes, sidebarShow, sidebarUnfoldable, _persist | ConvertTo-Json -Compress + $object = $request.body.currentSettings | Select-Object * -ExcludeProperty CurrentTenant, pageSizes, sidebarShow, sidebarUnfoldable, _persist | ConvertTo-Json -Compress -Depth 10 $Table = Get-CippTable -tablename 'UserSettings' $Table.Force = $true Add-CIPPAzDataTableEntity @Table -Entity @{ JSON = "$object" RowKey = "$($Request.body.user)" - PartitionKey = "UserSettings" + PartitionKey = 'UserSettings' } $StatusCode = [HttpStatusCode]::OK - $Results = [pscustomobject]@{"Results" = "Successfully added user settings" } - } - catch { + $Results = [pscustomobject]@{'Results' = 'Successfully added user settings' } + } catch { $ErrorMsg = Get-NormalizedError -message $($_.Exception.Message) $Results = "Function Error: $ErrorMsg" $StatusCode = [HttpStatusCode]::BadRequest diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 index 274c5d8383f1..50781d922e7b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListUserSettings.ps1 @@ -14,8 +14,8 @@ function Invoke-ListUserSettings { try { $Table = Get-CippTable -tablename 'UserSettings' $UserSettings = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq 'allUsers'" - if (!$UserSettings) { Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$username'" } - $UserSettings = $UserSettings | Select-Object -ExpandProperty JSON | ConvertFrom-Json -Depth 10 -ErrorAction SilentlyContinue + if (!$UserSettings) { $userSettings = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$username'" } + $UserSettings = $UserSettings.JSON | ConvertFrom-Json -Depth 10 -ErrorAction SilentlyContinue $StatusCode = [HttpStatusCode]::OK $Results = $UserSettings } catch { From 872449094bf48a7d3294564f20343431c526f6f5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 12 Mar 2024 11:44:19 -0400 Subject: [PATCH 22/45] Tenant Onboarding durable --- .../Entrypoints/Invoke-ExecOnboardTenant.ps1 | 20 +++++++++++------ .../Push-ExecOnboardTenantQueue.ps1 | 22 +++++++++---------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 index e9e23c2a174e..41365297ca4f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecOnboardTenant.ps1 @@ -52,13 +52,19 @@ function Invoke-ExecOnboardTenant { } Add-CIPPAzDataTableEntity @OnboardTable -Entity $TenantOnboarding -Force -ErrorAction Stop - Push-OutputBinding -Name QueueItem -Value ([pscustomobject]@{ - FunctionName = 'ExecOnboardTenantQueue' - id = $Id - Roles = $Request.Body.gdapRoles - AddMissingGroups = $Request.Body.addMissingGroups - AutoMapRoles = $Request.Body.autoMapRoles - }) + $Item = [pscustomobject]@{ + FunctionName = 'ExecOnboardTenantQueue' + id = $Id + Roles = $Request.Body.gdapRoles + AddMissingGroups = $Request.Body.addMissingGroups + AutoMapRoles = $Request.Body.autoMapRoles + } + + $InputObject = @{ + OrchestratorName = 'OnboardingOrchestrator' + Batch = @($Item) + } + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) } $Steps = $TenantOnboarding.OnboardingSteps | ConvertFrom-Json diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 index 6c12ad723332..24d206b069c7 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ExecOnboardTenantQueue.ps1 @@ -4,11 +4,11 @@ Function Push-ExecOnboardTenantQueue { Entrypoint #> [CmdletBinding()] - param($QueueItem, $TriggerMetadata) + param($Item) try { $DateFormat = '%Y-%m-%d %H:%M:%S' - $Id = $QueueItem.id - #Write-Host ($QueueItem.Roles | ConvertTo-Json) + $Id = $Item.id + #Write-Host ($Item.Roles | ConvertTo-Json) $Start = Get-Date $Logs = [System.Collections.Generic.List[object]]::new() $OnboardTable = Get-CIPPTable -TableName 'TenantOnboarding' @@ -117,7 +117,7 @@ Function Push-ExecOnboardTenantQueue { if ($OnboardingSteps.Step2.Status -eq 'succeeded') { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Checking group mapping' }) $AccessAssignments = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id/accessAssignments" - if ($AccessAssignments.id -and $QueueItem.AutoMapRoles -ne $true) { + if ($AccessAssignments.id -and $Item.AutoMapRoles -ne $true) { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Groups mapped' }) $OnboardingSteps.Step3.Status = 'succeeded' $OnboardingSteps.Step3.Message = 'Your GDAP relationship already has mapped security groups' @@ -136,8 +136,8 @@ Function Push-ExecOnboardTenantQueue { $MissingRoles = [System.Collections.Generic.List[object]]::new() $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Relationship has existing access assignments, checking for missing mappings' }) #Write-Host ($AccessAssignments | ConvertTo-Json -Depth 5) - if ($QueueItem.Roles -and $QueueItem.AutoMapRoles -eq $true) { - foreach ($Role in $QueueItem.Roles) { + if ($Item.Roles -and $Item.AutoMapRoles -eq $true) { + foreach ($Role in $Item.Roles) { if ($AccessAssignments.accessContainer.accessContainerid -notcontains $Role.GroupId -and $Relationship.accessDetails.unifiedRoles.roleDefinitionId -contains $Role.roleDefinitionId) { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = "Adding missing group to relationship: $($Role.GroupName)" }) $MissingRoles.Add([PSCustomObject]$Role) @@ -161,16 +161,16 @@ Function Push-ExecOnboardTenantQueue { } } - if (!$AccessAssignments.id -and !$Invite -and $QueueItem.Roles) { + if (!$AccessAssignments.id -and !$Invite -and $Item.Roles) { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'No access assignments found, using defined role mapping.' }) $MatchingRoles = [System.Collections.Generic.List[object]]::new() - foreach ($Role in $QueueItem.Roles) { + foreach ($Role in $Item.Roles) { if ($Relationship.accessDetails.unifiedRoles.roleDefinitionId -contains $Role.roleDefinitionId) { $MatchingRoles.Add([PSCustomObject]$Role) } } - if (($MatchingRoles | Measure-Object).Count -gt 0 -and $QueueItem.AutoMapRoles -eq $true) { + if (($MatchingRoles | Measure-Object).Count -gt 0 -and $Item.AutoMapRoles -eq $true) { $Invite = [PSCustomObject]@{ 'PartitionKey' = 'invite' 'RowKey' = $Id @@ -224,11 +224,11 @@ Function Push-ExecOnboardTenantQueue { if ($AccessAssignments.status -notcontains 'pending') { $OnboardingSteps.Step3.Message = 'Group check: Access assignments are mapped and active' $OnboardingSteps.Step3.Status = 'succeeded' - if ($QueueItem.AddMissingGroups -eq $true) { + if ($Item.AddMissingGroups -eq $true) { $Logs.Add([PSCustomObject]@{ Date = Get-Date -UFormat $DateFormat; Log = 'Checking for missing groups for SAM user' }) $SamUserId = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/me?`$select=id").id $CurrentMemberships = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/me/transitiveMemberOf?`$select=id,displayName" - foreach ($Role in $QueueItem.Roles) { + foreach ($Role in $Item.Roles) { if ($CurrentMemberships.id -notcontains $Role.GroupId) { $PostBody = @{ '@odata.id' = 'https://graph.microsoft.com/v1.0/directoryObjects/{0}' -f $SamUserId From 6d8ccf652d753afa988c18a11b0e0509e2bcc454 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 12 Mar 2024 16:52:26 +0100 Subject: [PATCH 23/45] update license overview --- .../Entrypoints/Invoke-ListLicenses.ps1 | 12 ++++- .../Public/Get-CIPPLicenseOverview.ps1 | 51 +++++++++++-------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 index 27c2db9767a3..03a719a7f259 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 @@ -18,7 +18,11 @@ Function Invoke-ListLicenses { # Interact with query parameters or the body of the request. $TenantFilter = $Request.Query.TenantFilter $RawGraphRequest = if ($TenantFilter -ne 'AllTenants') { - $GraphRequest = Get-CIPPLicenseOverview -TenantFilter $TenantFilter + $GraphRequest = Get-CIPPLicenseOverview -TenantFilter $TenantFilter | ForEach-Object { + $TermInfo = $_.TermInfo | ConvertFrom-Json -ErrorAction SilentlyContinue + $_.TermInfo = $TermInfo + $_ + } } else { $Table = Get-CIPPTable -TableName cachelicenses $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) @@ -29,7 +33,11 @@ Function Invoke-ListLicenses { License = 'Loading data for all tenants. Please check back in 1 minute' } } else { - $GraphRequest = $Rows + $GraphRequest = $Rows | ForEach-Object { + $TermInfo = $_.TermInfo | ConvertFrom-Json -ErrorAction SilentlyContinue + $_.TermInfo = $TermInfo + $_ + } } } diff --git a/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 b/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 index 698d30653774..9f06bddd2512 100644 --- a/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 @@ -3,19 +3,17 @@ function Get-CIPPLicenseOverview { [CmdletBinding()] param ( $TenantFilter, - $APIName = "Get License Overview", + $APIName = 'Get License Overview', $ExecutingUser ) $LicRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/subscribedSkus' -tenantid $TenantFilter - $LicOverviewRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/directory/subscriptions' -tenantid $TenantFilter | Where-Object -Property nextLifecycleDateTime -GT (Get-Date) | Select-Object *, - @{Name = 'consumedUnits'; Expression = { ($LicRequest | Where-Object -Property skuid -EQ $_.skuId).consumedUnits } }, - @{Name = 'prepaidUnits'; Expression = { ($LicRequest | Where-Object -Property skuid -EQ $_.skuId).prepaidUnits } } + $SkuIDs = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/directory/subscriptions' -tenantid $TenantFilter $RawGraphRequest = [PSCustomObject]@{ Tenant = $TenantFilter - Licenses = $LicOverviewRequest + Licenses = $LicRequest } Set-Location (Get-Item $PSScriptRoot).FullName $ConvertTable = Import-Csv Conversiontable.csv @@ -27,33 +25,42 @@ function Get-CIPPLicenseOverview { if ($sku.skuId -in $ExcludedSkuList.GUID) { continue } $PrettyName = ($ConvertTable | Where-Object { $_.guid -eq $sku.skuid }).'Product_Display_Name' | Select-Object -Last 1 if (!$PrettyName) { $PrettyName = $sku.skuPartNumber } - $diff = $sku.nextLifecycleDateTime - $sku.createdDateTime + # Initialize $Term with the default value - $Term = "Term unknown or non-NCE license" - if ($diff.Days -ge 360 -and $diff.Days -le 1089) { - $Term = "Yearly" + $TermInfo = foreach ($Subscription in $skuid.subscriptionIds) { + $SubInfo = $SkuIDs | Where-Object { $_.id -eq $Subscription } + $diff = $SubInfo.nextLifecycleDateTime - $SubInfo.createdDateTime + $Term = 'Term unknown or non-NCE license' + if ($diff.Days -ge 360 -and $diff.Days -le 1089) { + $Term = 'Yearly' + } elseif ($diff.Days -ge 1090 -and $diff.Days -le 1100) { + $Term = '3 Year' + } elseif ($diff.Days -ge 25 -and $diff.Days -le 35) { + $Term = 'Monthly' + } + $TimeUntilRenew = ($subinfo.nextLifecycleDateTime - (Get-Date)).days + [PSCustomObject]@{ + Status = $SubInfo.status + Term = $Term + TotalLicenses = $SubInfo.totalLicenses + TimeUntilRenew = $TimeUntilRenew + NextLifecycle = $SubInfo.nextLifecycleDateTime + SubscriptionId = $subinfo.id + IsTrial = $SubInfo.isTrial + CSPSubscriptionId = $SubInfo.commerceSubscriptionId + OCPSubscriptionId = $SubInfo.ocpSubscriptionId + } } - elseif ($diff.Days -ge 1090 -and $diff.Days -le 1100) { - $Term = "3 Year" - } - elseif ($diff.Days -ge 25 -and $diff.Days -le 35) { - $Term = "Monthly" - } - $TimeUntilRenew = $sku.nextLifecycleDateTime - (Get-Date) [pscustomobject]@{ Tenant = [string]$singlereq.Tenant License = [string]$PrettyName CountUsed = [string]"$($sku.consumedUnits)" CountAvailable = [string]$sku.prepaidUnits.enabled - $sku.consumedUnits - TotalLicenses = [string]"$($sku.TotalLicenses)" + TotalLicenses = [string]"$($sku.prepaidUnits.enabled)" skuId = [string]$sku.skuId skuPartNumber = [string]$PrettyName availableUnits = [string]$sku.prepaidUnits.enabled - $sku.consumedUnits - EstTerm = [string]$Term - TimeUntilRenew = [string]"$($TimeUntilRenew.Days)" - Trial = [bool]$sku.isTrial - dateCreated = [string]$sku.createdDateTime - dateExpires = [string]$sku.nextLifecycleDateTime + TermInfo = [string]($TermInfo | ConvertTo-Json -Depth 10 -Compress) 'PartitionKey' = 'License' 'RowKey' = "$($singlereq.Tenant) - $($sku.skuid)" } From 46727f6d310ad521cf4ba44ac0a9e0d56afa728d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 12 Mar 2024 16:56:13 +0100 Subject: [PATCH 24/45] license overview --- Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 b/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 index 9f06bddd2512..d4d057f6ddfc 100644 --- a/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 @@ -43,10 +43,10 @@ function Get-CIPPLicenseOverview { Status = $SubInfo.status Term = $Term TotalLicenses = $SubInfo.totalLicenses - TimeUntilRenew = $TimeUntilRenew + DaysUntilRenew = $TimeUntilRenew NextLifecycle = $SubInfo.nextLifecycleDateTime - SubscriptionId = $subinfo.id IsTrial = $SubInfo.isTrial + SubscriptionId = $subinfo.id CSPSubscriptionId = $SubInfo.commerceSubscriptionId OCPSubscriptionId = $SubInfo.ocpSubscriptionId } From 0adb85d0c4d1cda43bd472333bafb5b531d0a2f7 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 12 Mar 2024 17:08:50 +0100 Subject: [PATCH 25/45] ordered list --- Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 b/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 index d4d057f6ddfc..9668cd51b50e 100644 --- a/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPLicenseOverview.ps1 @@ -27,7 +27,7 @@ function Get-CIPPLicenseOverview { if (!$PrettyName) { $PrettyName = $sku.skuPartNumber } # Initialize $Term with the default value - $TermInfo = foreach ($Subscription in $skuid.subscriptionIds) { + $TermInfo = foreach ($Subscription in $sku.subscriptionIds) { $SubInfo = $SkuIDs | Where-Object { $_.id -eq $Subscription } $diff = $SubInfo.nextLifecycleDateTime - $SubInfo.createdDateTime $Term = 'Term unknown or non-NCE license' From 21a4119ac3b0dc5de9e80bced3a23b93084531db Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 12 Mar 2024 12:27:01 -0400 Subject: [PATCH 26/45] Graph Request durable --- .../Push-ListGraphRequestQueue.ps1 | 46 +++++++++---------- .../GraphRequests/Get-GraphRequestList.ps1 | 21 +++++++-- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ListGraphRequestQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ListGraphRequestQueue.ps1 index 82f12e1df665..1705df9a7f70 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-ListGraphRequestQueue.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ListGraphRequestQueue.ps1 @@ -4,36 +4,36 @@ function Push-ListGraphRequestQueue { Entrypoint #> # Input bindings are passed in via param block. - param($QueueItem, $TriggerMetadata) + param($Item) # Write out the queue message and metadata to the information log. - Write-Host "PowerShell queue trigger function processed work item: $($QueueItem.Endpoint) - $($QueueItem.TenantFilter)" + Write-Host "PowerShell queue trigger function processed work item: $($Item.Endpoint) - $($Item.TenantFilter)" - $TenantQueueName = '{0} - {1}' -f $QueueItem.QueueName, $QueueItem.TenantFilter - Update-CippQueueEntry -RowKey $QueueItem.QueueId -Status 'Processing' -Name $TenantQueueName + $TenantQueueName = '{0} - {1}' -f $Item.QueueName, $Item.TenantFilter + Update-CippQueueEntry -RowKey $Item.QueueId -Status 'Processing' -Name $TenantQueueName $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) - foreach ($Item in ($QueueItem.Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { - $ParamCollection.Add($Item.Key, $Item.Value) + foreach ($Param in ($Item.Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { + $ParamCollection.Add($Param.Key, $Param.Value) } - $PartitionKey = $QueueItem.PartitionKey + $PartitionKey = $Item.PartitionKey - $TableName = ('cache{0}' -f ($QueueItem.Endpoint -replace '[^A-Za-z0-9]'))[0..62] -join '' - Write-Host $TableName + $TableName = ('cache{0}' -f ($Item.Endpoint -replace '[^A-Za-z0-9]'))[0..62] -join '' + Write-Host "Queue Table: $TableName" $Table = Get-CIPPTable -TableName $TableName - $Filter = "PartitionKey eq '{0}' and Tenant eq '{1}'" -f $PartitionKey, $QueueItem.TenantFilter - Write-Host $Filter + $Filter = "PartitionKey eq '{0}' and Tenant eq '{1}'" -f $PartitionKey, $Item.TenantFilter + Write-Host "Filter: $Filter" Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey | Remove-AzDataTableEntity @Table $GraphRequestParams = @{ - TenantFilter = $QueueItem.TenantFilter - Endpoint = $QueueItem.Endpoint - Parameters = $QueueItem.Parameters - NoPagination = $QueueItem.NoPagination - ReverseTenantLookupProperty = $QueueItem.ReverseTenantLookupProperty - ReverseTenantLookup = $QueueItem.ReverseTenantLookup + TenantFilter = $Item.TenantFilter + Endpoint = $Item.Endpoint + Parameters = $Item.Parameters + NoPagination = $Item.NoPagination + ReverseTenantLookupProperty = $Item.ReverseTenantLookupProperty + ReverseTenantLookup = $Item.ReverseTenantLookup SkipCache = $true } @@ -41,7 +41,7 @@ function Push-ListGraphRequestQueue { Get-GraphRequestList @GraphRequestParams } catch { [PSCustomObject]@{ - Tenant = $QueueItem.Tenant + Tenant = $Item.Tenant CippStatus = "Could not connect to tenant. $($_.Exception.message)" } } @@ -49,9 +49,9 @@ function Push-ListGraphRequestQueue { $GraphResults = foreach ($Request in $RawGraphRequest) { $Json = ConvertTo-Json -Depth 5 -Compress -InputObject $Request [PSCustomObject]@{ - TenantFilter = [string]$QueueItem.TenantFilter - QueueId = [string]$QueueItem.QueueId - QueueType = [string]$QueueItem.QueueType + TenantFilter = [string]$Item.TenantFilter + QueueId = [string]$Item.QueueId + QueueType = [string]$Item.QueueType RowKey = [string](New-Guid) PartitionKey = [string]$PartitionKey Data = [string]$Json @@ -59,9 +59,9 @@ function Push-ListGraphRequestQueue { } try { Add-CIPPAzDataTableEntity @Table -Entity $GraphResults -Force | Out-Null - Update-CippQueueEntry -RowKey $QueueItem.QueueId -Status 'Completed' + Update-CippQueueEntry -RowKey $Item.QueueId -Status 'Completed' } catch { Write-Host "Queue Error: $($_.Exception.Message)" - Update-CippQueueEntry -RowKey $QueueItem.QueueId -Status 'Failed' + Update-CippQueueEntry -RowKey $Item.QueueId -Status 'Failed' } } \ No newline at end of file diff --git a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 index 3b3b4660a0bf..52cc2c3778dc 100644 --- a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 +++ b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 @@ -158,9 +158,9 @@ function Get-GraphRequestList { } Write-Host 'Pushing output bindings' try { - Get-Tenants -IncludeErrors | ForEach-Object { + $Batch = Get-Tenants -IncludeErrors | ForEach-Object { $TenantFilter = $_.defaultDomainName - $QueueTenant = [PSCustomObject]@{ + [PSCustomObject]@{ FunctionName = 'ListGraphRequestQueue' TenantFilter = $TenantFilter Endpoint = $Endpoint @@ -175,8 +175,15 @@ function Get-GraphRequestList { ReverseTenantLookup = $ReverseTenantLookup.IsPresent } - Push-OutputBinding -Name QueueItem -Value $QueueTenant + #Push-OutputBinding -Name QueueItem -Value $QueueTenant + } + + $InputObject = @{ + OrchestratorName = 'GraphRequestOrchestrator' + Batch = @($Batch) } + #Write-Host ($InputObject | ConvertTo-Json -Depth 5) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) } catch { Write-Host "QUEUE ERROR: $($_.Exception.Message)" } @@ -234,7 +241,13 @@ function Get-GraphRequestList { ReverseTenantLookup = $ReverseTenantLookup.IsPresent } - Push-OutputBinding -Name QueueItem -Value $QueueTenant + $InputObject = @{ + OrchestratorName = 'GraphRequestOrchestrator' + Batch = @($QueueTenant) + } + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + + #Push-OutputBinding -Name QueueItem -Value $QueueTenant [PSCustomObject]@{ QueueMessage = ('Loading {0} rows for {1}. Please check back after the job completes' -f $Count, $TenantFilter) From ea858fcf4aa55185afecbdf98501c8f7e0c2be75 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 12 Mar 2024 17:38:07 +0100 Subject: [PATCH 27/45] cleanup --- MailProviders/Mesh.json | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 MailProviders/Mesh.json diff --git a/MailProviders/Mesh.json b/MailProviders/Mesh.json deleted file mode 100644 index 3109cc8c4876..000000000000 --- a/MailProviders/Mesh.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Name": "Mesh Email Security", - "_MxComment": "https://docs.emailsecurity.app/help-center/connection-details", - "MxMatch": "emailsecurity.app", - "_SpfComment": "https://docs.emailsecurity.app/help-center/connection-details", - "SpfInclude": "spf1.emailsecurity.app", - "_DkimComment": "No configuration found", - "Selectors": [""] -} \ No newline at end of file From 598be08dda54dbd666b4feffeb3e3d453c8849ff Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 12 Mar 2024 18:07:49 +0100 Subject: [PATCH 28/45] added hard exit fail --- Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 index c1d7512b14d6..41c1004a8cf6 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-AddUser.ps1 @@ -59,6 +59,7 @@ Function Invoke-AddUser { } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($userobj.tenantid) -message "Failed to create user. Error:$($_.Exception.Message)" -Sev 'Error' $body = $results.add("Failed to create user. $($_.Exception.Message)" ) + exit 1 } try { From 684f9fdd4c9cf61500d1b2fd44d85aa297b8780c Mon Sep 17 00:00:00 2001 From: Mo Date: Wed, 13 Mar 2024 11:36:20 +0000 Subject: [PATCH 29/45] Adding group members missing output --- Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1 b/Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1 index 2ff3c406102b..a4c66a07cb7b 100644 --- a/Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPGroupMember.ps1 @@ -16,7 +16,7 @@ function Add-CIPPGroupMember( } else { New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($GroupId)" -tenantid $TenantFilter -type patch -body $addmemberbody -Verbose } - $Message = "Successfully added user $($Member) to $GroupId." + $Message = "Successfully added user $($Member) to $($GroupId)." Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message $Message -Sev 'Info' return $message return From b2dd27611b0a4384b681a674da3d2d821e512b9a Mon Sep 17 00:00:00 2001 From: Mo Date: Wed, 13 Mar 2024 12:18:15 +0000 Subject: [PATCH 30/45] Add output for what group the user was removed from --- Modules/CIPPCore/Public/Remove-CIPPGroupMember.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Remove-CIPPGroupMember.ps1 b/Modules/CIPPCore/Public/Remove-CIPPGroupMember.ps1 index 9567039fe8e4..54c6a33e1a9d 100644 --- a/Modules/CIPPCore/Public/Remove-CIPPGroupMember.ps1 +++ b/Modules/CIPPCore/Public/Remove-CIPPGroupMember.ps1 @@ -16,7 +16,7 @@ function Remove-CIPPGroupMember( } else { New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($GroupId)/members/$($Member)/`$ref" -tenantid $TenantFilter -type DELETE -body '{}' -Verbose } - $Message = "Successfully removed user $($Member) from $GroupId." + $Message = "Successfully removed user $($Member) from $($GroupId)." Write-LogMessage -user $ExecutingUser -API $APIName -tenant $TenantFilter -message $Message -Sev 'Info' return $message } catch { From e71c9ccb6f1cad34d061424ab23931fa602ad96c Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 13 Mar 2024 09:57:52 -0400 Subject: [PATCH 31/45] Webhooks Durable --- .../Invoke-ListPendingWebhooks.ps1 | 41 ++++++ .../Entrypoints/Invoke-PublicWebhooks.ps1 | 128 ++++++++++++++++++ .../Entrypoints/Push-PublicWebhookProcess.ps1 | 17 +++ PublicWebhooks/function.json | 22 --- PublicWebhooks/run.ps1 | 37 ----- PublicWebhooksProcess/function.json | 10 -- PublicWebhooksProcess/run.ps1 | 79 ----------- Scheduler_GetWebhooks/function.json | 15 ++ Scheduler_GetWebhooks/run.ps1 | 13 ++ 9 files changed, 214 insertions(+), 148 deletions(-) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListPendingWebhooks.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-PublicWebhooks.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-PublicWebhookProcess.ps1 delete mode 100644 PublicWebhooks/function.json delete mode 100644 PublicWebhooks/run.ps1 delete mode 100644 PublicWebhooksProcess/function.json delete mode 100644 PublicWebhooksProcess/run.ps1 create mode 100644 Scheduler_GetWebhooks/function.json create mode 100644 Scheduler_GetWebhooks/run.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPendingWebhooks.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPendingWebhooks.ps1 new file mode 100644 index 000000000000..24c7020f0e31 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListPendingWebhooks.ps1 @@ -0,0 +1,41 @@ +using namespace System.Net + +Function Invoke-ListPendingWebhooks { + <# + .FUNCTIONALITY + Entrypoint + #> + [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.' + try { + $Table = Get-CIPPTable -TableName 'WebhookIncoming' + $Webhooks = Get-CIPPAzDataTableEntity @Table + $Results = $Webhooks | Select-Object -ExcludeProperty RowKey, PartitionKey, ETag, Timestamp + $PendingWebhooks = foreach ($Result in $Results) { + foreach ($Property in $Result.PSObject.Properties.Name) { + if (Test-Json -Json $Result.$Property -ErrorAction SilentlyContinue) { + $Result.$Property = $Result.$Property | ConvertFrom-Json + } + } + $Result + } + } catch { + $PendingWebhooks = @() + } + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ + Results = @($PendingWebhooks) + Metadata = @{ + Count = ($PendingWebhooks | Measure-Object).Count + } + } + }) +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicWebhooks.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicWebhooks.ps1 new file mode 100644 index 000000000000..97c135235cd6 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-PublicWebhooks.ps1 @@ -0,0 +1,128 @@ +using namespace System.Net +function Invoke-PublicWebhooks { + # Input bindings are passed in via param block. + param($Request, $TriggerMetadata) + + Set-Location (Get-Item $PSScriptRoot).Parent.FullName + $WebhookTable = Get-CIPPTable -TableName webhookTable + $WebhookIncoming = Get-CIPPTable -TableName WebhookIncoming + $Webhooks = Get-CIPPAzDataTableEntity @WebhookTable + Write-Host 'Received request' + Write-Host "CIPPID: $($request.Query.CIPPID)" + $url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 + Write-Host $url + if ($Request.Query.CIPPID -in $Webhooks.RowKey) { + Write-Host 'Found matching CIPPID' + if ($Webhooks.Resource -eq 'M365AuditLogs') { + Write-Host "Found M365AuditLogs - This is an old entry, we'll deny so Microsoft stops sending it." + $body = 'This webhook is not authorized, its an old entry.' + $StatusCode = [HttpStatusCode]::Forbidden + } + if ($Request.query.ValidationToken -or $Request.body.validationCode) { + Write-Host 'Validation token received' + $body = $request.query.ValidationToken + $StatusCode = [HttpStatusCode]::OK + } else { + Write-Host 'Received request' + Write-Host "CIPPID: $($request.Query.CIPPID)" + $url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 + Write-Host $url + + $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID + + if ($Request.Query.Type -eq 'GraphSubscription') { + # Graph Subscriptions + [pscustomobject]$ReceivedItem = $Request.Body.value + $Entity = [PSCustomObject]@{ + PartitionKey = 'Webhook' + RowKey = [string](New-Guid).Guid + Type = $Request.Query.Type + Data = [string]($ReceivedItem | ConvertTo-Json -Depth 10) + CIPPID = $Request.Query.CIPPID + WebhookInfo = [string]($WebhookInfo | ConvertTo-Json -Depth 10) + FunctionName = 'PublicWebhookProcess' + } + Add-CIPPAzDataTableEntity @WebhookIncoming -Entity $Entity + ## Push webhook data to queue + #Invoke-CippGraphWebhookProcessing -Data $ReceivedItem -CIPPID $request.Query.CIPPID -WebhookInfo $Webhookinfo + + } else { + # Auditlog Subscriptions + try { + foreach ($ReceivedItem In ($Request.body)) { + $ReceivedItem = [pscustomobject]$ReceivedItem + Write-Host "Received Item: $($ReceivedItem | ConvertTo-Json -Depth 15 -Compress))" + $TenantFilter = (Get-Tenants | Where-Object -Property customerId -EQ $ReceivedItem.TenantId).defaultDomainName + Write-Host "Webhook TenantFilter: $TenantFilter" + $ConfigTable = get-cipptable -TableName 'SchedulerConfig' + $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable | Where-Object { $_.Tenant -eq $TenantFilter -or $_.Tenant -eq 'AllTenants' } + $Operations = @(($AlertConfig.if | ConvertFrom-Json -ErrorAction SilentlyContinue).selection) + 'UserLoggedIn' + $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID + #Increased download efficiency: only download the data we need for processing. Todo: Change this to load from table or dynamic source. + $MappingTable = [pscustomobject]@{ + 'UserLoggedIn' = 'Audit.AzureActiveDirectory' + 'Add member to role.' = 'Audit.AzureActiveDirectory' + 'Disable account.' = 'Audit.AzureActiveDirectory' + 'Update StsRefreshTokenValidFrom Timestamp.' = 'Audit.AzureActiveDirectory' + 'Enable account.' = 'Audit.AzureActiveDirectory' + 'Disable Strong Authentication.' = 'Audit.AzureActiveDirectory' + 'Reset user password.' = 'Audit.AzureActiveDirectory' + 'Add service principal.' = 'Audit.AzureActiveDirectory' + 'HostedIP' = 'Audit.AzureActiveDirectory' + 'badRepIP' = 'Audit.AzureActiveDirectory' + 'UserLoggedInFromUnknownLocation' = 'Audit.AzureActiveDirectory' + 'customfield' = 'AnyLog' + 'anyAlert' = 'AnyLog' + 'New-InboxRule' = 'Audit.Exchange' + 'Set-InboxRule' = 'Audit.Exchange' + } + #Compare $Operations to $MappingTable. If there is a match, we make a new variable called $LogsToDownload + #Example: $Operations = 'UserLoggedIn', 'Set-InboxRule' makes : $LogsToDownload = @('Audit.AzureActiveDirectory',Audit.Exchange) + $LogsToDownload = $Operations | Where-Object { $MappingTable.$_ } | ForEach-Object { $MappingTable.$_ } + Write-Host "Our operations: $Operations" + Write-Host "Logs to download: $LogsToDownload" + if ($ReceivedItem.ContentType -in $LogsToDownload -or 'AnyLog' -in $LogsToDownload) { + $Data = New-GraphPostRequest -type GET -uri "https://manage.office.com/api/v1.0/$($ReceivedItem.tenantId)/activity/feed/audit/$($ReceivedItem.contentid)" -tenantid $TenantFilter -scope 'https://manage.office.com/.default' + } else { + Write-Host "No data to download for $($ReceivedItem.ContentType)" + continue + } + Write-Host "Data found: $($data.count) items" + $DataToProcess = if ('anylog' -NotIn $LogsToDownload) { $Data | Where-Object -Property Operation -In $Operations } else { $Data } + Write-Host "Data to process found: $($DataToProcess.count) items" + foreach ($Item in $DataToProcess) { + Write-Host "Processing $($item.operation)" + + ## Push webhook data to table + $Entity = [PSCustomObject]@{ + PartitionKey = 'Webhook' + RowKey = [string](New-Guid).Guid + Type = 'AuditLog' + Data = [string]($Item | ConvertTo-Json -Depth 10) + CIPPURL = $CIPPURL + TenantFilter = $TenantFilter + FunctionName = 'PublicWebhookProcess' + } + Add-CIPPAzDataTableEntity @WebhookIncoming -Entity $Entity -Force + #Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url + } + } + } catch { + Write-Host "Webhook Failed: $($_.Exception.Message). Line number $($_.InvocationInfo.ScriptLineNumber)" + } + } + + $Body = 'Webhook Recieved' + $StatusCode = [HttpStatusCode]::OK + } + } else { + $Body = 'This webhook is not authorized.' + $StatusCode = [HttpStatusCode]::Forbidden + } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = $StatusCode + Body = $Body + }) +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-PublicWebhookProcess.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-PublicWebhookProcess.ps1 new file mode 100644 index 000000000000..99a932d35e79 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-PublicWebhookProcess.ps1 @@ -0,0 +1,17 @@ +function Push-PublicWebhookProcess { + param($Item) + + try { + if ($Item.Type -eq 'GraphSubscription') { + Invoke-CippGraphWebhookProcessing -Data ($Item.Data | ConvertFrom-Json) -CIPPID $Item.CIPPID -WebhookInfo ($Item.Webhookinfo | ConvertFrom-Json) + } elseif ($Item.Type -eq 'AuditLog') { + Invoke-CippWebhookProcessing -TenantFilter $Item.TenantFilter -Data ($Item.Data | ConvertFrom-Json) -CIPPPURL $Item.CIPPURL + } + $WebhookIncoming = Get-CIPPTable -TableName WebhookIncoming + $Entity = $Item | Select-Object -Property RowKey, PartitionKey + Remove-AzDataTableEntity @WebhookIncoming -Entity $Entity + } catch { + Write-Host "Webhook Exception: $($_.Exception.Message)" + } + +} \ No newline at end of file diff --git a/PublicWebhooks/function.json b/PublicWebhooks/function.json deleted file mode 100644 index f59adac3eb84..000000000000 --- a/PublicWebhooks/function.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "bindings": [ - { - "authLevel": "anonymous", - "type": "httpTrigger", - "direction": "in", - "name": "Request", - "methods": ["get", "post"] - }, - { - "type": "http", - "direction": "out", - "name": "Response" - }, - { - "type": "queue", - "direction": "out", - "name": "QueueWebhook", - "queueName": "webhooksqueue" - } - ] -} diff --git a/PublicWebhooks/run.ps1 b/PublicWebhooks/run.ps1 deleted file mode 100644 index 2faa35804e05..000000000000 --- a/PublicWebhooks/run.ps1 +++ /dev/null @@ -1,37 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($Request, $TriggerMetadata) - -Set-Location (Get-Item $PSScriptRoot).Parent.FullName -$WebhookTable = Get-CIPPTable -TableName webhookTable -$Webhooks = Get-CIPPAzDataTableEntity @WebhookTable -Write-Host 'Received request' -Write-Host "CIPPID: $($request.Query.CIPPID)" -$url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 -Write-Host $url -if ($Request.Query.CIPPID -in $Webhooks.RowKey) { - Write-Host 'Found matching CIPPID' - if ($Webhooks.Resource -eq 'M365AuditLogs') { - Write-Host "Found M365AuditLogs - This is an old entry, we'll deny so Microsoft stops sending it." - $body = 'This webhook is not authorized, its an old entry.' - $StatusCode = [HttpStatusCode]::Forbidden - } - if ($Request.query.ValidationToken -or $Request.body.validationCode) { - Write-Host 'Validation token received' - $body = $request.query.ValidationToken - } else { - Push-OutputBinding -Name QueueWebhook -Value $Request - $Body = 'Webhook Recieved' - $StatusCode = [HttpStatusCode]::OK - } -} else { - $body = 'This webhook is not authorized.' - $StatusCode = [HttpStatusCode]::Forbidden -} - -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = $body - }) diff --git a/PublicWebhooksProcess/function.json b/PublicWebhooksProcess/function.json deleted file mode 100644 index d358059b9e50..000000000000 --- a/PublicWebhooksProcess/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "webhooksqueue" - } - ] -} diff --git a/PublicWebhooksProcess/run.ps1 b/PublicWebhooksProcess/run.ps1 deleted file mode 100644 index 50bbde87cfb2..000000000000 --- a/PublicWebhooksProcess/run.ps1 +++ /dev/null @@ -1,79 +0,0 @@ -using namespace System.Net - -# Input bindings are passed in via param block. -param($QueueItem, $TriggerMetadata) - -$Request = $QueueItem - -$WebhookTable = Get-CIPPTable -TableName webhookTable -$Webhooks = Get-AzDataTableEntity @WebhookTable -Write-Host 'Received request' -Write-Host "CIPPID: $($request.Query.CIPPID)" -$url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 -Write-Host $url -if ($Request.query.CIPPID -in $Webhooks.RowKey) { - Write-Host 'Found matching CIPPID' - $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID - - if ($Request.Query.Type -eq 'GraphSubscription') { - # Graph Subscriptions - [pscustomobject]$ReceivedItem = $Request.Body.value - Invoke-CippGraphWebhookProcessing -Data $ReceivedItem -CIPPID $request.Query.CIPPID -WebhookInfo $Webhookinfo - - } else { - # Auditlog Subscriptions - try { - foreach ($ReceivedItem In ($Request.body)) { - $ReceivedItem = [pscustomobject]$ReceivedItem - Write-Host "Received Item: $($ReceivedItem | ConvertTo-Json -Depth 15 -Compress))" - $TenantFilter = (Get-Tenants | Where-Object -Property customerId -EQ $ReceivedItem.TenantId).defaultDomainName - Write-Host "Webhook TenantFilter: $TenantFilter" - $ConfigTable = get-cipptable -TableName 'SchedulerConfig' - $Alertconfig = Get-CIPPAzDataTableEntity @ConfigTable | Where-Object { $_.Tenant -eq $TenantFilter -or $_.Tenant -eq 'AllTenants' } - $Operations = ($AlertConfig.if | ConvertFrom-Json -ErrorAction SilentlyContinue).selection + 'UserLoggedIn' - $Webhookinfo = $Webhooks | Where-Object -Property RowKey -EQ $Request.query.CIPPID - #Increased download efficiency: only download the data we need for processing. Todo: Change this to load from table or dynamic source. - $MappingTable = [pscustomobject]@{ - 'UserLoggedIn' = 'Audit.AzureActiveDirectory' - 'Add member to role.' = 'Audit.AzureActiveDirectory' - 'Disable account.' = 'Audit.AzureActiveDirectory' - 'Update StsRefreshTokenValidFrom Timestamp.' = 'Audit.AzureActiveDirectory' - 'Enable account.' = 'Audit.AzureActiveDirectory' - 'Disable Strong Authentication.' = 'Audit.AzureActiveDirectory' - 'Reset user password.' = 'Audit.AzureActiveDirectory' - 'Add service principal.' = 'Audit.AzureActiveDirectory' - 'HostedIP' = 'Audit.AzureActiveDirectory' - 'badRepIP' = 'Audit.AzureActiveDirectory' - 'UserLoggedInFromUnknownLocation' = 'Audit.AzureActiveDirectory' - 'customfield' = 'AnyLog' - 'anyAlert' = 'AnyLog' - 'New-InboxRule' = 'Audit.Exchange' - 'Set-InboxRule' = 'Audit.Exchange' - } - #Compare $Operations to $MappingTable. If there is a match, we make a new variable called $LogsToDownload - #Example: $Operations = 'UserLoggedIn', 'Set-InboxRule' makes : $LogsToDownload = @('Audit.AzureActiveDirectory',Audit.Exchange) - $LogsToDownload = $Operations | Where-Object { $MappingTable.$_ } | ForEach-Object { $MappingTable.$_ } - Write-Host "Our operations: $Operations" - Write-Host "Logs to download: $LogsToDownload" - if ($ReceivedItem.ContentType -in $LogsToDownload -or 'AnyLog' -in $LogsToDownload) { - $Data = New-GraphPostRequest -type GET -uri "https://manage.office.com/api/v1.0/$($ReceivedItem.tenantId)/activity/feed/audit/$($ReceivedItem.contentid)" -tenantid $TenantFilter -scope 'https://manage.office.com/.default' - } else { - Write-Host "No data to download for $($ReceivedItem.ContentType)" - continue - } - Write-Host "Data found: $($data.count) items" - $DataToProcess = if ('anylog' -NotIn $LogsToDownload) { $Data | Where-Object -Property Operation -In $Operations } else { $Data } - Write-Host "Data to process found: $($DataToProcess.count) items" - foreach ($Item in $DataToProcess) { - Write-Host "Processing $($item.operation)" - Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url - } - } - } catch { - Write-Host "Webhook Failed: $($_.Exception.Message). Line number $($_.InvocationInfo.ScriptLineNumber)" - } - } - -} else { - Write-Host 'Unauthorised Webhook' -} diff --git a/Scheduler_GetWebhooks/function.json b/Scheduler_GetWebhooks/function.json new file mode 100644 index 000000000000..f30537d11b34 --- /dev/null +++ b/Scheduler_GetWebhooks/function.json @@ -0,0 +1,15 @@ +{ + "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 new file mode 100644 index 000000000000..9b890b878588 --- /dev/null +++ b/Scheduler_GetWebhooks/run.ps1 @@ -0,0 +1,13 @@ +param($Timer) + +$Table = Get-CIPPTable -TableName WebhookIncoming +$Webhooks = Get-CIPPAzDataTableEntity @Table +$InputObject = [PSCustomObject]@{ + OrchestratorName = 'WebhookOrchestrator' + Batch = @($Webhooks) + SkipLog = $true +} +#Write-Host ($InputObject | ConvertTo-Json) +$InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) +Write-Host "Started orchestration with ID = '$InstanceId'" +#$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId \ No newline at end of file From 06e7763f3413edbc2eae6f55afa955830e998250 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 13 Mar 2024 16:41:36 -0400 Subject: [PATCH 32/45] Scheduler Durable --- ExecScheduledCommand/function.json | 10 --- ExecScheduledCommand/run.ps1 | 83 ------------------ .../Entrypoints/Push-ExecScheduledCommand.ps1 | 85 +++++++++++++++++++ Modules/CippEntrypoints/CippEntrypoints.psm1 | 4 +- Scheduler_UserTasks/function.json | 7 +- Scheduler_UserTasks/run.ps1 | 32 ++++--- 6 files changed, 112 insertions(+), 109 deletions(-) delete mode 100644 ExecScheduledCommand/function.json delete mode 100644 ExecScheduledCommand/run.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-ExecScheduledCommand.ps1 diff --git a/ExecScheduledCommand/function.json b/ExecScheduledCommand/function.json deleted file mode 100644 index e4c27b23b985..000000000000 --- a/ExecScheduledCommand/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "scheduledcommandprocessor" - } - ] -} diff --git a/ExecScheduledCommand/run.ps1 b/ExecScheduledCommand/run.ps1 deleted file mode 100644 index d0031f771c5c..000000000000 --- a/ExecScheduledCommand/run.ps1 +++ /dev/null @@ -1,83 +0,0 @@ -# Input bindings are passed in via param block. -param($QueueItem, $TriggerMetadata) - -$Table = Get-CippTable -tablename 'ScheduledTasks' -$task = $QueueItem.TaskInfo -$commandParameters = $QueueItem.Parameters - -$tenant = $QueueItem.Parameters['TenantFilter'] -Write-Host 'started task' -try { - try { - $results = & $QueueItem.command @commandParameters - } catch { - $results = "Task Failed: $($_.Exception.Message)" - - } - - Write-Host 'ran the command' - if ($results -is [String]) { - $results = @{ Results = $results } - } - if ($results -is [array] -and $results[0] -is [string]) { - $results = $results | Where-Object { $_ -is [string] } - $results = $results | ForEach-Object { @{ Results = $_ } } - } - - $results = $results | Select-Object * -ExcludeProperty RowKey, PartitionKey - - $StoredResults = $results | ConvertTo-Json -Compress -Depth 20 | Out-String - if ($StoredResults.Length -gt 64000 -or $task.Tenant -eq 'AllTenants') { - $StoredResults = @{ Results = 'The results for this query are too long to store in this table, or the query was meant for All Tenants. Please use the options to send the results to another target to be able to view the results. ' } | ConvertTo-Json -Compress - } -} catch { - $errorMessage = $_.Exception.Message - if ($task.Recurrence -gt 0) { $State = 'Failed - Planned' } else { $State = 'Failed' } - Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - Results = "$errorMessage" - TaskState = $State - } - Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Failed to execute task $($task.Name): $errorMessage" -sev Error -} - - -$TableDesign = '' -$HTML = ($results | Select-Object * -ExcludeProperty RowKey, PartitionKey | ConvertTo-Html -Fragment) -replace '', "$TableDesign
" | Out-String -$title = "Scheduled Task $($task.Name) - $($task.ExpectedRunTime)" -Write-Host $title -switch -wildcard ($task.PostExecution) { - '*psa*' { Send-CIPPAlert -Type 'psa' -Title $title -HTMLContent $HTML } - '*email*' { Send-CIPPAlert -Type 'email' -Title $title -HTMLContent $HTML } - '*webhook*' { - $Webhook = [PSCustomObject]@{ - 'Tenant' = $tenant - 'TaskInfo' = $QueueItem.TaskInfo - 'Results' = $Results - } - Send-CIPPAlert -Type 'webhook' -Title $title -JSONContent $($Webhook | ConvertTo-Json -Depth 20) - } -} - -Write-Host 'ran the command' - -if ($task.Recurrence -le '0' -or $task.Recurrence -eq $null) { - Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - Results = "$StoredResults" - TaskState = 'Completed' - } -} else { - $nextRun = (Get-Date).AddDays($task.Recurrence) - $nextRunUnixTime = [int64]($nextRun - (Get-Date '1/1/1970')).TotalSeconds - Update-AzDataTableEntity @Table -Entity @{ - PartitionKey = $task.PartitionKey - RowKey = $task.RowKey - Results = "$StoredResults" - TaskState = 'Planned' - ScheduledTime = "$nextRunUnixTime" - } -} -Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Successfully executed task: $($task.name)" -sev Info \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecScheduledCommand.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ExecScheduledCommand.ps1 new file mode 100644 index 000000000000..8e53dcbd8bd4 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ExecScheduledCommand.ps1 @@ -0,0 +1,85 @@ +function Push-ExecScheduledCommand { + # Input bindings are passed in via param block. + param($Item) + + $Table = Get-CippTable -tablename 'ScheduledTasks' + $task = $Item.TaskInfo + $commandParameters = $Item.Parameters + + $tenant = $Item.Parameters['TenantFilter'] + Write-Host 'started task' + try { + try { + $results = & $Item.command @commandParameters + } catch { + $results = "Task Failed: $($_.Exception.Message)" + + } + + Write-Host 'ran the command' + if ($results -is [String]) { + $results = @{ Results = $results } + } + if ($results -is [array] -and $results[0] -is [string]) { + $results = $results | Where-Object { $_ -is [string] } + $results = $results | ForEach-Object { @{ Results = $_ } } + } + + $results = $results | Select-Object * -ExcludeProperty RowKey, PartitionKey + + $StoredResults = $results | ConvertTo-Json -Compress -Depth 20 | Out-String + if ($StoredResults.Length -gt 64000 -or $task.Tenant -eq 'AllTenants') { + $StoredResults = @{ Results = 'The results for this query are too long to store in this table, or the query was meant for All Tenants. Please use the options to send the results to another target to be able to view the results. ' } | ConvertTo-Json -Compress + } + } catch { + $errorMessage = $_.Exception.Message + if ($task.Recurrence -gt 0) { $State = 'Failed - Planned' } else { $State = 'Failed' } + Update-AzDataTableEntity @Table -Entity @{ + PartitionKey = $task.PartitionKey + RowKey = $task.RowKey + Results = "$errorMessage" + TaskState = $State + } + Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Failed to execute task $($task.Name): $errorMessage" -sev Error + } + + + $TableDesign = '' + $HTML = ($results | Select-Object * -ExcludeProperty RowKey, PartitionKey | ConvertTo-Html -Fragment) -replace '
', "$TableDesign
" | Out-String + $title = "Scheduled Task $($task.Name) - $($task.ExpectedRunTime)" + Write-Host $title + switch -wildcard ($task.PostExecution) { + '*psa*' { Send-CIPPAlert -Type 'psa' -Title $title -HTMLContent $HTML } + '*email*' { Send-CIPPAlert -Type 'email' -Title $title -HTMLContent $HTML } + '*webhook*' { + $Webhook = [PSCustomObject]@{ + 'Tenant' = $tenant + 'TaskInfo' = $Item.TaskInfo + 'Results' = $Results + } + Send-CIPPAlert -Type 'webhook' -Title $title -JSONContent $($Webhook | ConvertTo-Json -Depth 20) + } + } + + Write-Host 'ran the command' + + if ($task.Recurrence -le '0' -or $task.Recurrence -eq $null) { + Update-AzDataTableEntity @Table -Entity @{ + PartitionKey = $task.PartitionKey + RowKey = $task.RowKey + Results = "$StoredResults" + TaskState = 'Completed' + } + } else { + $nextRun = (Get-Date).AddDays($task.Recurrence) + $nextRunUnixTime = [int64]($nextRun - (Get-Date '1/1/1970')).TotalSeconds + Update-AzDataTableEntity @Table -Entity @{ + PartitionKey = $task.PartitionKey + RowKey = $task.RowKey + Results = "$StoredResults" + TaskState = 'Planned' + ScheduledTime = "$nextRunUnixTime" + } + } + Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Successfully executed task: $($task.name)" -sev Info +} \ No newline at end of file diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 749a0a41a674..15a25671cc9c 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -66,7 +66,7 @@ function Receive-CippOrchestrationTrigger { #Write-Host ($OrchestratorInput | ConvertTo-Json -Depth 10) $RetryOptions = New-DurableRetryOptions @DurableRetryOptions - if ($Context.IsReplaying -ne $true -and $Context.Input.SkipLog -ne $true) { + if ($Context.IsReplaying -ne $true -and $OrchestratorInput.SkipLog -ne $true) { Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $OrchestratorInput.TenantFilter -message "Started $($OrchestratorInput.OrchestratorName)" -sev info } @@ -82,7 +82,7 @@ function Receive-CippOrchestrationTrigger { } } - if ($Context.IsReplaying -ne $true -and $Context.Input.SkipLog -ne $true) { + if ($Context.IsReplaying -ne $true -and $OrchestratorInput.SkipLog -ne $true) { Write-LogMessage -API $OrchestratorInput.OrchestratorName -tenant $tenant -message "Finished $($OrchestratorInput.OrchestratorName)" -sev Info } } catch { diff --git a/Scheduler_UserTasks/function.json b/Scheduler_UserTasks/function.json index de2a7380d759..f7af84092121 100644 --- a/Scheduler_UserTasks/function.json +++ b/Scheduler_UserTasks/function.json @@ -7,10 +7,9 @@ "type": "timerTrigger" }, { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "scheduledcommandprocessor" + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/Scheduler_UserTasks/run.ps1 b/Scheduler_UserTasks/run.ps1 index 802d4624473f..8ad06065a2fa 100644 --- a/Scheduler_UserTasks/run.ps1 +++ b/Scheduler_UserTasks/run.ps1 @@ -3,12 +3,12 @@ param($Timer) $Table = Get-CippTable -tablename 'ScheduledTasks' $Filter = "TaskState eq 'Planned' or TaskState eq 'Failed - Planned'" $tasks = Get-CIPPAzDataTableEntity @Table -Filter $Filter -foreach ($task in $tasks) { +$Batch = foreach ($task in $tasks) { $tenant = $task.Tenant $currentUnixTime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds if ($currentUnixTime -ge $task.ScheduledTime) { try { - Update-AzDataTableEntity @Table -Entity @{ + $null = Update-AzDataTableEntity @Table -Entity @{ PartitionKey = $task.PartitionKey RowKey = $task.RowKey ExecutedTime = "$currentUnixTime" @@ -19,25 +19,27 @@ foreach ($task in $tasks) { if (!$task.Parameters) { $task.Parameters = @{} } $ScheduledCommand = [pscustomobject]@{ - Command = $task.Command - Parameters = $task.Parameters - TaskInfo = $task + Command = $task.Command + Parameters = $task.Parameters + TaskInfo = $task + FunctionName = 'ExecScheduledCommand' } if ($task.Tenant -eq 'AllTenants') { - $Results = Get-Tenants | ForEach-Object { + Get-Tenants | ForEach-Object { $ScheduledCommand.Parameters['TenantFilter'] = $_.defaultDomainName - Push-OutputBinding -Name Msg -Value $ScheduledCommand + $ScheduledCommand + #Push-OutputBinding -Name Msg -Value $ScheduledCommand } } else { $ScheduledCommand.Parameters['TenantFilter'] = $task.Tenant - $Results = Push-OutputBinding -Name Msg -Value $ScheduledCommand + $ScheduledCommand + #$Results = Push-OutputBinding -Name Msg -Value $ScheduledCommand } - } catch { $errorMessage = $_.Exception.Message - Update-AzDataTableEntity @Table -Entity @{ + $null = Update-AzDataTableEntity @Table -Entity @{ PartitionKey = $task.PartitionKey RowKey = $task.RowKey Results = "$errorMessage" @@ -47,4 +49,14 @@ foreach ($task in $tasks) { Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Failed to execute task $($task.Name): $errorMessage" -sev Error } } +} +if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'UserTaskOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started orchestration with ID = '$InstanceId'" } \ No newline at end of file From b7b29fae2fe946d744a4230f38d7b49fe3732042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Bentsen=20Kj=C3=A6rg=C3=A5rd=20=28KBK=29?= Date: Thu, 14 Mar 2024 11:54:24 +0100 Subject: [PATCH 33/45] Some error handling --- .../Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 | 6 ++++-- .../Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 | 3 +-- .../Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 index 07571db760aa..412cc1103f8a 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertApnCertExpiry.ps1 @@ -17,7 +17,9 @@ function Push-CIPPAlertApnCertExpiry { if ($Apn.expirationDateTime -lt (Get-Date).AddDays(30) -and $Apn.expirationDateTime -gt (Get-Date).AddDays(-7)) { Write-AlertMessage -tenant $($QueueItem.tenant) -message ('Intune: Apple Push Notification certificate for {0} is expiring on {1}' -f $Apn.appleIdentifier, $Apn.expirationDateTime) } - } catch {} + } catch { + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Failed to check APN certificate expiry for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" + } } $LastRun = @{ RowKey = 'ApnCertExpiry' @@ -25,6 +27,6 @@ function Push-CIPPAlertApnCertExpiry { } Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } catch { - # Error handling + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Failed to check APN certificate expiry for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 index 82bcde9bb74f..46c13638e956 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertAppSecretExpiry.ps1 @@ -6,7 +6,6 @@ function Push-CIPPAlertAppSecretExpiry { $TriggerMetadata ) $LastRunTable = Get-CIPPTable -Table AlertLastRun - try { $Filter = "RowKey eq 'AppSecretExpiry' and PartitionKey eq '{0}'" -f $QueueItem.tenantid @@ -34,7 +33,7 @@ function Push-CIPPAlertAppSecretExpiry { Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } } catch { - + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Failed to check App registration expiry for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 index 804750e60705..e8f15d6bbcce 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-CIPPAlertDepTokenExpiry.ps1 @@ -27,6 +27,6 @@ function Push-CIPPAlertDepTokenExpiry { Add-CIPPAzDataTableEntity @LastRunTable -Entity $LastRun -Force } } catch { - # Error handling + Write-AlertMessage -tenant $($QueueItem.tenant) -message "Failed to check Apple Device Enrollment Program token expiry for $($QueueItem.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } From d321b8de4926d43e38176aefc2efb77a0944d9a3 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Mar 2024 10:04:45 -0400 Subject: [PATCH 34/45] CPV Durable Cleanup queue output bindings --- .../Entrypoints/Push-UpdatePermissionsQueue.ps1 | 17 +++++++++++++++++ Scheduler_GetQueue/function.json | 6 ------ Scheduler_Standards/function.json | 6 ------ UpdatePermissions/function.json | 7 +++---- UpdatePermissions/run.ps1 | 17 +++++++++++++---- UpdatePermissionsQueue/function.json | 10 ---------- UpdatePermissionsQueue/run.ps1 | 15 --------------- 7 files changed, 33 insertions(+), 45 deletions(-) create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-UpdatePermissionsQueue.ps1 delete mode 100644 UpdatePermissionsQueue/function.json delete mode 100644 UpdatePermissionsQueue/run.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-UpdatePermissionsQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-UpdatePermissionsQueue.ps1 new file mode 100644 index 000000000000..e1d72b14e867 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-UpdatePermissionsQueue.ps1 @@ -0,0 +1,17 @@ +function Push-UpdatePermissionsQueue { + # Input bindings are passed in via param block. + param($Item) + Write-Host "Applying permissions for $($Item.defaultDomainName)" + $Table = Get-CIPPTable -TableName cpvtenants + $CPVRows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Tenant -EQ $Item.customerId + if (!$CPVRows -or $ENV:ApplicationID -notin $CPVRows.applicationId) { + Write-LogMessage -tenant $Item.defaultDomainName -tenantId $Item.customerId -message 'A New tenant has been added, or a new CIPP-SAM Application is in use' -Sev 'Warn' -API 'NewTenant' + Write-Host 'Adding CPV permissions' + Set-CIPPCPVConsent -Tenantfilter $Item.defaultDomainName + } + + Add-CIPPApplicationPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $Item.defaultDomainName + Add-CIPPDelegatedPermission -RequiredResourceAccess 'CippDefaults' -ApplicationId $ENV:ApplicationID -tenantfilter $Item.defaultDomainName + + Write-LogMessage -tenant $Item.defaultDomainName -tenantId $Item.customerId -message "Updated permissions for $($Item.defaultDomainName)" -Sev 'Info' -API 'UpdatePermissionsQueue' +} \ No newline at end of file diff --git a/Scheduler_GetQueue/function.json b/Scheduler_GetQueue/function.json index 122f86c71d70..56e4cf0cfda1 100644 --- a/Scheduler_GetQueue/function.json +++ b/Scheduler_GetQueue/function.json @@ -6,12 +6,6 @@ "direction": "in", "type": "timerTrigger" }, - { - "type": "queue", - "direction": "out", - "name": "QueueItem", - "queueName": "CIPPGenericQueue" - }, { "name": "starter", "type": "durableClient", diff --git a/Scheduler_Standards/function.json b/Scheduler_Standards/function.json index 81d53b9a1598..e071591357a0 100644 --- a/Scheduler_Standards/function.json +++ b/Scheduler_Standards/function.json @@ -6,12 +6,6 @@ "direction": "in", "type": "timerTrigger" }, - { - "type": "queue", - "direction": "out", - "name": "QueueItem", - "queueName": "CIPPGenericQueue" - }, { "name": "starter", "type": "durableClient", diff --git a/UpdatePermissions/function.json b/UpdatePermissions/function.json index a7fdc6ca8da8..7e97fe568d29 100644 --- a/UpdatePermissions/function.json +++ b/UpdatePermissions/function.json @@ -7,10 +7,9 @@ "schedule": "0 0 0 * * *" }, { - "type": "queue", - "direction": "out", - "name": "Msg", - "queueName": "cpvqueue" + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/UpdatePermissions/run.ps1 b/UpdatePermissions/run.ps1 index 5707bb45734a..03d3c0e1cc41 100644 --- a/UpdatePermissions/run.ps1 +++ b/UpdatePermissions/run.ps1 @@ -1,7 +1,16 @@ # Input bindings are passed in via param block. param($Timer) -$Tenants = get-tenants -IncludeErrors | Where-Object { $_.customerId -ne $env:TenantId } -foreach ($Row in $Tenants) { - Push-OutputBinding -Name Msg -Value $row -} \ No newline at end of file +try { + $Tenants = Get-Tenants -IncludeErrors | Where-Object { $_.customerId -ne $env:TenantId } | ForEach-Object { $_ | Add-Member -NotePropertyName FunctionName -NotePropertyValue 'UpdatePermissionsQueue'; $_ } + + if (($Tenants | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'UpdatePermissionsOrchestrator' + Batch = @($Tenants) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + } +} catch {} \ No newline at end of file diff --git a/UpdatePermissionsQueue/function.json b/UpdatePermissionsQueue/function.json deleted file mode 100644 index 7fc4f12cef56..000000000000 --- a/UpdatePermissionsQueue/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "cpvqueue" - } - ] -} diff --git a/UpdatePermissionsQueue/run.ps1 b/UpdatePermissionsQueue/run.ps1 deleted file mode 100644 index 9b147478e274..000000000000 --- a/UpdatePermissionsQueue/run.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -# Input bindings are passed in via param block. -param($QueueItem, $TriggerMetadata) -Write-Host "Applying permissions for $($QueueItem.defaultDomainName)" -$Table = Get-CIPPTable -TableName cpvtenants -$CPVRows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Tenant -EQ $QueueItem.customerId -if (!$CPVRows -or $ENV:ApplicationID -notin $CPVRows.applicationId) { - Write-LogMessage -tenant $queueitem.defaultDomainName -tenantId $queueitem.customerId -message "A New tenant has been added, or a new CIPP-SAM Application is in use" -Sev "Warn" -API "NewTenant" - Write-Host "Adding CPV permissions" - Set-CIPPCPVConsent -Tenantfilter $QueueItem.defaultDomainName -} - -Add-CIPPApplicationPermission -RequiredResourceAccess "CippDefaults" -ApplicationId $ENV:ApplicationID -tenantfilter $QueueItem.defaultDomainName -Add-CIPPDelegatedPermission -RequiredResourceAccess "CippDefaults" -ApplicationId $ENV:ApplicationID -tenantfilter $QueueItem.defaultDomainName - -Write-LogMessage -tenant $QueueItem.defaultDomainName -tenantId $queueitem.customerId -message "Updated permissions for $($QueueItem.defaultDomainName)" -Sev "Info" -API "UpdatePermissionsQueue" \ No newline at end of file From dbc431a84cf5c07552fd3eb5c5f0ebf9b55b36e9 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Mar 2024 11:34:13 -0400 Subject: [PATCH 35/45] GDAP Invite durable --- ExecGDAPInviteApproved_Timer/function.json | 7 +++---- ExecGDAPInviteQueue/function.json | 10 ---------- ExecGDAPInviteQueue/run.ps1 | 7 ------- .../Entrypoints/Push-ExecGDAPInviteQueue.ps1 | 9 +++++++++ .../CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 | 15 +++++++++++++-- 5 files changed, 25 insertions(+), 23 deletions(-) delete mode 100644 ExecGDAPInviteQueue/function.json delete mode 100644 ExecGDAPInviteQueue/run.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-ExecGDAPInviteQueue.ps1 diff --git a/ExecGDAPInviteApproved_Timer/function.json b/ExecGDAPInviteApproved_Timer/function.json index 32b454a2a015..f8904bbb0a7f 100644 --- a/ExecGDAPInviteApproved_Timer/function.json +++ b/ExecGDAPInviteApproved_Timer/function.json @@ -7,10 +7,9 @@ "schedule": "0 0 */3 * * *" }, { - "type": "queue", - "direction": "out", - "name": "gdapinvitequeue", - "queueName": "gdapinvitequeue" + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/ExecGDAPInviteQueue/function.json b/ExecGDAPInviteQueue/function.json deleted file mode 100644 index e51e66299d6f..000000000000 --- a/ExecGDAPInviteQueue/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "gdapinvitequeue" - } - ] -} diff --git a/ExecGDAPInviteQueue/run.ps1 b/ExecGDAPInviteQueue/run.ps1 deleted file mode 100644 index 1b95a443bab0..000000000000 --- a/ExecGDAPInviteQueue/run.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -# Input bindings are passed in via param block. -param( $QueueItem, $TriggerMetadata) - -# Write out the queue message and metadata to the information log. -Write-Host "PowerShell queue trigger function processed work item: $($QueueItem.customer.displayName)" - -Set-CIPPGDAPInviteGroups -Relationship $QueueItem \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ExecGDAPInviteQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ExecGDAPInviteQueue.ps1 new file mode 100644 index 000000000000..833b498eb34e --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ExecGDAPInviteQueue.ps1 @@ -0,0 +1,9 @@ +function Push-ExecGDAPInviteQueue { + # Input bindings are passed in via param block. + param($Item) + + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell queue trigger function processed work item: $($Item.customer.displayName)" + + Set-CIPPGDAPInviteGroups -Relationship $Item +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 b/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 index 407eccddc796..e2ae2dba708d 100644 --- a/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 @@ -35,12 +35,23 @@ function Set-CIPPGDAPInviteGroups { if (($InviteList | Measure-Object).Count -gt 0) { $Activations = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships?`$filter=status eq 'active'" - foreach ($Activation in $Activations) { + $Batch = foreach ($Activation in $Activations) { if ($InviteList.RowKey -contains $Activation.id) { Write-Host "Mapping groups for GDAP relationship: $($Activation.customer.displayName) - $($Activation.id)" - Push-OutputBinding -Name gdapinvitequeue -Value $Activation + $Activation | Add-Member -NotePropertyName FunctionName -NotePropertyValue 'ExecGDAPInviteQueue' + $Activation } } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'GDAPInviteOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started GDAP Invite orchestration with ID = '$InstanceId'" + } } } } From 75fa35998f987c55457b2acaa2f4d3056c93115b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Mar 2024 11:52:50 -0400 Subject: [PATCH 36/45] Cleanup webhook entry in finally block --- .../Public/Entrypoints/Push-PublicWebhookProcess.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-PublicWebhookProcess.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-PublicWebhookProcess.ps1 index 99a932d35e79..4d321a825f4e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Push-PublicWebhookProcess.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Push-PublicWebhookProcess.ps1 @@ -7,11 +7,11 @@ function Push-PublicWebhookProcess { } elseif ($Item.Type -eq 'AuditLog') { Invoke-CippWebhookProcessing -TenantFilter $Item.TenantFilter -Data ($Item.Data | ConvertFrom-Json) -CIPPPURL $Item.CIPPURL } - $WebhookIncoming = Get-CIPPTable -TableName WebhookIncoming - $Entity = $Item | Select-Object -Property RowKey, PartitionKey - Remove-AzDataTableEntity @WebhookIncoming -Entity $Entity } catch { Write-Host "Webhook Exception: $($_.Exception.Message)" + } finally { + $WebhookIncoming = Get-CIPPTable -TableName WebhookIncoming + $Entity = $Item | Select-Object -Property RowKey, PartitionKey + Remove-AzDataTableEntity @WebhookIncoming -Entity $Entity } - } \ No newline at end of file From 4b583940162aced85facb4d321947e014be8c044 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Mar 2024 12:27:40 -0400 Subject: [PATCH 37/45] Licenses & Mailbox Rules durables Remove outer parallel loop --- ListLicensesAllTenants/function.json | 10 ---- ListLicensesAllTenants/run.ps1 | 28 --------- .../Entrypoints/Invoke-ListLicenses.ps1 | 18 +++++- .../Invoke-ListLicensesAllTenants.ps1 | 35 ----------- .../Entrypoints/Invoke-ListMailboxRules.ps1 | 24 +++++++- .../Invoke-ListMailboxRulesAllTenants.ps1 | 60 ------------------- .../Entrypoints/Push-ListLicensesQueue.ps1 | 22 +++++++ .../Push-ListMailboxRulesQueue.ps1 | 53 ++++++++++++++++ Z_CIPPHttpTrigger/function.json | 12 ---- 9 files changed, 113 insertions(+), 149 deletions(-) delete mode 100644 ListLicensesAllTenants/function.json delete mode 100644 ListLicensesAllTenants/run.ps1 delete mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 delete mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-ListLicensesQueue.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-ListMailboxRulesQueue.ps1 diff --git a/ListLicensesAllTenants/function.json b/ListLicensesAllTenants/function.json deleted file mode 100644 index eee973c7ede2..000000000000 --- a/ListLicensesAllTenants/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "licqueue" - } - ] -} diff --git a/ListLicensesAllTenants/run.ps1 b/ListLicensesAllTenants/run.ps1 deleted file mode 100644 index abc69465c094..000000000000 --- a/ListLicensesAllTenants/run.ps1 +++ /dev/null @@ -1,28 +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" - -$RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module '.\Modules\AzBobbyTables' - Import-Module '.\Modules\CIPPCore' - try { - Write-Host "Processing $domainName" - Get-CIPPLicenseOverview -TenantFilter $domainName - } - catch { - [pscustomobject]@{ - Tenant = [string]$domainName - License = "Could not connect to client: $($_.Exception.Message)" - 'PartitionKey' = 'License' - 'RowKey' = "$($domainName)-$(New-Guid)" - } - } -} - -$Table = Get-CIPPTable -TableName cachelicenses -foreach ($GraphRequest in $RawGraphRequest) { - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null -} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 index 03a719a7f259..6870b020be6e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicenses.ps1 @@ -18,7 +18,7 @@ Function Invoke-ListLicenses { # Interact with query parameters or the body of the request. $TenantFilter = $Request.Query.TenantFilter $RawGraphRequest = if ($TenantFilter -ne 'AllTenants') { - $GraphRequest = Get-CIPPLicenseOverview -TenantFilter $TenantFilter | ForEach-Object { + $GraphRequest = Get-CIPPLicenseOverview -TenantFilter $TenantFilter | ForEach-Object { $TermInfo = $_.TermInfo | ConvertFrom-Json -ErrorAction SilentlyContinue $_.TermInfo = $TermInfo $_ @@ -27,13 +27,25 @@ Function Invoke-ListLicenses { $Table = Get-CIPPTable -TableName cachelicenses $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1) if (!$Rows) { - Push-OutputBinding -Name LicenseQueue -Value (Get-Date).ToString() + #Push-OutputBinding -Name LicenseQueue -Value (Get-Date).ToString() $GraphRequest = [PSCustomObject]@{ Tenant = 'Loading data for all tenants. Please check back in 1 minute' License = 'Loading data for all tenants. Please check back in 1 minute' } + $Tenants = Get-Tenants -IncludeErrors | ForEach-Object { $_ | Add-Member -NotePropertyName FunctionName -NotePropertyValue 'ListLicensesQueue'; $_ } + + if (($Tenants | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'ListLicensesOrchestrator' + Batch = @($Tenants) + SkipLog = $true + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + } } else { - $GraphRequest = $Rows | ForEach-Object { + $GraphRequest = $Rows | ForEach-Object { $TermInfo = $_.TermInfo | ConvertFrom-Json -ErrorAction SilentlyContinue $_.TermInfo = $TermInfo $_ diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 deleted file mode 100644 index 7d6c25d9daa7..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLicensesAllTenants.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -using namespace System.Net - -Function Invoke-ListLicensesAllTenants { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - - $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - - Import-Module '.\Modules\AzBobbyTables' - Import-Module '.\Modules\CIPPCore' - try { - Write-Host "Processing $domainName" - Get-CIPPLicenseOverview -TenantFilter $domainName - } catch { - [pscustomobject]@{ - Tenant = [string]$domainName - License = "Could not connect to client: $($_.Exception.Message)" - 'PartitionKey' = 'License' - 'RowKey' = "$($domainName)-$(New-Guid)" - } - } - } - - $Table = Get-CIPPTable -TableName cachelicenses - foreach ($GraphRequest in $RawGraphRequest) { - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 index a4ca980ba7d3..d1de06beada8 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRules.ps1 @@ -19,14 +19,36 @@ Function Invoke-ListMailboxRules { $TenantFilter = $Request.Query.TenantFilter $Table = Get-CIPPTable -TableName cachembxrules + if ($TenantFilter -ne 'AllTenants') { + $Table.Filter = "Tenant eq '$TenantFilter'" + } $Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).Addhours(-1) if (!$Rows) { - Push-OutputBinding -Name mbxrulequeue -Value $TenantFilter + #Push-OutputBinding -Name mbxrulequeue -Value $TenantFilter $GraphRequest = [PSCustomObject]@{ Tenant = 'Loading data. Please check back in 1 minute' Licenses = 'Loading data. Please check back in 1 minute' } + $Batch = if ($TenantFilter -eq 'AllTenants') { + Get-Tenants -IncludeErrors | ForEach-Object { $_ | Add-Member -NotePropertyName FunctionName -NotePropertyValue 'ListMailboxRulesQueue'; $_ } + } else { + [PSCustomObject]@{ + defaultDomainName = $TenantFilter + FunctionName = 'ListMailboxRulesQueue' + } + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'ListMailboxRulesOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + } + } else { if ($TenantFilter -ne 'AllTenants') { $Rows = $Rows | Where-Object -Property Tenant -EQ $TenantFilter diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 deleted file mode 100644 index b3909976d4c9..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMailboxRulesAllTenants.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -using namespace System.Net - -Function Invoke-ListMailboxRulesAllTenants { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - $Tenants = if ($QueueItem -ne 'AllTenants') { - [PSCustomObject]@{ - defaultDomainName = $QueueItem - } - } else { - Get-Tenants - } - $Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module '.\Modules\CIPPcore' - Import-Module '.\Modules\AzBobbyTables' - - try { - - $Rules = New-ExoRequest -tenantid $domainName -cmdlet 'Get-Mailbox' | ForEach-Object -Parallel { - New-ExoRequest -Anchor $_.UserPrincipalName -tenantid $domainName -cmdlet 'Get-InboxRule' -cmdParams @{Mailbox = $_.GUID } - } - foreach ($Rule in $Rules) { - $GraphRequest = @{ - Rules = [string]($Rule | ConvertTo-Json) - RowKey = [string](New-Guid).guid - Tenant = [string]$domainName - PartitionKey = 'mailboxrules' - } - $Table = Get-CIPPTable -TableName cachembxrules - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } - } catch { - $Rules = @{ - Name = "Could not connect to tenant $($_.Exception.message)" - } | ConvertTo-Json - $GraphRequest = @{ - Rules = [string]$Rules - RowKey = [string]$domainName - Tenant = [string]$domainName - - PartitionKey = 'mailboxrules' - } - $Table = Get-CIPPTable -TableName cachembxrules - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } - } - - - - $Table = Get-CIPPTable -TableName cachembxrules - Write-Host "$($GraphRequest.RowKey) - $($GraphRequest.tenant)" - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ListLicensesQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ListLicensesQueue.ps1 new file mode 100644 index 000000000000..f587fa65fe39 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ListLicensesQueue.ps1 @@ -0,0 +1,22 @@ +function Push-ListLicensesQueue { + # Input bindings are passed in via param block. + param($Item) + + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell queue trigger function processed work item: $($Item.defaultDomainName)" + + $domainName = $Item.defaultDomainName + $GraphRequest = try { + Write-Host "Processing $domainName" + Get-CIPPLicenseOverview -TenantFilter $domainName + } catch { + [pscustomobject]@{ + Tenant = [string]$domainName + License = "Could not connect to client: $($_.Exception.Message)" + 'PartitionKey' = 'License' + 'RowKey' = "$($domainName)-$((New-Guid).Guid)" + } + } + $Table = Get-CIPPTable -TableName cachelicenses + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ListMailboxRulesQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ListMailboxRulesQueue.ps1 new file mode 100644 index 000000000000..5c7ba40b2f06 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ListMailboxRulesQueue.ps1 @@ -0,0 +1,53 @@ +function Push-ListMailboxRulesQueue { + # Input bindings are passed in via param block. + param($Item) + + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell queue trigger function processed work item: $($Item.defaultDomainName)" + + $domainName = $Item.defaultDomainName + + $Table = Get-CIPPTable -TableName cachembxrules + try { + $Rules = New-ExoRequest -tenantid $domainName -cmdlet 'Get-Mailbox' -Select 'userPrincipalName,GUID' | ForEach-Object -Parallel { + Import-Module CippCore + $MbxRules = New-ExoRequest -Anchor $_.UserPrincipalName -tenantid $using:domainName -cmdlet 'Get-InboxRule' -cmdParams @{Mailbox = $_.GUID } + foreach ($Rule in $MbxRules) { + $Rule | Add-Member -NotePropertyName 'UserPrincipalName' -NotePropertyValue $_.userPrincipalName + $Rule + } + } + if (($Rules | Measure-Object).Count -gt 0) { + foreach ($Rule in $Rules) { + $GraphRequest = [PSCustomObject]@{ + Rules = [string]($Rule | ConvertTo-Json) + RowKey = [string](New-Guid).guid + Tenant = [string]$domainName + PartitionKey = 'mailboxrules' + } + + } + } else { + $Rules = @{ + Name = 'No rules found' + } | ConvertTo-Json + $GraphRequest = [PSCustomObject]@{ + Rules = [string]$Rules + RowKey = [string]$domainName + Tenant = [string]$domainName + PartitionKey = 'mailboxrules' + } + } + } catch { + $Rules = @{ + Name = "Could not connect to tenant $($_.Exception.message)" + } | ConvertTo-Json + $GraphRequest = [PSCustomObject]@{ + Rules = [string]$Rules + RowKey = [string]$domainName + Tenant = [string]$domainName + PartitionKey = 'mailboxrules' + } + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null +} diff --git a/Z_CIPPHttpTrigger/function.json b/Z_CIPPHttpTrigger/function.json index 90c8dc9ea6af..815789ce62d6 100644 --- a/Z_CIPPHttpTrigger/function.json +++ b/Z_CIPPHttpTrigger/function.json @@ -27,18 +27,6 @@ "name": "Subscription", "queueName": "AlertSubscriptions" }, - { - "type": "queue", - "direction": "out", - "name": "LicenseQueue", - "queueName": "licqueue" - }, - { - "type": "queue", - "direction": "out", - "name": "mbxrulequeue", - "queueName": "mbxrulequeue" - }, { "type": "queue", "direction": "out", From 1b4d937f2bf8d1d2ef2e88198c8dc89799bacf3d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Mar 2024 13:16:57 -0400 Subject: [PATCH 38/45] MFA Report durable Cleanup functions and output bindings --- ListMFAUsersAllTenants/function.json | 10 --- ListMFAUsersAllTenants/run.ps1 | 57 ----------------- ListMailboxRulesAllTenants/function.json | 10 --- ListMailboxRulesAllTenants/run.ps1 | 61 ------------------ .../Entrypoints/Invoke-ListMFAUsers.ps1 | 19 +++++- .../Invoke-ListMFAUsersAllTenants.ps1 | 63 ------------------- .../Entrypoints/Push-ListMFAUsersQueue.ps1 | 50 +++++++++++++++ Z_CIPPHttpTrigger/function.json | 12 ---- 8 files changed, 67 insertions(+), 215 deletions(-) delete mode 100644 ListMFAUsersAllTenants/function.json delete mode 100644 ListMFAUsersAllTenants/run.ps1 delete mode 100644 ListMailboxRulesAllTenants/function.json delete mode 100644 ListMailboxRulesAllTenants/run.ps1 delete mode 100644 Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 create mode 100644 Modules/CIPPCore/Public/Entrypoints/Push-ListMFAUsersQueue.ps1 diff --git a/ListMFAUsersAllTenants/function.json b/ListMFAUsersAllTenants/function.json deleted file mode 100644 index 7bb9da79d405..000000000000 --- a/ListMFAUsersAllTenants/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "mfaqueue" - } - ] -} diff --git a/ListMFAUsersAllTenants/run.ps1 b/ListMFAUsersAllTenants/run.ps1 deleted file mode 100644 index 8ab611e514fe..000000000000 --- a/ListMFAUsersAllTenants/run.ps1 +++ /dev/null @@ -1,57 +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" - - -Write-Information "Item: $QueueItem" -Write-Information ($TriggerMetadata | ConvertTo-Json) - -try { - Update-CippQueueEntry -RowKey $QueueItem -Status 'Running' - - $GraphRequest = Get-Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module '.\modules\CippCore' - $Table = Get-CIPPTable -TableName cachemfa - Try { - $GraphRequest = Get-CIPPMFAState -TenantFilter $domainName -ErrorAction Stop - } - catch { - $GraphRequest = $null - } - if (!$GraphRequest) { - $GraphRequest = @{ - Tenant = [string]$tenantName - UPN = [string]$domainName - AccountEnabled = 'none' - PerUser = [string]'Could not connect to tenant' - MFARegistration = 'none' - CoveredByCA = [string]'Could not connect to tenant' - CoveredBySD = 'none' - RowKey = [string]"$domainName" - PartitionKey = 'users' - } - } - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } -} -catch { - $Table = Get-CIPPTable -TableName cachemfa - $GraphRequest = @{ - Tenant = [string]$tenantName - UPN = [string]$domainName - AccountEnabled = 'none' - PerUser = [string]'Could not connect to tenant' - MFARegistration = 'none' - CoveredByCA = [string]'Could not connect to tenant' - CoveredBySD = 'none' - RowKey = [string]"$domainName" - PartitionKey = 'users' - } - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null -} -finally { - Update-CippQueueEntry -RowKey $QueueItem -Status "Completed" -} diff --git a/ListMailboxRulesAllTenants/function.json b/ListMailboxRulesAllTenants/function.json deleted file mode 100644 index 776ec6ebfdcb..000000000000 --- a/ListMailboxRulesAllTenants/function.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "mbxrulequeue" - } - ] -} diff --git a/ListMailboxRulesAllTenants/run.ps1 b/ListMailboxRulesAllTenants/run.ps1 deleted file mode 100644 index 36b671ad11f6..000000000000 --- a/ListMailboxRulesAllTenants/run.ps1 +++ /dev/null @@ -1,61 +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" - -$Tenants = if ($QueueItem -ne 'AllTenants') { - [PSCustomObject]@{ - defaultDomainName = $QueueItem - } -} else { - Get-Tenants -} -$Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module CippCore - Import-Module AzBobbyTables - $Table = Get-CIPPTable -TableName cachembxrules - try { - $Rules = New-ExoRequest -tenantid $domainName -cmdlet 'Get-Mailbox' -Select 'userPrincipalName,GUID' | ForEach-Object -Parallel { - Import-Module CippCore - $MbxRules = New-ExoRequest -Anchor $_.UserPrincipalName -tenantid $using:domainName -cmdlet 'Get-InboxRule' -cmdParams @{Mailbox = $_.GUID } - foreach ($Rule in $MbxRules) { - $Rule | Add-Member -NotePropertyName 'UserPrincipalName' -NotePropertyValue $_.userPrincipalName - $Rule - } - } - if (($Rules | Measure-Object).Count -gt 0) { - foreach ($Rule in $Rules) { - $GraphRequest = [PSCustomObject]@{ - Rules = [string]($Rule | ConvertTo-Json) - RowKey = [string](New-Guid).guid - Tenant = [string]$domainName - PartitionKey = 'mailboxrules' - } - - } - } else { - $Rules = @{ - Name = 'No rules found' - } | ConvertTo-Json - $GraphRequest = [PSCustomObject]@{ - Rules = [string]$Rules - RowKey = [string]$domainName - Tenant = [string]$domainName - PartitionKey = 'mailboxrules' - } - } - } catch { - $Rules = @{ - Name = "Could not connect to tenant $($_.Exception.message)" - } | ConvertTo-Json - $GraphRequest = [PSCustomObject]@{ - Rules = [string]$Rules - RowKey = [string]$domainName - Tenant = [string]$domainName - PartitionKey = 'mailboxrules' - } - } - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 index 32314d2298d8..99209bfc9c8f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsers.ps1 @@ -24,9 +24,24 @@ Function Invoke-ListMFAUsers { if (!$Rows) { $Queue = New-CippQueueEntry -Name 'MFA Users - All Tenants' -Link '/identity/reports/mfa-report?customerId=AllTenants' Write-Information ($Queue | ConvertTo-Json) - Push-OutputBinding -Name mfaqueue -Value $Queue.RowKey + #Push-OutputBinding -Name mfaqueue -Value $Queue.RowKey $GraphRequest = [PSCustomObject]@{ - UPN = 'Loading data for all tenants. Please check back in 10 minutes' + UPN = 'Loading data for all tenants. Please check back in a few minutes' + } + $Batch = Get-Tenants -IncludeErrors | ForEach-Object { + $_ | Add-Member -NotePropertyName FunctionName -NotePropertyValue 'ListMFAUsersQueue' + $_ | Add-Member -NotePropertyName QueueId -NotePropertyValue $Queue.RowKey + $_ + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'ListMFAUsersOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" } } else { $GraphRequest = $Rows diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 deleted file mode 100644 index 8123a963e1ff..000000000000 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListMFAUsersAllTenants.ps1 +++ /dev/null @@ -1,63 +0,0 @@ -using namespace System.Net - -Function Invoke-ListMFAUsersAllTenants { - <# - .FUNCTIONALITY - Entrypoint - #> - [CmdletBinding()] - param($Request, $TriggerMetadata) - - - - Write-Information "Item: $QueueItem" - Write-Information ($TriggerMetadata | ConvertTo-Json) - - try { - Update-CippQueueEntry -RowKey $QueueItem -Status 'Running' - - $GraphRequest = Get-Tenants | ForEach-Object -Parallel { - $domainName = $_.defaultDomainName - Import-Module '.\modules\CippCore' - Import-Module '.\Modules\AzBobbyTables' - - $Table = Get-CIPPTable -TableName cachemfa - Try { - $GraphRequest = Get-CIPPMFAState -TenantFilter $domainName -ErrorAction Stop - } catch { - $GraphRequest = $null - } - if (!$GraphRequest) { - $GraphRequest = @{ - Tenant = [string]$tenantName - UPN = [string]$domainName - AccountEnabled = 'none' - PerUser = [string]'Could not connect to tenant' - MFARegistration = 'none' - CoveredByCA = [string]'Could not connect to tenant' - CoveredBySD = 'none' - RowKey = [string]"$domainName" - PartitionKey = 'users' - } - } - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } - } catch { - $Table = Get-CIPPTable -TableName cachemfa - $GraphRequest = @{ - Tenant = [string]$tenantName - UPN = [string]$domainName - AccountEnabled = 'none' - PerUser = [string]'Could not connect to tenant' - MFARegistration = 'none' - CoveredByCA = [string]'Could not connect to tenant' - CoveredBySD = 'none' - RowKey = [string]"$domainName" - PartitionKey = 'users' - } - Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null - } finally { - Update-CippQueueEntry -RowKey $QueueItem -Status 'Completed' - } - -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Push-ListMFAUsersQueue.ps1 b/Modules/CIPPCore/Public/Entrypoints/Push-ListMFAUsersQueue.ps1 new file mode 100644 index 000000000000..89e9d1dbd3bd --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Push-ListMFAUsersQueue.ps1 @@ -0,0 +1,50 @@ +function Push-ListMFAUsersQueue { + # Input bindings are passed in via param block. + param($Item) + + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell queue trigger function processed work item: $($Item.defaultDomainName)" + + try { + Update-CippQueueEntry -RowKey $Item.QueueId -Status 'Running' -Name $Item.displayName + $domainName = $Item.defaultDomainName + $Table = Get-CIPPTable -TableName cachemfa + Try { + $GraphRequest = Get-CIPPMFAState -TenantFilter $domainName -ErrorAction Stop + } catch { + $GraphRequest = $null + } + if (!$GraphRequest) { + $GraphRequest = @{ + Tenant = [string]$domainName + UPN = [string]$domainName + AccountEnabled = 'none' + PerUser = [string]'Could not connect to tenant' + MFARegistration = 'none' + CoveredByCA = [string]'Could not connect to tenant' + CoveredBySD = 'none' + RowKey = [string]"$domainName" + PartitionKey = 'users' + } + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + + } catch { + $Table = Get-CIPPTable -TableName cachemfa + $GraphRequest = @{ + Tenant = [string]$domainName + UPN = [string]$domainName + AccountEnabled = 'none' + PerUser = [string]'Could not connect to tenant' + MFARegistration = 'none' + CoveredByCA = [string]'Could not connect to tenant' + CoveredBySD = 'none' + RowKey = [string]"$domainName" + PartitionKey = 'users' + } + Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null + } finally { + Update-CippQueueEntry -RowKey $QueueItem -Status 'Completed' + } + +} \ No newline at end of file diff --git a/Z_CIPPHttpTrigger/function.json b/Z_CIPPHttpTrigger/function.json index 815789ce62d6..f5a94dad93d7 100644 --- a/Z_CIPPHttpTrigger/function.json +++ b/Z_CIPPHttpTrigger/function.json @@ -27,24 +27,12 @@ "name": "Subscription", "queueName": "AlertSubscriptions" }, - { - "type": "queue", - "direction": "out", - "name": "mfaqueue", - "queueName": "mfaqueue" - }, { "type": "queue", "direction": "out", "name": "mailboxstats", "queueName": "generalAllTenantQueue" }, - { - "type": "queue", - "direction": "out", - "name": "listusers", - "queueName": "generalAllTenantQueue" - }, { "type": "queue", "direction": "out", From 850bfdbc0198adaff3dd5632dfc9fa69cd941e58 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Mar 2024 13:21:41 -0400 Subject: [PATCH 39/45] cleanup bindings --- Z_CIPPHttpTrigger/function.json | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Z_CIPPHttpTrigger/function.json b/Z_CIPPHttpTrigger/function.json index f5a94dad93d7..a77f42a0ea97 100644 --- a/Z_CIPPHttpTrigger/function.json +++ b/Z_CIPPHttpTrigger/function.json @@ -51,12 +51,6 @@ "name": "alertqueue", "queueName": "alertqueue" }, - { - "type": "queue", - "direction": "out", - "name": "gdapinvitequeue", - "queueName": "gdapinvitequeue" - }, { "type": "queue", "direction": "out", @@ -75,12 +69,6 @@ "name": "offboardingmailbox", "queueName": "offboardingmailbox" }, - { - "type": "queue", - "direction": "out", - "name": "QueueWebhook", - "queueName": "webhooksqueue" - }, { "name": "starter", "type": "durableClient", From d3cdbeb6ae63cb53f9d5706eb032ecc7762ccb8d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Mar 2024 14:00:28 -0400 Subject: [PATCH 40/45] NinjaOne Durable --- ExecExtensionNinjaOneQueue/function.json | 16 --- ExecExtensionNinjaOneQueue/run.ps1 | 13 -- .../Invoke-ExecExtensionMapping.ps1 | 116 ++++++++++-------- .../Entrypoints/Invoke-ExecExtensionSync.ps1 | 42 +++++-- .../Invoke-NinjaOneExtensionScheduler.ps1 | 109 ++++++++++++++++ .../NinjaOne/Invoke-NinjaOneOrgMapping.ps1 | 47 ++++--- .../NinjaOne/Invoke-NinjaOneSync.ps1 | 25 +++- .../NinjaOne/Push-NinjaOneQueue.ps1 | 15 +++ Scheduler_Extensions/function.json | 5 + Scheduler_Extensions/run.ps1 | 82 +------------ 10 files changed, 272 insertions(+), 198 deletions(-) delete mode 100644 ExecExtensionNinjaOneQueue/function.json delete mode 100644 ExecExtensionNinjaOneQueue/run.ps1 create mode 100644 Modules/CippExtensions/NinjaOne/Invoke-NinjaOneExtensionScheduler.ps1 create mode 100644 Modules/CippExtensions/NinjaOne/Push-NinjaOneQueue.ps1 diff --git a/ExecExtensionNinjaOneQueue/function.json b/ExecExtensionNinjaOneQueue/function.json deleted file mode 100644 index 058a42bd6db9..000000000000 --- a/ExecExtensionNinjaOneQueue/function.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "bindings": [ - { - "name": "QueueItem", - "type": "queueTrigger", - "direction": "in", - "queueName": "NinjaOneQueue" - }, - { - "type": "queue", - "direction": "out", - "name": "NinjaProcess", - "queueName": "NinjaOneQueue" - } - ] -} diff --git a/ExecExtensionNinjaOneQueue/run.ps1 b/ExecExtensionNinjaOneQueue/run.ps1 deleted file mode 100644 index 21720a79b6a5..000000000000 --- a/ExecExtensionNinjaOneQueue/run.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -# Input bindings are passed in via param block. -param($QueueItem, $TriggerMetadata) - -# Write out the queue message and metadata to the information log. -Write-Host "PowerShell NinjaOne queue trigger function processed work item: $($QueueItem.NinjaAction)" - - -Switch ($QueueItem.NinjaAction) { - 'StartAutoMapping' { Invoke-NinjaOneOrgMapping } - 'AutoMapTenant' { Invoke-NinjaOneOrgMappingTenant -QueueItem $QueueItem } - 'SyncTenant' { Invoke-NinjaOneTenantSync -QueueItem $QueueItem } - 'SyncTenants' {Invoke-NinjaOneSync} -} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 index 1e4c8c8677e3..f35e5a38c94f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionMapping.ps1 @@ -1,6 +1,6 @@ - using namespace System.Net +using namespace System.Net - Function Invoke-ExecExtensionMapping { +Function Invoke-ExecExtensionMapping { <# .FUNCTIONALITY Entrypoint @@ -8,74 +8,82 @@ [CmdletBinding()] param($Request, $TriggerMetadata) - $APIName = $TriggerMetadata.FunctionName -Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + $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 CippMapping + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $Table = Get-CIPPTable -TableName CippMapping -if ($Request.Query.List) { - switch ($Request.Query.List) { - 'Halo' { - $body = Get-HaloMapping -CIPPMapping $Table - } - - 'NinjaOrgs' { - $Body = Get-NinjaOneOrgMapping -CIPPMapping $Table - } - - 'NinjaFields' { - $Body = Get-NinjaOneFieldMapping -CIPPMapping $Table - - } - } -} - -try { - if ($Request.Query.AddMapping) { - switch ($Request.Query.AddMapping) { + if ($Request.Query.List) { + switch ($Request.Query.List) { 'Halo' { - $body = Set-HaloMapping -CIPPMapping $Table -APIName $APIName -Request $Request + $body = Get-HaloMapping -CIPPMapping $Table } - + 'NinjaOrgs' { - $Body = Set-NinjaOneOrgMapping -CIPPMapping $Table -APIName $APIName -Request $Request + $Body = Get-NinjaOneOrgMapping -CIPPMapping $Table } - + 'NinjaFields' { - $Body = Set-NinjaOneFieldMapping -CIPPMapping $Table -APIName $APIName -Request $Request -TriggerMetadata $TriggerMetadata + $Body = Get-NinjaOneFieldMapping -CIPPMapping $Table + } } } -} -catch { - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "mapping API failed. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } -} -try { - if ($Request.Query.AutoMapping) { - switch ($Request.Query.AutoMapping) { - 'NinjaOrgs' { - Push-OutputBinding -Name NinjaProcess -Value @{'NinjaAction' = 'StartAutoMapping' } - $Body = [pscustomobject]@{'Results' = 'Automapping Request has been queued. Exact name matches will appear first and matches on device names and serials will take longer. Please check the CIPP Logbook and refresh the page once complete.' } - } + try { + if ($Request.Query.AddMapping) { + switch ($Request.Query.AddMapping) { + 'Halo' { + $body = Set-HaloMapping -CIPPMapping $Table -APIName $APIName -Request $Request + } + 'NinjaOrgs' { + $Body = Set-NinjaOneOrgMapping -CIPPMapping $Table -APIName $APIName -Request $Request + } + 'NinjaFields' { + $Body = Set-NinjaOneFieldMapping -CIPPMapping $Table -APIName $APIName -Request $Request -TriggerMetadata $TriggerMetadata + } + } } + } catch { + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "mapping API failed. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } } -} -catch { - Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "mapping API failed. $($_.Exception.Message)" -Sev 'Error' - $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } -} -# Associate values to output bindings by calling 'Push-OutputBinding'. -Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $body - }) + try { + if ($Request.Query.AutoMapping) { + switch ($Request.Query.AutoMapping) { + 'NinjaOrgs' { + #Push-OutputBinding -Name NinjaProcess -Value @{'NinjaAction' = 'StartAutoMapping' } + $Batch = [PSCustomObject]@{ + 'NinjaAction' = 'StartAutoMapping' + 'FunctionName' = 'NinjaOneQueue' + } + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + $Body = [pscustomobject]@{'Results' = 'Automapping Request has been queued. Exact name matches will appear first and matches on device names and serials will take longer. Please check the CIPP Logbook and refresh the page once complete.' } + } + } + } + } catch { + Write-LogMessage -API $APINAME -user $request.headers.'x-ms-client-principal' -message "mapping API failed. $($_.Exception.Message)" -Sev 'Error' + $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } } + + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $body + }) + +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 index effb15af199d..57e44b3f946f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecExtensionSync.ps1 @@ -42,28 +42,50 @@ Function Invoke-ExecExtensionSync { $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } if ($Request.Query.TenantID) { - $Tenant = $TenantsToProcess | Where-Object {$_.RowKey -eq $Request.Query.TenantID} - if (($Tenant | Measure-Object).count -eq 1){ - Push-OutputBinding -Name NinjaProcess -Value @{ + $Tenant = $TenantsToProcess | Where-Object { $_.RowKey -eq $Request.Query.TenantID } + if (($Tenant | Measure-Object).count -eq 1) { + <#Push-OutputBinding -Name NinjaProcess -Value @{ 'NinjaAction' = 'SyncTenant' 'MappedTenant' = $Tenant + }#> + $Batch = [PSCustomObject]@{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + 'FunctionName' = 'NinjaOneQueue' + } + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + $Results = [pscustomobject]@{'Results' = "NinjaOne Synchronization Queued for $($Tenant.NinjaOneName)" } } else { - $Results = [pscustomobject]@{'Results' = "Tenant was not found." } - } - + $Results = [pscustomobject]@{'Results' = 'Tenant was not found.' } + } + } else { - - Push-OutputBinding -Name NinjaProcess -Value @{ + <#Push-OutputBinding -Name NinjaProcess -Value @{ 'NinjaAction' = 'SyncTenants' + }#> + $Batch = [PSCustomObject]@{ + 'NinjaAction' = 'SyncTenants' + 'FunctionName' = 'NinjaOneQueue' } - + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" $Results = [pscustomobject]@{'Results' = "NinjaOne Synchronization Queuing $(($TenantsToProcess | Measure-Object).count) Tenants" } } - + } catch { $Results = [pscustomobject]@{'Results' = "Could not start NinjaOne Sync: $($_.Exception.Message)" } Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message "Could not start NinjaOne Sync $($_.Exception.Message)" -sev Error diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneExtensionScheduler.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneExtensionScheduler.ps1 new file mode 100644 index 000000000000..02ac73202eb6 --- /dev/null +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneExtensionScheduler.ps1 @@ -0,0 +1,109 @@ +function Invoke-NinjaOneExtensionScheduler { + $Table = Get-CIPPTable -TableName NinjaOneSettings + $Settings = (Get-AzDataTableEntity @Table) + $TimeSetting = ($Settings | Where-Object { $_.RowKey -eq 'NinjaSyncTime' }).SettingValue + + + if (($TimeSetting | Measure-Object).count -ne 1) { + [int]$TimeSetting = Get-Random -Minimum 1 -Maximum 95 + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaSyncTime' + 'SettingValue' = $TimeSetting + } + Add-AzDataTableEntity @Table -Entity $AddObject -Force + } + + Write-Host "Ninja Time Setting: $TimeSetting" + + $LastRunTime = Get-Date(($Settings | Where-Object { $_.RowKey -eq 'NinjaLastRunTime' }).SettingValue) + + Write-Host "Last Run: $LastRunTime" + + $CurrentTime = Get-Date + $CurrentInterval = ($CurrentTime.Hour * 4) + [math]::Floor($CurrentTime.Minute / 15) + + Write-Host "Current Interval: $CurrentInterval" + + $CIPPMapping = Get-CIPPTable -TableName CippMapping + $Filter = "PartitionKey eq 'NinjaOrgsMapping'" + $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } + + if ($Null -eq $LastRunTime -or $LastRunTime -le (Get-Date).addhours(-25) -or $TimeSetting -eq $CurrentInterval) { + Write-Host 'Executing' + $Batch = foreach ($Tenant in $TenantsToProcess | Sort-Object lastEndTime) { + <#Push-OutputBinding -Name NinjaProcess -Value @{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + } + Start-Sleep -Seconds 1#> + [PSCustomObject]@{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + 'FunctionName' = 'NinjaOneQueue' + } + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + } + + $AddObject = @{ + PartitionKey = 'NinjaConfig' + RowKey = 'NinjaLastRunTime' + 'SettingValue' = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffK') + } + Add-AzDataTableEntity @Table -Entity $AddObject -Force + + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "NinjaOne Daily Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' + + } else { + if ($LastRunTime -lt (Get-Date).AddMinutes(-90)) { + $TenantsToProcess | ForEach-Object { + if ($Null -ne $_.lastEndTime -and $_.lastEndTime -ne '') { + $_.lastEndTime = (Get-Date($_.lastEndTime)) + } else { + $_ | Add-Member -NotePropertyName lastEndTime -NotePropertyValue $Null -Force + } + + if ($Null -ne $_.lastStartTime -and $_.lastStartTime -ne '') { + $_.lastStartTime = (Get-Date($_.lastStartTime)) + } else { + $_ | Add-Member -NotePropertyName lastStartTime -NotePropertyValue $Null -Force + } + } + $CatchupTenants = $TenantsToProcess | Where-Object { (((($_.lastEndTime -eq $Null) -or ($_.lastStartTime -gt $_.lastEndTime)) -and ($_.lastStartTime -lt (Get-Date).AddMinutes(-30)))) -or ($_.lastStartTime -lt $LastRunTime) } + $Batch = foreach ($Tenant in $CatchupTenants) { + #Push-OutputBinding -Name NinjaProcess -Value @{ + # 'NinjaAction' = 'SyncTenant' + # 'MappedTenant' = $Tenant + #} + [PSCustomObject]@{ + NinjaAction = 'SyncTenant' + MappedTenant = $Tenant + FunctionName = 'NinjaOneQueue' + } + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" + } + + if (($CatchupTenants | Measure-Object).count -gt 0) { + Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "NinjaOne Synchronization Catchup Queued for $(($CatchupTenants | Measure-Object).count) Tenants" -Sev 'Info' + } + + } + + } +} \ No newline at end of file diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 index 0590894a8fec..f351d43ee039 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneOrgMapping.ps1 @@ -3,30 +3,30 @@ function Invoke-NinjaOneOrgMapping { [System.Collections.Generic.List[PSCustomObject]]$MatchedM365Tenants = @() [System.Collections.Generic.List[PSCustomObject]]$MatchedNinjaOrgs = @() - $ExcludeSerials = @("0", "SystemSerialNumber", "To Be Filled By O.E.M.", "System Serial Number", "0123456789", "123456789", "............") + $ExcludeSerials = @('0', 'SystemSerialNumber', 'To Be Filled By O.E.M.', 'System Serial Number', '0123456789', '123456789', '............') $CIPPMapping = Get-CIPPTable -TableName CippMapping - + #Get available mappings $Mappings = [pscustomobject]@{} $Filter = "PartitionKey eq 'NinjaOrgsMapping'" Get-AzDataTableEntity @CIPPMapping -Filter $Filter | ForEach-Object { $Mappings | Add-Member -NotePropertyName $_.RowKey -NotePropertyValue @{ label = "$($_.NinjaOneName)"; value = "$($_.NinjaOne)" } } - + #Get Available Tenants $Tenants = Get-Tenants #Get available Ninja clients $Table = Get-CIPPTable -TableName Extensionsconfig $Configuration = ((Get-AzDataTableEntity @Table).config | ConvertFrom-Json).NinjaOne - + $Token = Get-NinjaOneToken -configuration $Configuration - + # Fetch Ninja Orgs $After = 0 $PageSize = 1000 $NinjaOrgs = do { - $Result = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/organizations?pageSize=$PageSize&after=$After" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + $Result = (Invoke-WebRequest -Uri "https://$($Configuration.Instance)/api/v2/organizations?pageSize=$PageSize&after=$After" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -Depth 100 $Result $ResultCount = ($Result.id | Measure-Object -Maximum) $After = $ResultCount.maximum @@ -51,11 +51,11 @@ function Invoke-NinjaOneOrgMapping { $After = 0 $PageSize = 1000 $NinjaDevicesRaw = do { - $Result = (Invoke-WebRequest -uri "https://$($Configuration.Instance)/api/v2/devices-detailed?pageSize=$PageSize&after=$After" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -depth 100 + $Result = (Invoke-WebRequest -Uri "https://$($Configuration.Instance)/api/v2/devices-detailed?pageSize=$PageSize&after=$After" -Method GET -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json').content | ConvertFrom-Json -Depth 100 $Result $ResultCount = ($Result.id | Measure-Object -Maximum) $After = $ResultCount.maximum - + } while ($ResultCount.count -eq $PageSize) @@ -71,12 +71,12 @@ function Invoke-NinjaOneOrgMapping { } # Remove any devices with duplicate serials - $ParsedNinjaDevices = $NinjaDevices | Where-Object { $_.Serial -in (($NinjaDevices | Group-Object Serial | where-object { $_.count -eq 1 }).name) } + $ParsedNinjaDevices = $NinjaDevices | Where-Object { $_.Serial -in (($NinjaDevices | Group-Object Serial | Where-Object { $_.count -eq 1 }).name) } # First lets match on Org names foreach ($Tenant in $Tenants | Where-Object { $_.customerId -notin $MatchedM365Tenants.customerId }) { - $MatchedOrg = $NinjaOrgs | where-object { $_.name -eq $Tenant.displayName } + $MatchedOrg = $NinjaOrgs | Where-Object { $_.name -eq $Tenant.displayName } if (($MatchedOrg | Measure-Object).count -eq 1) { $MatchedM365Tenants.add($Tenant) $MatchedNinjaOrgs.add($MatchedOrg) @@ -87,23 +87,34 @@ function Invoke-NinjaOneOrgMapping { 'NinjaOneName' = "$($MatchedOrg.name)" } Add-AzDataTableEntity @CIPPMapping -Entity $AddObject -Force - Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "Added mapping from Organization name match for $($Tenant.customerId). to $($($MatchedOrg.name))" -Sev 'Info' + Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "Added mapping from Organization name match for $($Tenant.customerId). to $($($MatchedOrg.name))" -Sev 'Info' } } # Now Let match on remaining Tenants - Foreach ($Tenant in $Tenants | Where-Object { $_.customerId -notin $MatchedM365Tenants.customerId }) { - - Push-OutputBinding -Name NinjaProcess -Value @{ + $Batch = Foreach ($Tenant in $Tenants | Where-Object { $_.customerId -notin $MatchedM365Tenants.customerId }) { + <#Push-OutputBinding -Name NinjaProcess -Value @{ + 'NinjaAction' = 'AutoMapTenant' + 'M365Tenant' = $Tenant + 'NinjaOrgs' = $NinjaOrgs | Where-Object { $_.id -notin $MatchedNinjaOrgs } + 'NinjaDevices' = $ParsedNinjaDevices + }#> + [PSCustomObject]@{ 'NinjaAction' = 'AutoMapTenant' 'M365Tenant' = $Tenant 'NinjaOrgs' = $NinjaOrgs | Where-Object { $_.id -notin $MatchedNinjaOrgs } 'NinjaDevices' = $ParsedNinjaDevices + 'FunctionName' = 'NinjaOneQueue' } - - Start-Sleep -Seconds 1 - + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" } } - \ No newline at end of file diff --git a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneSync.ps1 b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneSync.ps1 index a168d291575b..df7d6f67111a 100644 --- a/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneSync.ps1 +++ b/Modules/CippExtensions/NinjaOne/Invoke-NinjaOneSync.ps1 @@ -7,12 +7,26 @@ function Invoke-NinjaOneSync { $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } - foreach ($Tenant in $TenantsToProcess) { - Push-OutputBinding -Name NinjaProcess -Value @{ + $Batch = foreach ($Tenant in $TenantsToProcess) { + <#Push-OutputBinding -Name NinjaProcess -Value @{ 'NinjaAction' = 'SyncTenant' 'MappedTenant' = $Tenant } - Start-Sleep -Seconds 1 + Start-Sleep -Seconds 1#> + [PSCustomObject]@{ + 'NinjaAction' = 'SyncTenant' + 'MappedTenant' = $Tenant + 'FunctionName' = 'NinjaOneQueue' + } + } + if (($Batch | Measure-Object).Count -gt 0) { + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'NinjaOneOrchestrator' + Batch = @($Batch) + } + #Write-Host ($InputObject | ConvertTo-Json) + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5) + Write-Host "Started permissions orchestration with ID = '$InstanceId'" } $AddObject = @{ @@ -23,10 +37,9 @@ function Invoke-NinjaOneSync { Add-AzDataTableEntity @Table -Entity $AddObject -Force - Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' + Write-LogMessage -API 'NinjaOneAutoMap_Queue' -user 'CIPP' -message "NinjaOne Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' } catch { Write-LogMessage -API 'Scheduler_Billing' -tenant 'none' -message "Could not start NinjaOne Sync $($_.Exception.Message)" -sev Error } - + } - \ No newline at end of file diff --git a/Modules/CippExtensions/NinjaOne/Push-NinjaOneQueue.ps1 b/Modules/CippExtensions/NinjaOne/Push-NinjaOneQueue.ps1 new file mode 100644 index 000000000000..09fe668a97b4 --- /dev/null +++ b/Modules/CippExtensions/NinjaOne/Push-NinjaOneQueue.ps1 @@ -0,0 +1,15 @@ +function Push-NinjaOneQueue { + # Input bindings are passed in via param block. + param($Item) + + # Write out the queue message and metadata to the information log. + Write-Host "PowerShell NinjaOne queue trigger function processed work item: $($Item.NinjaAction)" + + Switch ($Item.NinjaAction) { + 'StartAutoMapping' { Invoke-NinjaOneOrgMapping } + 'AutoMapTenant' { Invoke-NinjaOneOrgMappingTenant -QueueItem $Item } + 'SyncTenant' { Invoke-NinjaOneTenantSync -QueueItem $Item } + 'SyncTenants' { Invoke-NinjaOneSync } + } + +} \ No newline at end of file diff --git a/Scheduler_Extensions/function.json b/Scheduler_Extensions/function.json index f3e5317f409a..7474f0f13334 100644 --- a/Scheduler_Extensions/function.json +++ b/Scheduler_Extensions/function.json @@ -11,6 +11,11 @@ "direction": "out", "name": "NinjaProcess", "queueName": "NinjaOneQueue" + }, + { + "name": "starter", + "type": "durableClient", + "direction": "in" } ] } diff --git a/Scheduler_Extensions/run.ps1 b/Scheduler_Extensions/run.ps1 index 66af8649ebf7..58e228ebbe1d 100644 --- a/Scheduler_Extensions/run.ps1 +++ b/Scheduler_Extensions/run.ps1 @@ -10,85 +10,5 @@ Write-Host 'Started Scheduler for Extensions' # NinjaOne Extension if ($Configuration.NinjaOne.Enabled -eq $True) { - - $Table = Get-CIPPTable -TableName NinjaOneSettings - $Settings = (Get-AzDataTableEntity @Table) - $TimeSetting = ($Settings | Where-Object { $_.RowKey -eq 'NinjaSyncTime' }).SettingValue - - - if (($TimeSetting | Measure-Object).count -ne 1) { - [int]$TimeSetting = Get-Random -Minimum 1 -Maximum 95 - $AddObject = @{ - PartitionKey = 'NinjaConfig' - RowKey = 'NinjaSyncTime' - 'SettingValue' = $TimeSetting - } - Add-AzDataTableEntity @Table -Entity $AddObject -Force - } - - Write-Host "Ninja Time Setting: $TimeSetting" - - $LastRunTime = Get-Date(($Settings | Where-Object { $_.RowKey -eq 'NinjaLastRunTime' }).SettingValue) - - Write-Host "Last Run: $LastRunTime" - - $CurrentTime = Get-Date - $CurrentInterval = ($CurrentTime.Hour * 4) + [math]::Floor($CurrentTime.Minute / 15) - - Write-Host "Current Interval: $CurrentInterval" - - $CIPPMapping = Get-CIPPTable -TableName CippMapping - $Filter = "PartitionKey eq 'NinjaOrgsMapping'" - $TenantsToProcess = Get-AzDataTableEntity @CIPPMapping -Filter $Filter | Where-Object { $Null -ne $_.NinjaOne -and $_.NinjaOne -ne '' } - - if ($Null -eq $LastRunTime -or $LastRunTime -le (Get-Date).addhours(-25) -or $TimeSetting -eq $CurrentInterval) { - Write-Host 'Executing' - foreach ($Tenant in $TenantsToProcess | Sort-Object lastEndTime) { - Push-OutputBinding -Name NinjaProcess -Value @{ - 'NinjaAction' = 'SyncTenant' - 'MappedTenant' = $Tenant - } - Start-Sleep -Seconds 1 - - } - - $AddObject = @{ - PartitionKey = 'NinjaConfig' - RowKey = 'NinjaLastRunTime' - 'SettingValue' = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffK') - } - Add-AzDataTableEntity @Table -Entity $AddObject -Force - - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "NinjaOne Daily Synchronization Queued for $(($TenantsToProcess | Measure-Object).count) Tenants" -Sev 'Info' - - } else { - if ($LastRunTime -lt (Get-Date).AddMinutes(-90)) { - $TenantsToProcess | ForEach-Object { - if ($Null -ne $_.lastEndTime -and $_.lastEndTime -ne '') { - $_.lastEndTime = (Get-Date($_.lastEndTime)) - } else { - $_ | Add-Member -NotePropertyName lastEndTime -NotePropertyValue $Null -Force - } - - if ($Null -ne $_.lastStartTime -and $_.lastStartTime -ne '') { - $_.lastStartTime = (Get-Date($_.lastStartTime)) - } else { - $_ | Add-Member -NotePropertyName lastStartTime -NotePropertyValue $Null -Force - } - } - $CatchupTenants = $TenantsToProcess | Where-Object { (((($_.lastEndTime -eq $Null) -or ($_.lastStartTime -gt $_.lastEndTime)) -and ($_.lastStartTime -lt (Get-Date).AddMinutes(-30)))) -or ($_.lastStartTime -lt $LastRunTime) } - foreach ($Tenant in $CatchupTenants) { - Push-OutputBinding -Name NinjaProcess -Value @{ - 'NinjaAction' = 'SyncTenant' - 'MappedTenant' = $Tenant - } - Start-Sleep -Seconds 1 - } - if (($CatchupTenants | Measure-Object).count -gt 0) { - Write-LogMessage -API 'NinjaOneSync' -user 'CIPP' -message "NinjaOne Synchronization Catchup Queued for $(($CatchupTenants | Measure-Object).count) Tenants" -Sev 'Info' - } - - } - - } + Invoke-NinjaOneExtensionScheduler } \ No newline at end of file From 532bed35504dca068c40941365ff50e6d64ce5fd Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:35:18 -0700 Subject: [PATCH 41/45] Add or update the Azure App Service build and deployment workflow config --- .github/workflows/dev_cippckdtz.yml | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/dev_cippckdtz.yml diff --git a/.github/workflows/dev_cippckdtz.yml b/.github/workflows/dev_cippckdtz.yml new file mode 100644 index 000000000000..6e0c53e9df0a --- /dev/null +++ b/.github/workflows/dev_cippckdtz.yml @@ -0,0 +1,30 @@ +# Docs for the Azure Web Apps Deploy action: https://github.com/azure/functions-action +# More GitHub Actions for Azure: https://github.com/Azure/actions + +name: Build and deploy Powershell project to Azure Function App - cippckdtz + +on: + push: + branches: + - dev + workflow_dispatch: + +env: + AZURE_FUNCTIONAPP_PACKAGE_PATH: '.' # set this to the path to your web app project, defaults to the repository root + +jobs: + deploy: + runs-on: windows-latest + + steps: + - name: 'Checkout GitHub Action' + uses: actions/checkout@v4 + + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + id: fa + with: + app-name: 'cippckdtz' + slot-name: 'Production' + package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} + publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_2101C7175BFB47E58240ABD1E72E81C2 }} \ No newline at end of file From 078eefdacc7857ca0dedc023fe911c7898b96e53 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Mar 2024 15:02:32 -0400 Subject: [PATCH 42/45] Update CippExtensions.psd1 --- Modules/CippExtensions/CippExtensions.psd1 | Bin 11436 -> 9666 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Modules/CippExtensions/CippExtensions.psd1 b/Modules/CippExtensions/CippExtensions.psd1 index 437cc32f8661e599f3a75a3a0abb378391030900..8a30d23c6ef143845bbf7f162aa4dd3128cf7174 100644 GIT binary patch delta 16 XcmZ1zdB}T%f#_rhIjPMiG8F;%-iNwx**56K^n3KodQ#V+`Qn2o}HF-09d9 z$c;EMPlmJo0!iPx$hHK=ldlYGaF>|5xO6R(QMSvU<{L4 z98}RoOYT+1r2u!6W z)|kV0+wOR6PNWAcdLdCCGRO6t!OZMCT&Fit<(Sl|G})IpO$xh{)SFQJO6qJPPaoLI z@jX0wyp$X+vlW~zo@;gfV$&$y!;i& Date: Thu, 14 Mar 2024 20:36:32 -0400 Subject: [PATCH 43/45] Graph Request - Add %tenantid% replace option --- .../Public/GraphRequests/Get-GraphRequestList.ps1 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 index 52cc2c3778dc..49c0c3fa801e 100644 --- a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 +++ b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 @@ -112,6 +112,17 @@ function Get-GraphRequestList { $QueueReference = '{0}-{1}' -f $TenantFilter, $PartitionKey $RunningQueue = Get-CippQueue | Where-Object { $_.Reference -eq $QueueReference -and $_.Status -ne 'Completed' -and $_.Status -ne 'Failed' } + if ($TenantFilter -ne 'AllTenants' -and $Endpoint -match '%tenantid%') { + $TenantId = (Get-Tenants -IncludeErrors | Where-Object { $_.defaultDomainName -eq $TenantFilter -or $_.customerId -eq $TenantFilter }).customerId + $Endpoint = $Endpoint -replace '%tenantid%', $TenantId + $GraphQuery = [System.UriBuilder]('https://graph.microsoft.com/{0}/{1}' -f $Version, $Endpoint) + $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) + foreach ($Item in ($Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { + $ParamCollection.Add($Item.Key, $Item.Value) + } + $GraphQuery.Query = $ParamCollection.ToString() + } + if (!$Rows) { switch ($TenantFilter) { 'AllTenants' { From 1a34d93dcc5727f05d60504cfca692967e85d8e1 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Mar 2024 20:42:59 -0400 Subject: [PATCH 44/45] Update Get-GraphRequestList.ps1 --- Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 index 49c0c3fa801e..1117f95d196d 100644 --- a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 +++ b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 @@ -68,6 +68,7 @@ function Get-GraphRequestList { $TableName = ('cache{0}' -f ($Endpoint -replace '[^A-Za-z0-9]'))[0..62] -join '' Write-Host "Table: $TableName" + $Endpoint = $Endpoint -replace '^/', '' $DisplayName = ($Endpoint -split '/')[0] if ($QueueNameOverride) { From 7707162a21be4693ae840cf5d103ba78661796b8 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 15 Mar 2024 14:04:32 +0100 Subject: [PATCH 45/45] upp version --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index fb467b15735a..e230c8396d19 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -5.2.2 \ No newline at end of file +5.3.0 \ No newline at end of file