From 684f9fdd4c9cf61500d1b2fd44d85aa297b8780c Mon Sep 17 00:00:00 2001 From: Mo Date: Wed, 13 Mar 2024 11:36:20 +0000 Subject: [PATCH 01/14] 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 02/14] 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 d321b8de4926d43e38176aefc2efb77a0944d9a3 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 14 Mar 2024 10:04:45 -0400 Subject: [PATCH 03/14] 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 04/14] 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 05/14] 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 06/14] 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 07/14] 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 08/14] 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 09/14] 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 10/14] 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 11/14] 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 12/14] 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 13/14] 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 14/14] 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