diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDepTokenExpiry.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDepTokenExpiry.ps1 index 1bf1e9c4463e..ec55c10bb283 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDepTokenExpiry.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertDepTokenExpiry.ps1 @@ -16,7 +16,8 @@ function Get-CIPPAlertDepTokenExpiry { $DepTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings' -tenantid $TenantFilter).value $AlertData = foreach ($Dep in $DepTokens) { if ($Dep.tokenExpirationDateTime -lt (Get-Date).AddDays(30) -and $Dep.tokenExpirationDateTime -gt (Get-Date).AddDays(-7)) { - 'Apple Device Enrollment Program token expiring on {0}' -f $Dep.tokenExpirationDateTime + $Message = 'Apple Device Enrollment Program token expiring on {0}' -f $Dep.tokenExpirationDateTime + $Dep | Select-Object -Property tokenName, @{Name = 'Message'; Expression = { $Message } } } } Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertVppTokenExpiry.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertVppTokenExpiry.ps1 index 224d23857005..9767e24fd5c4 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertVppTokenExpiry.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertVppTokenExpiry.ps1 @@ -15,10 +15,12 @@ function Get-CIPPAlertVppTokenExpiry { $VppTokens = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceAppManagement/vppTokens' -tenantid $TenantFilter).value $AlertData = foreach ($Vpp in $VppTokens) { if ($Vpp.state -ne 'valid') { - 'Apple Volume Purchase Program Token is not valid, new token required' + $Message = 'Apple Volume Purchase Program Token is not valid, new token required' + $Vpp | Select-Object -Property organizationName, appleId, vppTokenAccountType, @{Name = 'Message'; Expression = { $Message } } } if ($Vpp.expirationDateTime -lt (Get-Date).AddDays(30) -and $Vpp.expirationDateTime -gt (Get-Date).AddDays(-7)) { - 'Apple Volume Purchase Program token expiring on {0}' -f $Vpp.expirationDateTime + $Message = 'Apple Volume Purchase Program token expiring on {0}' -f $Vpp.expirationDateTime + $Vpp | Select-Object -Property organizationName, appleId, vppTokenAccountType, @{Name = 'Message'; Expression = { $Message } } } } Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData @@ -28,4 +30,4 @@ function Get-CIPPAlertVppTokenExpiry { } catch { # Error handling } -} \ No newline at end of file +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-GetTenantDomains.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-GetTenantDomains.ps1 index 5fb9c64cdad1..071bb5289139 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-GetTenantDomains.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Domain Analyser/Push-GetTenantDomains.ps1 @@ -2,6 +2,6 @@ function Push-GetTenantDomains { Param($Item) $DomainTable = Get-CippTable -tablename 'Domains' $Filter = "PartitionKey eq 'TenantDomains' and TenantGUID eq '{0}'" -f $Item.TenantGUID - $Domains = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter -Property RowKey | Select-Object RowKey, @{n = 'FunctionName'; exp = { 'DomainAnalyserDomain' } } + $Domains = Get-CIPPAzDataTableEntity @DomainTable -Filter $Filter -Property PartitionKey, RowKey | Select-Object RowKey, @{n = 'FunctionName'; exp = { 'DomainAnalyserDomain' } } return @($Domains) -} \ No newline at end of file +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-GetPendingWebhooks.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-GetPendingWebhooks.ps1 index 4b292fdd7041..2cdf56c8a6b5 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-GetPendingWebhooks.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-GetPendingWebhooks.ps1 @@ -5,9 +5,9 @@ function Push-GetPendingWebhooks { #> Param($Item) $Table = Get-CIPPTable -TableName WebhookIncoming - $Webhooks = Get-CIPPAzDataTableEntity @Table -Property RowKey, FunctionName -First 10000 + $Webhooks = Get-CIPPAzDataTableEntity @Table -Property PartitionKey, RowKey, FunctionName -First 10000 $WebhookCount = ($Webhooks | Measure-Object).Count $Message = 'Processing {0} webhooks' -f $WebhookCount Write-LogMessage -API 'Webhooks' -message $Message -sev Info return $Webhooks -} \ No newline at end of file +} diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecDurableFunctions.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecDurableFunctions.ps1 index 09da6bd2c990..f01062fe5720 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecDurableFunctions.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecDurableFunctions.ps1 @@ -51,7 +51,7 @@ function Invoke-ExecDurableFunctions { if ($Request.Query.PartitionKey) { $HistoryTable = Get-CippTable -TableName ('{0}History' -f $FunctionName) $Filter = "PartitionKey eq '{0}'" -f $Request.Query.PartitionKey - $History = Get-CippAzDataTableEntity @HistoryTable -Filter $Filter -Property RowKey, Timestamp, EventType, Name, IsPlayed, OrchestrationStatus | Select-Object * -ExcludeProperty ETag + $History = Get-CippAzDataTableEntity @HistoryTable -Filter $Filter -Property PartitionKey, RowKey, Timestamp, EventType, Name, IsPlayed, OrchestrationStatus | Select-Object * -ExcludeProperty ETag $Body = [PSCustomObject]@{ Results = @($History) @@ -173,4 +173,4 @@ function Invoke-ExecDurableFunctions { StatusCode = [HttpStatusCode]::OK Body = $Body }) -} \ No newline at end of file +} diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionMapping.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ExecExtensionMapping.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionMapping.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ExecExtensionMapping.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionSync.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ExecExtensionSync.ps1 similarity index 100% rename from Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionSync.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ExecExtensionSync.ps1 diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionTest.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ExecExtensionTest.ps1 similarity index 90% rename from Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionTest.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ExecExtensionTest.ps1 index e9a6465c4ff0..1262ab6260a9 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionTest.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ExecExtensionTest.ps1 @@ -49,8 +49,11 @@ Function Invoke-ExecExtensionTest { 'Hudu' { Connect-HuduAPI -configuration $Configuration.Hudu $Version = Get-HuduAppInfo - Write-Host ($Version | ConvertTo-Json) - $Results = [pscustomobject]@{'Results' = ('Successfully Connected to Hudu, version: {0}' -f $Version.version) } + if ($Version.version) { + $Results = [pscustomobject]@{'Results' = ('Successfully Connected to Hudu, version: {0}' -f $Version.version) } + } else { + $Results = [pscustomobject]@{'Results' = 'Failed to connect to Hudu' } + } } } } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionsConfig.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ExecExtensionsConfig.ps1 similarity index 93% rename from Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionsConfig.ps1 rename to Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ExecExtensionsConfig.ps1 index bc19a2b3940a..2484fd9885dd 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecExtensionsConfig.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ExecExtensionsConfig.ps1 @@ -56,7 +56,11 @@ Function Invoke-ExecExtensionsConfig { $null = Set-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name $APIKey -SecretValue (ConvertTo-SecureString -AsPlainText -Force -String $Request.Body.$APIKey.APIKey) } } - $Request.Body.$APIKey.APIKey = 'SentToKeyVault' + if ($Request.Body.$APIKey.PSObject.Properties -notcontains 'APIKey') { + $Request.Body.$APIKey | Add-Member -MemberType NoteProperty -Name APIKey -Value 'SentToKeyVault' + } else { + $Request.Body.$APIKey.APIKey = 'SentToKeyVault' + } } $Request.Body.$APIKey = $Request.Body.$APIKey | Select-Object * -ExcludeProperty ResetPassword } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ListExtensionSync.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ListExtensionSync.ps1 new file mode 100644 index 000000000000..8ccf26abd1cc --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Extensions/Invoke-ListExtensionSync.ps1 @@ -0,0 +1,59 @@ +using namespace System.Net + +Function Invoke-ListExtensionSync { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + CIPP.Extension.Read + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + # Write to the Azure Functions log stream. + Write-Host 'PowerShell HTTP trigger function processed a request.' + $ScheduledTasksTable = Get-CIPPTable -TableName 'ScheduledTasks' + $ScheduledTasks = Get-CIPPAzDataTableEntity @ScheduledTasksTable -Filter 'Hidden eq true' | Where-Object { $_.Command -match 'CippExtension' } + + $AllowedTenants = Test-CIPPAccess -Request $Request -TenantList + $TenantList = Get-Tenants -IncludeErrors + $AllTasksArrayList = [system.collections.generic.list[object]]::new() + + foreach ($Task in $ScheduledTasks) { + if ($Task.Results -and (Test-Json -Json $Task.Results -ErrorAction SilentlyContinue)) { + $Results = $Task.Results | ConvertFrom-Json + } else { + $Results = $Task.Results + } + + $TaskEntry = [PSCustomObject]@{ + RowKey = $Task.RowKey + PartitionKey = $Task.PartitionKey + Tenant = $Task.Tenant + Name = $Task.Name + SyncType = $Task.SyncType + ScheduledTime = $Task.ScheduledTime + ExecutedTime = $Task.ExecutedTime + RepeatsEvery = $Task.Recurrence + Results = $Results + } + + if ($AllowedTenants -notcontains 'AllTenants') { + $Tenant = $TenantList | Where-Object -Property defaultDomainName -EQ $Task.Tenant + if ($AllowedTenants -contains $Tenant.customerId) { + $AllTasksArrayList.Add($TaskEntry) + } + } else { + $AllTasksArrayList.Add($TaskEntry) + } + } + Write-Host ($AllTasksArrayList | ConvertTo-Json -Depth 5 -Compress) + # Associate values to output bindings by calling 'Push-OutputBinding'. + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = ConvertTo-Json -Depth 5 -InputObject $($AllTasksArrayList) + }) +} diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroupTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroupTemplate.ps1 index 83d8e9a64743..6574633a16c7 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroupTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroupTemplate.ps1 @@ -9,7 +9,6 @@ Function Invoke-AddGroupTemplate { #> [CmdletBinding()] param($Request, $TriggerMetadata) - $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' @@ -36,8 +35,7 @@ Function Invoke-AddGroupTemplate { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created Group template named $($Request.body.displayname) with GUID $GUID" -Sev 'Debug' $body = [pscustomobject]@{'Results' = 'Successfully added template' } - } - catch { + } catch { Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Group Template Creation failed: $($_.Exception.Message)" -Sev 'Error' $body = [pscustomobject]@{'Results' = "Group Template Creation failed: $($_.Exception.Message)" } } diff --git a/Modules/CIPPCore/Public/Get-CIPPAzDatatableEntity.ps1 b/Modules/CIPPCore/Public/Get-CIPPAzDatatableEntity.ps1 index 60ee9464ab93..fd5676683860 100644 --- a/Modules/CIPPCore/Public/Get-CIPPAzDatatableEntity.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPAzDatatableEntity.ps1 @@ -17,47 +17,55 @@ function Get-CIPPAzDataTableEntity { foreach ($entity in $Results) { if ($entity.OriginalEntityId) { $entityId = $entity.OriginalEntityId - if (-not $mergedResults.ContainsKey($entityId)) { - $mergedResults[$entityId] = @{ + $partitionKey = $entity.PartitionKey + if (-not $mergedResults.ContainsKey($partitionKey)) { + $mergedResults[$partitionKey] = @{} + } + if (-not $mergedResults[$partitionKey].ContainsKey($entityId)) { + $mergedResults[$partitionKey][$entityId] = @{ Parts = New-Object 'System.Collections.ArrayList' } } - $mergedResults[$entityId]['Parts'].Add($entity) > $null + $mergedResults[$partitionKey][$entityId]['Parts'].Add($entity) > $null } else { - $mergedResults[$entity.RowKey] = @{ + $partitionKey = $entity.PartitionKey + if (-not $mergedResults.ContainsKey($partitionKey)) { + $mergedResults[$partitionKey] = @{} + } + $mergedResults[$partitionKey][$entity.RowKey] = @{ Entity = $entity Parts = New-Object 'System.Collections.ArrayList' } } } - # Second pass: Reassemble entities from parts $finalResults = @() - foreach ($entityId in $mergedResults.Keys) { - $entityData = $mergedResults[$entityId] - if ($entityData.Parts.Count -gt 0) { - $fullEntity = [PSCustomObject]@{} - $parts = $entityData.Parts | Sort-Object PartIndex - foreach ($part in $parts) { - foreach ($key in $part.PSObject.Properties.Name) { - if ($key -notin @('OriginalEntityId', 'PartIndex', 'PartitionKey', 'RowKey', 'Timestamp')) { - if ($fullEntity.PSObject.Properties[$key]) { - $fullEntity | Add-Member -MemberType NoteProperty -Name $key -Value ($fullEntity.$key + $part.$key) -Force - } else { - $fullEntity | Add-Member -MemberType NoteProperty -Name $key -Value $part.$key + foreach ($partitionKey in $mergedResults.Keys) { + foreach ($entityId in $mergedResults[$partitionKey].Keys) { + $entityData = $mergedResults[$partitionKey][$entityId] + if ($entityData.Parts.Count -gt 0) { + $fullEntity = [PSCustomObject]@{} + $parts = $entityData.Parts | Sort-Object PartIndex + foreach ($part in $parts) { + foreach ($key in $part.PSObject.Properties.Name) { + if ($key -notin @('OriginalEntityId', 'PartIndex', 'PartitionKey', 'RowKey', 'Timestamp')) { + if ($fullEntity.PSObject.Properties[$key]) { + $fullEntity | Add-Member -MemberType NoteProperty -Name $key -Value ($fullEntity.$key + $part.$key) -Force + } else { + $fullEntity | Add-Member -MemberType NoteProperty -Name $key -Value $part.$key + } } } } + $fullEntity | Add-Member -MemberType NoteProperty -Name 'PartitionKey' -Value $parts[0].PartitionKey -Force + $fullEntity | Add-Member -MemberType NoteProperty -Name 'RowKey' -Value $entityId -Force + $finalResults = $finalResults + @($fullEntity) + } else { + $finalResults = $finalResults + @($entityData.Entity) } - $fullEntity | Add-Member -MemberType NoteProperty -Name 'PartitionKey' -Value $parts[0].PartitionKey -Force - $fullEntity | Add-Member -MemberType NoteProperty -Name 'RowKey' -Value $entityId -Force - $finalResults = $finalResults + @($fullEntity) - } else { - $finalResults = $finalResults + @($entityData.Entity) } } - # Third pass: Process split properties and remerge them foreach ($entity in $finalResults) { if ($entity.SplitOverProps) { $splitInfoList = $entity.SplitOverProps | ConvertFrom-Json diff --git a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 index fac7a03612a0..1217dc4886e6 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 @@ -23,7 +23,6 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc if ($cmdparams.anr) { $Anchor = $cmdparams.anr } if ($cmdparams.User) { $Anchor = $cmdparams.User } if ($cmdparams.mailbox) { $Anchor = $cmdparams.mailbox } - if ($cmdlet -eq 'Set-AdminAuditLogConfig') { $anchor = "UPN:SystemMailbox{8cc370d3-822a-4ab8-a926-bb94bd0641a9}@$($OnMicrosoft)" } if (!$Anchor -or $useSystemMailbox) { if (!$Tenant.initialDomainName -or $Tenant.initialDomainName -notlike '*onmicrosoft.com*') { $OnMicrosoft = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains?$top=999' -tenantid $tenantid -NoAuthCheck $NoAuthCheck | Where-Object -Property isInitial -EQ $true).id @@ -31,6 +30,8 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc $OnMicrosoft = $Tenant.initialDomainName } $anchor = "UPN:SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c}@$($OnMicrosoft)" + if ($cmdlet -in 'Set-AdminAuditLogConfig', 'Get-AdminAuditLogConfig', 'Enable-OrganizationCustomization', 'Get-OrganizationConfig') { $anchor = "UPN:SystemMailbox{8cc370d3-822a-4ab8-a926-bb94bd0641a9}@$($OnMicrosoft)" } + } #if the anchor is a GUID, try looking up the user. if ($Anchor -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') { @@ -78,4 +79,4 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc } else { Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope' } -} \ 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 f73051f931a6..0e295e96cc46 100644 --- a/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/Write-CippFunctionStats.ps1 @@ -11,12 +11,23 @@ function Write-CippFunctionStats { [string]$ErrorMsg = '' ) try { + $Start = Get-Date $Start + $End = Get-Date $End + $Table = Get-CIPPTable -tablename CippFunctionStats $RowKey = [string](New-Guid).Guid $TimeSpan = New-TimeSpan -Start $Start -End $End $Duration = [int]$TimeSpan.TotalSeconds $DurationMS = [int]$TimeSpan.TotalMilliseconds + # if datetime is local, convert to UTC + if ($Start.Kind -eq 'Local') { + $Start = $Start.ToUniversalTime() + } + if ($End.Kind -eq 'Local') { + $End = $End.ToUniversalTime() + } + $StatEntity = @{} # Flatten data to json string $StatEntity.PartitionKey = $FunctionType @@ -28,13 +39,16 @@ function Write-CippFunctionStats { $StatEntity.ErrorMsg = $ErrorMsg $Entity = [PSCustomObject]$Entity foreach ($Property in $Entity.PSObject.Properties.Name) { - if ($Entity.$Property.GetType().Name -in ('Hashtable', 'PSCustomObject', 'OrderedHashtable')) { - $StatEntity.$Property = [string]($Entity.$Property | ConvertTo-Json -Compress) - } elseif ($Property -notin ('ETag', 'RowKey', 'PartitionKey', 'Timestamp', 'LastRefresh')) { - $StatEntity.$Property = $Entity.$Property + if ($Entity.$Property) { + if ($Entity.$Property.GetType().Name -in ('Hashtable', 'PSCustomObject', 'OrderedHashtable')) { + $StatEntity.$Property = [string]($Entity.$Property | ConvertTo-Json -Compress) + } elseif ($Property -notin ('ETag', 'RowKey', 'PartitionKey', 'Timestamp', 'LastRefresh')) { + $StatEntity.$Property = $Entity.$Property + } } } $StatEntity = [PSCustomObject]$StatEntity + Add-CIPPAzDataTableEntity @Table -Entity $StatEntity -Force } catch { Write-Host "Exception logging stats $($_.Exception.Message)" diff --git a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 index 20620029e0e9..0c0af71bc86e 100644 --- a/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 +++ b/Modules/CIPPCore/Public/GraphRequests/Get-GraphRequestList.ps1 @@ -285,12 +285,17 @@ function Get-GraphRequestList { if (!$QueueThresholdExceeded) { $GraphRequestResults = New-GraphGetRequest @GraphRequest -ErrorAction Stop | Select-Object *, @{l = 'Tenant'; e = { $TenantFilter } }, @{l = 'CippStatus'; e = { 'Good' } } if ($ReverseTenantLookup -and $GraphRequestResults) { - $TenantInfo = $GraphRequestResults.$ReverseTenantLookupProperty | Sort-Object -Unique | ForEach-Object { - New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='$_')" -noauthcheck $true -asApp:$true -tenant $env:TenantId - } - foreach ($Result in $GraphRequestResults) { - $Result | Select-Object @{n = 'TenantInfo'; e = { $TenantInfo | Where-Object { $Result.$ReverseTenantLookupProperty -eq $_.tenantId } } }, * + $ReverseLookupRequests = $GraphRequestResults.$ReverseTenantLookupProperty | Sort-Object -Unique | ForEach-Object { + @{ + id = $_ + url = "tenantRelationships/findTenantInformationByTenantId(tenantId='$_')" + method = 'GET' + } } + $TenantInfo = New-GraphBulkRequest -Requests @($ReverseLookupRequests) -tenantid $env:TenantId -NoAuthCheck $true -asapp $true + + $GraphRequestResults | Select-Object @{n = 'TenantInfo'; e = { Get-GraphBulkResultByID -Results @($TenantInfo) -ID $_.$ReverseTenantLookupProperty } }, * + } else { $GraphRequestResults } @@ -306,4 +311,4 @@ function Get-GraphRequestList { $_.Data | ConvertFrom-Json } } -} \ No newline at end of file +} diff --git a/Modules/CIPPCore/Public/Set-CIPPUserJITAdminProperties.ps1 b/Modules/CIPPCore/Public/Set-CIPPUserJITAdminProperties.ps1 index c6203128f2f9..d9d3dfb8c9af 100644 --- a/Modules/CIPPCore/Public/Set-CIPPUserJITAdminProperties.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPUserJITAdminProperties.ps1 @@ -7,25 +7,28 @@ function Set-CIPPUserJITAdminProperties { $Expiration, [switch]$Clear ) - - $Schema = Get-CIPPSchemaExtensions | Where-Object { $_.id -match '_cippUser' } - if ($Clear.IsPresent) { - $Body = [PSCustomObject]@{ - "$($Schema.id)" = @{ - jitAdminEnabled = $null - jitAdminExpiration = $null + try { + $Schema = Get-CIPPSchemaExtensions | Where-Object { $_.id -match '_cippUser' } + if ($Clear.IsPresent) { + $Body = [PSCustomObject]@{ + "$($Schema.id)" = @{ + jitAdminEnabled = $null + jitAdminExpiration = $null + } } - } - } else { - $Body = [PSCustomObject]@{ - "$($Schema.id)" = @{ - jitAdminEnabled = $Enabled.IsPresent - jitAdminExpiration = $Expiration.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + } else { + $Body = [PSCustomObject]@{ + "$($Schema.id)" = @{ + jitAdminEnabled = $Enabled.IsPresent + jitAdminExpiration = $Expiration.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + } } } - } - $Json = ConvertTo-Json -Depth 5 -InputObject $Body - Write-Information $Json - New-GraphPOSTRequest -type PATCH -Uri "https://graph.microsoft.com/beta/users/$UserId" -Body $Json -tenantid $TenantFilter | Out-Null -} \ No newline at end of file + $Json = ConvertTo-Json -Depth 5 -InputObject $Body + Write-Information $Json + New-GraphPOSTRequest -type PATCH -Uri "https://graph.microsoft.com/beta/users/$UserId" -Body $Json -tenantid $TenantFilter | Out-Null + } catch { + Write-Information "Error setting JIT Admin properties: $($_.Exception.Message) - $($_.InvocationInfo.PositionMessage)" + } +} diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 994dab0633bf..aae4096c53a1 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -9,13 +9,14 @@ function Receive-CippHttpTrigger { $Request, $TriggerMetadata ) - + # Convert the request to a PSCustomObject because the httpContext is case sensitive since 7.3 + $Request = $Request | ConvertTo-Json -Depth 100 | ConvertFrom-Json Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName $FunctionName = 'Invoke-{0}' -f $Request.Params.CIPPEndpoint Write-Host "Function: $($Request.Params.CIPPEndpoint)" $HttpTrigger = @{ - Request = $Request + Request = [pscustomobject]($Request) TriggerMetadata = $TriggerMetadata } @@ -43,6 +44,7 @@ function Receive-CippHttpTrigger { function Receive-CippQueueTrigger { Param($QueueItem, $TriggerMetadata) + Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName $Start = (Get-Date).ToUniversalTime() $APIName = $TriggerMetadata.FunctionName @@ -144,7 +146,7 @@ function Receive-CippOrchestrationTrigger { function Receive-CippActivityTrigger { Param($Item) try { - $Start = (Get-Date).ToUniversalTime() + $Start = Get-Date Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName if ($Item.QueueId) { @@ -188,7 +190,7 @@ function Receive-CippActivityTrigger { } } - $End = (Get-Date).ToUniversalTime() + $End = Get-Date try { $Stats = @{ diff --git a/Modules/CippExtensions/Private/Hudu/Get-HuduLinkBlock.ps1 b/Modules/CippExtensions/Private/Hudu/Get-HuduLinkBlock.ps1 index 5afbaf90de19..69bc48342d06 100644 --- a/Modules/CippExtensions/Private/Hudu/Get-HuduLinkBlock.ps1 +++ b/Modules/CippExtensions/Private/Hudu/Get-HuduLinkBlock.ps1 @@ -1,3 +1,3 @@ function Get-HuduLinkBlock($URL, $Icon, $Title) { - return "" + return '' -f $URL, $Icon, $Title } diff --git a/Modules/CippExtensions/Public/Extension Functions/Sync-CippExtensionData.ps1 b/Modules/CippExtensions/Public/Extension Functions/Sync-CippExtensionData.ps1 index 6a02fdd2c6cb..98a08fe48f49 100644 --- a/Modules/CippExtensions/Public/Extension Functions/Sync-CippExtensionData.ps1 +++ b/Modules/CippExtensions/Public/Extension Functions/Sync-CippExtensionData.ps1 @@ -151,14 +151,14 @@ function Sync-CippExtensionData { ) } 'Mailboxes' { - $Select = 'id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses,WhenSoftDeleted,IsInactiveMailbox' + $Select = 'id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses,WhenSoftDeleted,IsInactiveMailbox,ProhibitSendQuota,ProhibitSendReceiveQuota,LitigationHoldEnabled,InPlaceHolds,HiddenFromAddressListsEnabled' $ExoRequest = @{ tenantid = $TenantFilter cmdlet = 'Get-Mailbox' cmdParams = @{} Select = $Select } - $Mailboxes = (New-ExoRequest @ExoRequest) | Select-Object id, ExchangeGuid, ArchiveGuid, WhenSoftDeleted, @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, + $Mailboxes = (New-ExoRequest @ExoRequest) | Select-Object id, ExchangeGuid, ArchiveGuid, WhenSoftDeleted, ProhibitSendQuota, ProhibitSendReceiveQuota, LitigationHoldEnabled, InplaceHolds, HiddenFromAddressListsEnabled, @{ Name = 'UPN'; Expression = { $_.'UserPrincipalName' } }, @{ Name = 'displayName'; Expression = { $_.'DisplayName' } }, @{ Name = 'primarySmtpAddress'; Expression = { $_.'PrimarySMTPAddress' } }, @@ -298,7 +298,7 @@ function Sync-CippExtensionData { } catch { $LastSync.Status = 'Failed' $LastSync.Error = [string](Get-CippException -Exception $_ | ConvertTo-Json -Compress) - throw "Failed to sync data: $($_.Exception.Message)" + throw "Failed to sync data: $(Get-NormalizedError -message $_.Exception.Message)" } finally { Add-CIPPAzDataTableEntity @Table -Entity $LastSync -Force } diff --git a/Modules/CippExtensions/Public/Hudu/Invoke-HuduExtensionSync.ps1 b/Modules/CippExtensions/Public/Hudu/Invoke-HuduExtensionSync.ps1 index 80f92e6ad662..77a429e2d0ca 100644 --- a/Modules/CippExtensions/Public/Hudu/Invoke-HuduExtensionSync.ps1 +++ b/Modules/CippExtensions/Public/Hudu/Invoke-HuduExtensionSync.ps1 @@ -35,8 +35,12 @@ function Invoke-HuduExtensionSync { $DeviceLayoutId = $Mappings | Where-Object { $_.RowKey -eq 'Devices' } | Select-Object -ExpandProperty IntegrationId $CreateDevices = $Configuration.CreateMissingDevices - $null = Add-HuduAssetLayoutM365Field -AssetLayoutId $PeopleLayoutId - $null = Add-HuduAssetLayoutM365Field -AssetLayoutId $DeviceLayoutId + if ($PeopleLayoutId) { + $null = Add-HuduAssetLayoutM365Field -AssetLayoutId $PeopleLayoutId + } + if ($DeviceLayoutId) { + $null = Add-HuduAssetLayoutM365Field -AssetLayoutId $DeviceLayoutId + } $importDomains = $false #$monitorDomains = [System.Convert]::ToBoolean($env:monitorDomains) @@ -60,33 +64,64 @@ function Invoke-HuduExtensionSync { try { $company_id = $TenantMap.IntegrationId - $PeopleLayout = Get-HuduAssetLayouts -Id $PeopleLayoutId - $People = Get-HuduAssets -CompanyId $company_id -AssetLayoutId $PeopleLayout.id + if ($PeopleLayoutId) { + $PeopleLayout = Get-HuduAssetLayouts -Id $PeopleLayoutId + $People = Get-HuduAssets -CompanyId $company_id -AssetLayoutId $PeopleLayout.id + } - $DesktopsLayout = Get-HuduAssetLayouts -Id $DeviceLayoutId - $HuduDesktopDevices = Get-HuduAssets -CompanyId $company_id -AssetLayoutId $DesktopsLayout.id + if ($DeviceLayoutId) { + $DesktopsLayout = Get-HuduAssetLayouts -Id $DeviceLayoutId + $HuduDesktopDevices = Get-HuduAssets -CompanyId $company_id -AssetLayoutId $DesktopsLayout.id + } $HuduRelations = Get-HuduRelations $HuduDevices = $HuduDesktopDevices - $CustomerLinks = "
-
-
-
-
-
-
-
" + $Links = @( + @{ + Title = 'M365 Admin Portal' + URL = 'https://admin.microsoft.com/Partner/BeginClientSession.aspx?CTID={0}&CSDEST=o365admincenter' -f $Tenant.customerId + Icon = 'fas fa-cogs' + } + @{ + Title = 'Exchange Admin Portal' + URL = 'https://outlook.office365.com/ecp/?rfr=Admin_o365&exsvurl=1&delegatedOrg={0}' -f $Tenant.initialDomainName + Icon = 'fas fa-mail-bulk' + } + @{ + Title = 'Entra Portal' + URL = 'https://entra.microsoft.com/{0}' -f $Tenant.defaultDomainName + Icon = 'fas fa-users-cog' + } + @{ + Title = 'Intune' + URL = 'https://intune.microsoft.com/{0}/' -f $Tenant.defaultDomainName + Icon = 'fas fa-laptop' + } + @{ + Title = 'Teams Portal' + URL = 'https://admin.teams.microsoft.com/?delegatedOrg={0}' -f $Tenant.defaultDomainName + Icon = 'fas fa-users' + } + @{ + Title = 'Azure Portal' + URL = 'https://portal.azure.com/{0}' -f $Tenant.defaultDomainName + Icon = 'fas fa-server' + } + ) + $FormattedLinks = foreach ($Link in $Links) { + Get-HuduLinkBlock @Link + } + + $CustomerLinks = $FormattedLinks -join "`n" - #$Users = Get-BulkResultByID -Results $TenantResults -ID 'Users' $Users = $ExtensionCache.Users $licensedUsers = $Users | Where-Object { $null -ne $_.assignedLicenses.skuId } | Sort-Object userPrincipalName $CompanyResult.users = ($licensedUsers | Measure-Object).count - #$AllRoles = Get-BulkResultByID -Results $TenantResults -ID 'AllRoles' $AllRoles = $ExtensionCache.AllRoles @@ -161,7 +196,6 @@ function Invoke-HuduExtensionSync { " - #$Licenses = Get-BulkResultByID -Results $TenantResults -ID 'Licenses' $Licenses = $ExtensionCache.Licenses if ($Licenses) { $pre = "
@@ -174,27 +208,10 @@ function Invoke-HuduExtensionSync { $licenseHTML = $licenseOut | ConvertTo-Html -PreContent $pre -PostContent $post -Fragment | Out-String } - #$devices = Get-BulkResultByID -Results $TenantResults -ID 'Devices' $devices = $ExtensionCache.Devices $CompanyResult.Devices = ($Devices | Measure-Object).count - #$DeviceCompliancePolicies = Get-BulkResultByID -Results $TenantResults -ID 'DeviceCompliancePolicies' $DeviceCompliancePolicies = $ExtensionCache.DeviceCompliancePolicies - <#[System.Collections.Generic.List[PSCustomObject]]$PolicyRequestArray = @() - foreach ($CompliancePolicy in $DeviceCompliancePolicies) { - $PolicyRequestArray.add(@{ - id = $CompliancePolicy.id - method = 'GET' - url = "/deviceManagement/deviceCompliancePolicies/$($CompliancePolicy.id)/deviceStatuses" - }) - } - - try { - $PolicyReturn = New-GraphBulkRequest -Headers $AuthHeaders -Requests $PolicyRequestArray -tenantid $TenantFilter - } catch { - $CompanyResult.Errors.add("Company: Unable to fetch Policies $_") - $PolicyReturn = $null - }#> $DeviceComplianceDetails = foreach ($Policy in $DeviceCompliancePolicies) { $DeviceStatuses = $ExtensionCache."DeviceCompliancePolicy_$($Policy.id)" @@ -230,24 +247,8 @@ function Invoke-HuduExtensionSync { } } #> - #$AllGroups = Get-BulkResultByID -Results $TenantResults -ID 'Groups' $AllGroups = $ExtensionCache.Groups - <#[System.Collections.Generic.List[PSCustomObject]]$GroupRequestArray = @() - foreach ($Group in $AllGroups) { - $GroupRequestArray.add(@{ - id = $Group.id - method = 'GET' - url = "/groups/$($Group.id)/members" - }) - } - try { - $GroupMembersReturn = New-GraphBulkRequest -Headers $AuthHeaders -Requests $GroupRequestArray -tenantid $TenantFilter - } catch { - $CompanyResult.Errors.add("Company: Unable to fetch Group Membership Details $_") - $GroupMembersReturn = $null - }#> - $Groups = foreach ($Group in $AllGroups) { $Members = $ExtensionCache."Groups_$($Result.id)" [pscustomobject]@{ @@ -257,7 +258,6 @@ function Invoke-HuduExtensionSync { } } - #$AllConditionalAccessPolicies = Get-BulkResultByID -Results $TenantResults -ID 'ConditionalAccess' $AllConditionalAccessPolicies = $ExtensionCache.ConditionalAccess $ConditionalAccessMembers = foreach ($CAPolicy in $AllConditionalAccessPolicies) { @@ -307,19 +307,13 @@ function Invoke-HuduExtensionSync { } if ($ExtensionCache.OneDriveUsage) { - #$OneDriveDetails = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getOneDriveUsageAccountDetail(period='D7')" -tenantid $TenantFilter | ConvertFrom-Csv $OneDriveDetails = $ExtensionCache.OneDriveUsage } else { $CompanyResult.Errors.add("Company: Unable to fetch One Drive Details $_") $OneDriveDetails = $null } - <#try { - $CASFull = New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($tenantfilter)/CasMailbox" -Tenantid $tenantfilter -scope ExchangeOnline -noPagination $true - } catch { - $CASFull = $null - $CompanyResult.Errors.add("Company: Unable to fetch CAS Mailbox Details $_") - }#> + if ($ExtensionCache.CASMailbox) { $CASFull = $ExtensionCache.CASMailbox @@ -329,21 +323,16 @@ function Invoke-HuduExtensionSync { } - <#try { - $MailboxDetailedFull = New-ExoRequest -TenantID $TenantFilter -cmdlet 'Get-Mailbox' - } catch { - $CompanyResult.Errors.add("Company: Unable to fetch Mailbox Details $_") + + + if ($ExtensionCache.Mailboxes) { + $MailboxDetailedFull = $ExtensionCache.Mailboxes + } else { + $CompanyResult.Errors.add('Company: Unable to fetch Mailbox Details') $MailboxDetailedFull = $null - }#> - $MailboxDetailedFull = $ExtensionCache.Mailboxes + } - <#try { - $MailboxStatsFull = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/reports/getMailboxUsageDetail(period='D7')" -tenantid $TenantFilter | ConvertFrom-Csv - } catch { - $CompanyResult.Errors.add("Company: Unable to fetch Mailbox Statistic Details $_") - $MailboxStatsFull = $null - }#> if ($ExtensionCache.MailboxUsage) { $MailboxStatsFull = $ExtensionCache.MailboxUsage } else { @@ -389,15 +378,11 @@ function Invoke-HuduExtensionSync { $MailboxDetailedRequest = '' $CASRequest = '' - $CASRequest = $CASFull | Where-Object { $_.ExternalDirectoryObjectId -eq $User.iD } - $MailboxDetailedRequest = $MailboxDetailedFull | Where-Object { $_.ExternalDirectoryObjectId -eq $User.iD } + $CASRequest = $CASFull | Where-Object { $_.ExternalDirectoryObjectId -eq $User.id } + $MailboxDetailedRequest = $MailboxDetailedFull | Where-Object { $_.Id -eq $User.id } $StatsRequest = $MailboxStatsFull | Where-Object { $_.'userPrincipalName' -eq $User.UserPrincipalName } - <#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) { @@ -630,44 +615,45 @@ function Invoke-HuduExtensionSync { $UserBody = "
$AssignedPlansBlock
$UserLinksBlock
$($UserOverviewBlock)$($UserMailDetailsBlock)$($OneDriveBlock)$($UserMailSettingsBlock)$($UserPoliciesBlock)
$($UserDevicesDetailsBlock)
$($UserGroupsBlock)
" - $UserAssetFields = @{ - microsoft_365 = $UserBody - email_address = $user.UserPrincipalName - } - $NewHash = Get-StringHash -String $UserBody - - $HuduUserCount = ($HuduUser | Measure-Object).count - if ($HuduUserCount -eq 1) { - $ExistingAsset = Get-CIPPAzDataTableEntity @HuduAssetCache -Filter "PartitionKey eq 'HuduUser' and CompanyId eq $company_id and RowKey eq '$($HuduUser.id)'" - $ExistingHash = $ExistingAsset.Hash - - if (!$ExistingAsset -or $ExistingHash -ne $NewHash) { - $null = Set-HuduAsset -asset_id $HuduUser.id -Name $HuduUser.name -company_id $company_id -asset_layout_id $PeopleLayout.id -Fields $UserAssetFields - $AssetCache = [PSCustomObject]@{ - PartitionKey = 'HuduUser' - RowKey = [string]$HuduUser.id - CompanyId = [string]$company_id - Hash = [string]$NewHash - } - Add-CIPPAzDataTableEntity @HuduAssetCache -Entity $AssetCache -Force + if ($PeopleLayoutId) { + $UserAssetFields = @{ + microsoft_365 = $UserBody + email_address = $user.UserPrincipalName } + $NewHash = Get-StringHash -String $UserBody + + $HuduUserCount = ($HuduUser | Measure-Object).count + if ($HuduUserCount -eq 1) { + $ExistingAsset = Get-CIPPAzDataTableEntity @HuduAssetCache -Filter "PartitionKey eq 'HuduUser' and CompanyId eq '$company_id' and RowKey eq '$($HuduUser.id)'" + $ExistingHash = $ExistingAsset.Hash + + if (!$ExistingAsset -or $ExistingHash -ne $NewHash) { + $null = Set-HuduAsset -asset_id $HuduUser.id -Name $HuduUser.name -company_id $company_id -asset_layout_id $PeopleLayout.id -Fields $UserAssetFields + $AssetCache = [PSCustomObject]@{ + PartitionKey = 'HuduUser' + RowKey = [string]$HuduUser.id + CompanyId = [string]$company_id + Hash = [string]$NewHash + } + Add-CIPPAzDataTableEntity @HuduAssetCache -Entity $AssetCache -Force + } - } elseif ($HuduUserCount -eq 0) { - if ($CreateUsers -eq $True) { - $HuduUser = (New-HuduAsset -Name $User.DisplayName -company_id $company_id -asset_layout_id $PeopleLayout.id -Fields $UserAssetFields -primary_mail $user.UserPrincipalName).asset - $AssetCache = [PSCustomObject]@{ - PartitionKey = 'HuduUser' - RowKey = [string]$HuduUser.id - CompanyId = [string]$company_id - Hash = [string]$NewHash + } elseif ($HuduUserCount -eq 0) { + if ($CreateUsers -eq $True) { + $HuduUser = (New-HuduAsset -Name $User.DisplayName -company_id $company_id -asset_layout_id $PeopleLayout.id -Fields $UserAssetFields -primary_mail $user.UserPrincipalName).asset + $AssetCache = [PSCustomObject]@{ + PartitionKey = 'HuduUser' + RowKey = [string]$HuduUser.id + CompanyId = [string]$company_id + Hash = [string]$NewHash + } + Add-CIPPAzDataTableEntity @HuduAssetCache -Entity $AssetCache -Force } - Add-CIPPAzDataTableEntity @HuduAssetCache -Entity $AssetCache -Force + } else { + $CompanyResult.Errors.add("User $($User.UserPrincipalName): Multiple Users Matched to email address in Hudu: ($($HuduUser.name -join ', ') - $($($HuduUser.id -join ', '))) $_") } - } else { - $CompanyResult.Errors.add("User $($User.UserPrincipalName): Multiple Users Matched to email address in Hudu: ($($HuduUser.name -join ', ') - $($($HuduUser.id -join ', '))) $_") } - $UserLink = "$($user.DisplayName)" [PSCustomObject]@{ @@ -795,13 +781,47 @@ function Invoke-HuduExtensionSync { } $NewHash = Get-StringHash -String $DeviceIntuneDetailshtml - if ($HuduDevice) { - if (($HuduDevice | Measure-Object).count -eq 1) { - $ExistingAsset = Get-CIPPAzDataTableEntity @HuduAssetCache -Filter "PartitionKey eq 'HuduDevice' and CompanyId eq $company_id and RowKey eq '$($HuduDevice.id)'" - $ExistingHash = $ExistingAsset.Hash + if ($DeviceLayoutId) { + if ($HuduDevice) { + if (($HuduDevice | Measure-Object).count -eq 1) { + $ExistingAsset = Get-CIPPAzDataTableEntity @HuduAssetCache -Filter "PartitionKey eq 'HuduDevice' and CompanyId eq '$company_id' and RowKey eq '$($HuduDevice.id)'" + $ExistingHash = $ExistingAsset.Hash + + if (!$ExistingAsset -or $ExistingAsset.Hash -ne $NewHash) { + $null = Set-HuduAsset -asset_id $HuduDevice.id -Name $HuduDevice.name -company_id $company_id -asset_layout_id $HuduDevice.asset_layout_id -Fields $DeviceAssetFields -PrimarySerial $Device.serialNumber + $AssetCache = [PSCustomObject]@{ + PartitionKey = 'HuduDevice' + RowKey = [string]$HuduDevice.id + CompanyId = [string]$company_id + Hash = [string]$NewHash + } + Add-CIPPAzDataTableEntity @HuduAssetCache -Entity $AssetCache -Force + + $HuduUser = $People | Where-Object { $_.primary_mail -eq $Device.userPrincipalName -or ($_.cards.integrator_name -eq 'cw_manage' -and $_.cards.data.communicationItems.communicationType -eq 'Email' -and $_.cards.data.communicationItems.value -eq $Device.userPrincipalName) } + + if ($HuduUser) { + $Relation = $HuduRelations | Where-Object { $_.fromable_type -eq 'Asset' -and $_.fromable_id -eq $HuduUser.id -and $_.toable_type -eq 'Asset' -and $_toable_id -eq $HuduDevice.id } + if (-not $Relation) { + try { + $null = New-HuduRelation -FromableType 'Asset' -FromableID $HuduUser.id -ToableType 'Asset' -ToableID $HuduDevice.id -ea stop + } catch {} + } + } + } + } else { + $CompanyResult.Errors.add("Device $($HuduDevice.name): Multiple devices matched on name or serial ($($device.serialNumber -join ', '))") + } + } else { + if ($device.deviceType -in $IntuneDesktopDeviceTypes) { + $DeviceLayoutID = $DesktopsLayout.id + $DeviceCreation = $CreateDevices + } else { + $DeviceLayoutID = $MobilesLayout.id + $DeviceCreation = $CreateMobileDevices + } + if ($DeviceCreation -eq $true) { + $HuduDevice = (New-HuduAsset -Name $device.deviceName -company_id $company_id -asset_layout_id $DeviceLayoutID -Fields $DeviceAssetFields -PrimarySerial $Device.serialNumber).asset - if (!$ExistingAsset -or $ExistingAsset.Hash -ne $NewHash) { - $null = Set-HuduAsset -asset_id $HuduDevice.id -Name $HuduDevice.name -company_id $company_id -asset_layout_id $HuduDevice.asset_layout_id -Fields $DeviceAssetFields -PrimarySerial $Device.serialNumber $AssetCache = [PSCustomObject]@{ PartitionKey = 'HuduDevice' RowKey = [string]$HuduDevice.id @@ -811,46 +831,14 @@ function Invoke-HuduExtensionSync { Add-CIPPAzDataTableEntity @HuduAssetCache -Entity $AssetCache -Force $HuduUser = $People | Where-Object { $_.primary_mail -eq $Device.userPrincipalName -or ($_.cards.integrator_name -eq 'cw_manage' -and $_.cards.data.communicationItems.communicationType -eq 'Email' -and $_.cards.data.communicationItems.value -eq $Device.userPrincipalName) } - if ($HuduUser) { - $Relation = $HuduRelations | Where-Object { $_.fromable_type -eq 'Asset' -and $_.fromable_id -eq $HuduUser.id -and $_.toable_type -eq 'Asset' -and $_toable_id -eq $HuduDevice.id } - if (-not $Relation) { - try { - $null = New-HuduRelation -FromableType 'Asset' -FromableID $HuduUser.id -ToableType 'Asset' -ToableID $HuduDevice.id -ea stop - } catch {} + try { + $null = New-HuduRelation -FromableType 'Asset' -FromableID $HuduUser.id -ToableType 'Asset' -ToableID $HuduDevice.id -ea stop + } catch { + # No need to do anything here as its will be when relations already exist. } } } - } else { - $CompanyResult.Errors.add("Device $($HuduDevice.name): Multiple devices matched on name or serial ($($device.serialNumber -join ', '))") - } - } else { - if ($device.deviceType -in $IntuneDesktopDeviceTypes) { - $DeviceLayoutID = $DesktopsLayout.id - $DeviceCreation = $CreateDevices - } else { - $DeviceLayoutID = $MobilesLayout.id - $DeviceCreation = $CreateMobileDevices - } - if ($DeviceCreation -eq $true) { - $HuduDevice = (New-HuduAsset -Name $device.deviceName -company_id $company_id -asset_layout_id $DeviceLayoutID -Fields $DeviceAssetFields -PrimarySerial $Device.serialNumber).asset - - $AssetCache = [PSCustomObject]@{ - PartitionKey = 'HuduDevice' - RowKey = [string]$HuduDevice.id - CompanyId = [string]$company_id - Hash = [string]$NewHash - } - Add-CIPPAzDataTableEntity @HuduAssetCache -Entity $AssetCache -Force - - $HuduUser = $People | Where-Object { $_.primary_mail -eq $Device.userPrincipalName -or ($_.cards.integrator_name -eq 'cw_manage' -and $_.cards.data.communicationItems.communicationType -eq 'Email' -and $_.cards.data.communicationItems.value -eq $Device.userPrincipalName) } - if ($HuduUser) { - try { - $null = New-HuduRelation -FromableType 'Asset' -FromableID $HuduUser.id -ToableType 'Asset' -ToableID $HuduDevice.id -ea stop - } catch { - # No need to do anything here as its will be when relations already exist. - } - } } } } catch { @@ -864,7 +852,7 @@ function Invoke-HuduExtensionSync {

Administrative Portals

-
$CustomerLinks
+
$CustomerLinks


diff --git a/Modules/CippExtensions/Public/New-CippExtAlert.ps1 b/Modules/CippExtensions/Public/New-CippExtAlert.ps1 index 21f5acf1923e..54f9faf81258 100644 --- a/Modules/CippExtensions/Public/New-CippExtAlert.ps1 +++ b/Modules/CippExtensions/Public/New-CippExtAlert.ps1 @@ -8,14 +8,15 @@ function New-CippExtAlert { $Table = Get-CIPPTable -TableName Extensionsconfig $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 $MappingTable = Get-CIPPTable -TableName CippMapping - $MappingFile = (Get-CIPPAzDataTableEntity @MappingTable) + foreach ($ConfigItem in $Configuration.psobject.properties.name) { switch ($ConfigItem) { 'HaloPSA' { If ($Configuration.HaloPSA.enabled) { + $MappingFile = Get-CIPPAzDataTableEntity @MappingTable -Filter 'PartitionKey eq HaloMapping' $TenantId = (Get-Tenants | Where-Object defaultDomainName -EQ $Alert.TenantId).customerId Write-Host "TenantId: $TenantId" - $MappedId = ($MappingFile | Where-Object { $_.PartitionKey -eq 'HaloMapping' -and $_.RowKey -eq $TenantId }).IntegrationId + $MappedId = ($MappingFile | Where-Object { $_.RowKey -eq $TenantId }).IntegrationId Write-Host "MappedId: $MappedId" if (!$mappedId) { $MappedId = 1 } Write-Host "MappedId: $MappedId" @@ -30,4 +31,4 @@ function New-CippExtAlert { } } -} \ No newline at end of file +} diff --git a/Scheduler_GetWebhooks/run.ps1 b/Scheduler_GetWebhooks/run.ps1 index 3eb4aaae42fd..cef8cfb6d726 100644 --- a/Scheduler_GetWebhooks/run.ps1 +++ b/Scheduler_GetWebhooks/run.ps1 @@ -3,7 +3,7 @@ param($Timer) try { $webhookTable = Get-CIPPTable -tablename webhookTable - $Webhooks = Get-CIPPAzDataTableEntity @webhookTable -Property RowKey + $Webhooks = Get-CIPPAzDataTableEntity @webhookTable -Property PartitionKey, RowKey if (($Webhooks | Measure-Object).Count -eq 0) { Write-Host 'No webhook subscriptions found. Exiting.' return diff --git a/Scheduler_UserTasks/function.json b/Scheduler_UserTasks/function.json index 017acb166958..f7af84092121 100644 --- a/Scheduler_UserTasks/function.json +++ b/Scheduler_UserTasks/function.json @@ -2,7 +2,7 @@ "bindings": [ { "name": "Timer", - "schedule": "0 */5 * * * *", + "schedule": "0 */15 * * * *", "direction": "in", "type": "timerTrigger" }, diff --git a/Scheduler_UserTasks/run.ps1 b/Scheduler_UserTasks/run.ps1 index b5d5cdd874ca..950ca9e691bb 100644 --- a/Scheduler_UserTasks/run.ps1 +++ b/Scheduler_UserTasks/run.ps1 @@ -57,6 +57,11 @@ foreach ($task in $tasks) { } } if (($Batch | Measure-Object).Count -gt 0) { + # Create queue entry + $Queue = New-CippQueueEntry -Name 'Scheduled Tasks' -TotalTasks ($Batch | Measure-Object).Count + $QueueId = $Queue.RowKey + $Batch = $Batch | Select-Object *, @{Name = 'QueueId'; Expression = { $QueueId } }, @{Name = 'QueueName'; Expression = { '{0} - {1}' -f $_.TaskInfo.Name, ($_.TaskInfo.Tenant -ne 'AllTenants' ? $_.TaskInfo.Tenant : $_.Parameters.TenantFilter) } } + $InputObject = [PSCustomObject]@{ OrchestratorName = 'UserTaskOrchestrator' Batch = @($Batch) @@ -66,4 +71,4 @@ if (($Batch | Measure-Object).Count -gt 0) { $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 10 -Compress) Write-Host "Started orchestration with ID = '$InstanceId'" -} \ No newline at end of file +} diff --git a/version_latest.txt b/version_latest.txt index 09b254e90c61..090ea9dad19f 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -6.0.0 +6.0.3