diff --git a/Modules/CIPPCore/Public/Add-CIPPAzDataTableEntity.ps1 b/Modules/CIPPCore/Public/Add-CIPPAzDataTableEntity.ps1 index ae011505e430..6e2e0dd618d5 100644 --- a/Modules/CIPPCore/Public/Add-CIPPAzDataTableEntity.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPAzDataTableEntity.ps1 @@ -14,7 +14,7 @@ function Add-CIPPAzDataTableEntity { try { Add-AzDataTableEntity -Context $Context -Force:$Force -CreateTableIfNotExists:$CreateTableIfNotExists -Entity $SingleEnt -ErrorAction Stop } catch [System.Exception] { - if ($_.Exception.ErrorCode -eq 'PropertyValueTooLarge' -or $_.Exception.ErrorCode -eq 'EntityTooLarge') { + if ($_.Exception.ErrorCode -eq 'PropertyValueTooLarge' -or $_.Exception.ErrorCode -eq 'EntityTooLarge' -or $_.Exception.ErrorCode -eq 'RequestBodyTooLarge') { try { $largePropertyNames = [System.Collections.ArrayList]::new() $entitySize = 0 @@ -67,7 +67,7 @@ function Add-CIPPAzDataTableEntity { $entityIndex = 0 while ($entitySize -gt $MaxRowSize) { - Write-Host "Entity size is $entitySize. Splitting entity into multiple parts." + Write-Information "Entity size is $entitySize. Splitting entity into multiple parts." $newEntity = @{} $newEntity['PartitionKey'] = $originalPartitionKey if ($entityIndex -eq 0) { @@ -126,7 +126,7 @@ function Add-CIPPAzDataTableEntity { } foreach ($row in $rows) { - Write-Host "current entity is $($row.RowKey) with $($row.PartitionKey). Our size is $([System.Text.Encoding]::UTF8.GetByteCount($($row | ConvertTo-Json)))" + Write-Information "current entity is $($row.RowKey) with $($row.PartitionKey). Our size is $([System.Text.Encoding]::UTF8.GetByteCount($($row | ConvertTo-Json)))" Add-AzDataTableEntity -Context $Context -Force:$Force -CreateTableIfNotExists:$CreateTableIfNotExists -Entity $row } } else { @@ -137,7 +137,7 @@ function Add-CIPPAzDataTableEntity { throw "Error processing entity: $($_.Exception.Message) Linenumner: $($_.InvocationInfo.ScriptLineNumber)" } } else { - Write-Host "THE ERROR IS $($_.Exception.ErrorCode). The size of the entity is $entitySize." + Write-Information "THE ERROR IS $($_.Exception.ErrorCode). The size of the entity is $entitySize." throw $_ } } diff --git a/Modules/CippExtensions/Public/Extension Functions/Register-CippExtensionScheduledTasks.ps1 b/Modules/CippExtensions/Public/Extension Functions/Register-CippExtensionScheduledTasks.ps1 index e964d7c23515..ba3ba64a9f42 100644 --- a/Modules/CippExtensions/Public/Extension Functions/Register-CippExtensionScheduledTasks.ps1 +++ b/Modules/CippExtensions/Public/Extension Functions/Register-CippExtensionScheduledTasks.ps1 @@ -15,7 +15,7 @@ function Register-CIPPExtensionScheduledTasks { $Tenants = Get-Tenants -IncludeErrors $Extensions = @('Hudu') - + $MappedTenants = [System.Collections.Generic.List[string]]::new() foreach ($Extension in $Extensions) { $ExtensionConfig = $Config.$Extension if ($ExtensionConfig.Enabled -eq $true) { @@ -45,6 +45,7 @@ function Register-CIPPExtensionScheduledTasks { Write-Warning "Tenant $($Mapping.RowKey) not found" continue } + $MappedTenants.Add($Tenant.defaultDomainName) foreach ($SyncType in $SyncTypes) { $ExistingTask = $ScheduledTasks | Where-Object { $_.Tenant -eq $Tenant.defaultDomainName -and $_.SyncType -eq $SyncType } if (!$ExistingTask -or $Reschedule.IsPresent) { @@ -98,23 +99,24 @@ function Register-CIPPExtensionScheduledTasks { } } else { # remove existing scheduled tasks - $ScheduledTasks | Where-Object { $_.SyncType -eq $Extension } | ForEach-Object { + $PushTasks | Where-Object { $_.SyncType -eq $Extension } | ForEach-Object { Write-Information "Extension Disabled: Cleaning up scheduled task $($_.Name) for tenant $($_.Tenant)" $Entity = $_ | Select-Object -Property PartitionKey, RowKey Remove-AzDataTableEntity @ScheduledTasksTable -Entity $Entity } } } + $MappedTenants = $MappedTenants | Sort-Object -Unique foreach ($Task in $ScheduledTasks) { - if ($Task.Tenant -notin $Tenants.defaultDomainName) { + if ($Task.Tenant -notin $MappedTenants) { Write-Information "Tenant Removed: Cleaning up scheduled task $($Task.Name) for tenant $($Task.TenantFilter)" $Entity = $Task | Select-Object -Property PartitionKey, RowKey Remove-AzDataTableEntity @ScheduledTasksTable -Entity $Entity } } foreach ($Task in $PushTasks) { - if ($Task.Tenant -notin $Tenants.defaultDomainName) { + if ($Task.Tenant -notin $MappedTenants) { Write-Information "Tenant Removed: Cleaning up scheduled task $($Task.Name) for tenant $($Task.TenantFilter)" $Entity = $Task | Select-Object -Property PartitionKey, RowKey Remove-AzDataTableEntity @ScheduledTasksTable -Entity $Entity diff --git a/Modules/CippExtensions/Public/Extension Functions/Sync-CippExtensionData.ps1 b/Modules/CippExtensions/Public/Extension Functions/Sync-CippExtensionData.ps1 index 544ba27b1960..dba30aad0b60 100644 --- a/Modules/CippExtensions/Public/Extension Functions/Sync-CippExtensionData.ps1 +++ b/Modules/CippExtensions/Public/Extension Functions/Sync-CippExtensionData.ps1 @@ -22,7 +22,7 @@ function Sync-CippExtensionData { Error = '' LastSync = 'Never' } - Add-CIPPAzDataTableEntity @Table -Entity $LastSync + $null = Add-CIPPAzDataTableEntity @Table -Entity $LastSync } try { @@ -68,12 +68,12 @@ function Sync-CippExtensionData { @{ id = 'OneDriveUsage' method = 'GET' - url = "reports/getOneDriveUsageAccountDetail(period='D7')?`$format=application/json" + url = "reports/getOneDriveUsageAccountDetail(period='D7')?`$format=application%2fjson" }, @{ id = 'MailboxUsage' method = 'GET' - url = "reports/getMailboxUsageDetail(period='D7')?`$format=application/json" + url = "reports/getMailboxUsageDetail(period='D7')?`$format=application%2fjson" } ) @@ -172,29 +172,63 @@ function Sync-CippExtensionData { RowKey = 'Mailboxes' Data = [string]($Mailboxes | ConvertTo-Json -Depth 10 -Compress) } - Add-CIPPAzDataTableEntity @CacheTable -Entity $Entity -Force + $null = Add-CIPPAzDataTableEntity @CacheTable -Entity $Entity -Force + + $SingleGraphQueries = @( + @{ + id = 'CASMailbox' + graphRequest = @{ + uri = "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/CasMailbox" + Tenantid = $tenantfilter + scope = 'ExchangeOnline' + noPagination = $true + } + } + ) + + # Bulk request mailbox permissions using New-ExoBulkRequest for each mailbox - mailboxPermissions is not a valid graph query + $ExoBulkRequests = foreach ($Mailbox in $Mailboxes) { + @{ + CmdletInput = @{ + CmdletName = 'Get-MailboxPermission' + Parameters = @{ Identity = $Mailbox.UPN } + } + } + } + $MailboxPermissions = New-ExoBulkRequest -cmdletArray @($ExoBulkRequests) -tenantid $TenantFilter + $Entity = @{ + PartitionKey = $TenantFilter + SyncType = 'Mailboxes' + RowKey = 'MailboxPermissions' + Data = [string]($MailboxPermissions | ConvertTo-Json -Depth 10 -Compress) + } + $null = Add-CIPPAzDataTableEntity @CacheTable -Entity $Entity -Force } } if ($TenantRequests) { + Write-Information "Requesting tenant information for $TenantFilter $SyncType" try { - $TenantResults = New-GraphBulkRequest -Requests $TenantRequests -tenantid $TenantFilter + $TenantResults = New-GraphBulkRequest -Requests @($TenantRequests) -tenantid $TenantFilter } catch { Throw "Failed to fetch bulk company data: $_" } - if ($SingleGraphQueries) { - foreach ($SingleGraphQuery in $SingleGraphQueries) { - $Request = $SingleGraphQuery.graphRequest - $Data = New-GraphGetRequest @Request -tenantid $TenantFilter - $Entity = @{ - PartitionKey = $TenantFilter - SyncType = $SyncType - RowKey = $SingleGraphQuery.id - Data = [string]($Data | ConvertTo-Json -Depth 10 -Compress) - } - Add-CIPPAzDataTableEntity @CacheTable -Entity $Entity -Force + $TenantResults | Select-Object id, body | ForEach-Object { + $Data = $_.body.value ?? $_.body + if ($Data -match '^eyJ') { + # base64 decode + $Data = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Data)) | ConvertFrom-Json + $Data = $Data.Value + } + + $Entity = @{ + PartitionKey = $TenantFilter + RowKey = $_.id + SyncType = $SyncType + Data = [string]($Data | ConvertTo-Json -Depth 10 -Compress) } + $null = Add-CIPPAzDataTableEntity @CacheTable -Entity $Entity -Force } if ($AdditionalRequests) { @@ -210,32 +244,54 @@ function Sync-CippExtensionData { } } } - #Write-Information ($AdditionalRequestQueries | ConvertTo-Json -Depth 10 -Compress) if (($AdditionalRequestQueries | Measure-Object).Count -gt 0) { - $AdditionalResults = New-GraphBulkRequest -Requests $AdditionalRequestQueries -tenantid $TenantFilter - $AdditionalResults | ForEach-Object { - $Entity = @{ - PartitionKey = $TenantFilter - SyncType = $SyncType - RowKey = '{0}_{1}' -f $ParentId, $_.id - Data = [string]($_.body.value | ConvertTo-Json -Depth 10 -Compress) + try { + $AdditionalResults = New-GraphBulkRequest -Requests @($AdditionalRequestQueries) -tenantid $TenantFilter + } catch { + throw $_ + } + if ($AdditionalResults) { + $AdditionalResults | ForEach-Object { + $Data = $_.body.value ?? $_.body + if ($Data -match '^eyJ') { + # base64 decode + $Data = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Data)) | ConvertFrom-Json + $Data = $Data.Value + } + $Entity = @{ + PartitionKey = $TenantFilter + SyncType = $SyncType + RowKey = '{0}_{1}' -f $ParentId, $_.id + Data = [string]($Data | ConvertTo-Json -Depth 10 -Compress) + } + try { + $null = Add-CIPPAzDataTableEntity @CacheTable -Entity $Entity -Force + } catch { + throw $_ + } } - Add-CIPPAzDataTableEntity @CacheTable -Entity $Entity -Force } + } } } + } - $TenantResults | Select-Object id, body | ForEach-Object { + if ($SingleGraphQueries) { + foreach ($SingleGraphQuery in $SingleGraphQueries) { + $Request = $SingleGraphQuery.graphRequest + $Data = New-GraphGetRequest @Request -tenantid $TenantFilter $Entity = @{ PartitionKey = $TenantFilter - RowKey = $_.id SyncType = $SyncType - Data = [string]($_.body.value | ConvertTo-Json -Depth 10 -Compress) + RowKey = $SingleGraphQuery.id + Data = [string]($Data | ConvertTo-Json -Depth 10 -Compress) } - Add-CIPPAzDataTableEntity @CacheTable -Entity $Entity -Force + $null = Add-CIPPAzDataTableEntity @CacheTable -Entity $Entity -Force } } + + $LastSync.LastSync = [datetime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ') $LastSync.Status = 'Completed' $LastSync.Error = '' diff --git a/Modules/CippExtensions/Public/Hudu/Invoke-HuduExtensionSync.ps1 b/Modules/CippExtensions/Public/Hudu/Invoke-HuduExtensionSync.ps1 index d5f352e06679..09518896bf9b 100644 --- a/Modules/CippExtensions/Public/Hudu/Invoke-HuduExtensionSync.ps1 +++ b/Modules/CippExtensions/Public/Hudu/Invoke-HuduExtensionSync.ps1 @@ -314,7 +314,6 @@ function Invoke-HuduExtensionSync { $OneDriveDetails = $null } - <#try { $CASFull = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/CasMailbox" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true } catch { @@ -322,6 +321,14 @@ function Invoke-HuduExtensionSync { $CompanyResult.Errors.add("Company: Unable to fetch CAS Mailbox Details $_") }#> + if ($ExtensionCache.CASMailbox) { + $CASFull = $ExtensionCache.CASMailbox + } else { + $CompanyResult.Errors.add('Company: Unable to fetch CAS Mailbox Details') + $CASFull = $null + + } + <#try { $MailboxDetailedFull = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-Mailbox' } catch { @@ -344,6 +351,7 @@ function Invoke-HuduExtensionSync { $CompanyResult.Errors.add('Company: Unable to fetch Mailbox Statistic Details') } + $Permissions = $ExtensionCache.MailboxPermissions if ($licensedUsers) { $pre = "

Licensed Users

@@ -383,13 +391,14 @@ function Invoke-HuduExtensionSync { $CASRequest = $CASFull | Where-Object { $_.ExternalDirectoryObjectId -eq $User.iD } $MailboxDetailedRequest = $MailboxDetailedFull | Where-Object { $_.ExternalDirectoryObjectId -eq $User.iD } - $StatsRequest = $MailboxStatsFull | Where-Object { $_.'User Principal Name' -eq $User.UserPrincipalName } + $StatsRequest = $MailboxStatsFull | Where-Object { $_.'userPrincipalName' -eq $User.UserPrincipalName } - try { + <#try { $PermsRequest = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/Mailbox('$($User.ID)')/MailboxPermission" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true } catch { $PermsRequest = $null - } + }#> + $PermsRequest = $Permissions | Where-Object { $_.Identity -eq $User.ID } $ParsedPerms = foreach ($Perm in $PermsRequest) { if ($Perm.User -ne 'NT AUTHORITY\SELF') { @@ -401,7 +410,7 @@ function Invoke-HuduExtensionSync { } try { - $TotalItemSize = [math]::Round($StatsRequest.'Storage Used (Byte)' / 1Gb, 2) + $TotalItemSize = [math]::Round($StatsRequest.storageUsedInBytes / 1Gb, 2) } catch { $TotalItemSize = 0 } @@ -420,7 +429,7 @@ function Invoke-HuduExtensionSync { Permissions = $ParsedPerms ProhibitSendQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendQuota -split ' GB')[0], 2) ProhibitSendReceiveQuota = [math]::Round([float]($MailboxDetailedRequest.ProhibitSendReceiveQuota -split ' GB')[0], 2) - ItemCount = [math]::Round($StatsRequest.'Item Count', 2) + ItemCount = [math]::Round($StatsRequest.'itemCount', 2) TotalItemSize = $TotalItemSize } @@ -456,23 +465,23 @@ function Invoke-HuduExtensionSync { [System.Collections.Generic.List[PSCustomObject]]$OneDriveFormatted = @() if ($UserOneDriveDetails) { try { - $OneDriveUsePercent = [math]::Round([float](($UserOneDriveDetails.'Storage Used (Byte)' / $UserOneDriveDetails.'Storage Allocated (Byte)') * 100), 2) - $StorageUsed = [math]::Round($UserOneDriveDetails.'Storage Used (Byte)' / 1024 / 1024 / 1024, 2) - $StorageAllocated = [math]::Round($UserOneDriveDetails.'Storage Allocated (Byte)' / 1024 / 1024 / 1024, 2) + $OneDriveUsePercent = [math]::Round([float](($UserOneDriveDetails.storageUsedInBytes / $UserOneDriveDetails.storageAllocatedInBytes) * 100), 2) + $StorageUsed = [math]::Round($UserOneDriveDetails.storageUsedInBytes / 1024 / 1024 / 1024, 2) + $StorageAllocated = [math]::Round($UserOneDriveDetails.storageAllocatedInBytes / 1024 / 1024 / 1024, 2) } catch { $OneDriveUsePercent = 100 $StorageUsed = 0 $StorageAllocated = 0 } - $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Owner Principal Name' -Value "$($UserOneDriveDetails.'Owner Principal Name')")) - $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'One Drive URL' -Value "$($UserOneDriveDetails.'Site URL')")) - $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Is Deleted' -Value "$($UserOneDriveDetails.'Is Deleted')")) - $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Last Activity Date' -Value "$($UserOneDriveDetails.'Last Activity Date')")) - $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'File Count' -Value "$($UserOneDriveDetails.'File Count')")) - $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Active File Count' -Value "$($UserOneDriveDetails.'Active File Count')")) - $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Storage Used (Byte)' -Value "$($UserOneDriveDetails.'Storage Used (Byte)')")) - $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Storage Allocated (Byte)' -Value "$($UserOneDriveDetails.'Storage Allocated (Byte)')")) + $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Owner Principal Name' -Value "$($UserOneDriveDetails.ownerPrincipalName)")) + $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'One Drive URL' -Value "$($UserOneDriveDetails.siteUrl)")) + $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Is Deleted' -Value "$($UserOneDriveDetails.isDeleted)")) + $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Last Activity Date' -Value "$($UserOneDriveDetails.lastActivityDate)")) + $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'File Count' -Value "$($UserOneDriveDetails.fileCount)")) + $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Active File Count' -Value "$($UserOneDriveDetails.activeFileCount)")) + $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Storage Used (Byte)' -Value "$($UserOneDriveDetails.storageUsedInBytes)")) + $OneDriveFormatted.add($(Get-HuduFormattedField -Title 'Storage Allocated (Byte)' -Value "$($UserOneDriveDetails.storageAllocatedInBytes)")) $OneDriveUserUsage = @"