From 4f9aa04233568651e3bc45756add4c36d4df1d39 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:04:04 +0200 Subject: [PATCH 01/19] fixes bpa sync --- .../HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 index a3484765099a..06731cb29663 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecBPA.ps1 @@ -34,7 +34,7 @@ function Invoke-ExecBPA { $Results = [pscustomobject]@{'Results' = 'BPA queued for execution' } } } else { - Start-BPAOrchestrator -TenantFilter $Request.Query.TenantFilter + Start-BPAOrchestrator -TenantFilter $TenantFilter $Results = [pscustomobject]@{'Results' = 'BPA started' } } From 64ff3bb3ddd15aa8ec09347ce12e4c695a3bda01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Mon, 14 Apr 2025 17:49:36 +0200 Subject: [PATCH 02/19] fix: logging of the user calling the function not working after the headers update --- .../Users/Invoke-ExecPerUserMFA.ps1 | 34 ++++++++++++------- .../Users/Invoke-ExecPerUserMFAAllUsers.ps1 | 23 ++++++++----- .../CIPPCore/Public/Set-CIPPPerUserMFA.ps1 | 14 ++++---- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 index 5dbea9130edd..c1669879e605 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 @@ -6,23 +6,31 @@ function Invoke-ExecPerUserMFA { .ROLE Identity.User.ReadWrite #> - Param( - $Request, - $TriggerMetadata - ) + Param($Request, $TriggerMetadata) + + $APIName = $Request.Params.CIPPEndpoint + $Headers = $Request.Headers + Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + $Request = @{ - userId = $Request.Body.userId - TenantFilter = $Request.Body.TenantFilter - State = $Request.Body.State.value ? $Request.Body.State.value : $Request.Body.State - Headers = $Request.Headers + userId = $Request.Body.userId + TenantFilter = $Request.Body.tenantFilter + State = $Request.Body.State.value ? $Request.Body.State.value : $Request.Body.State + Headers = $Request.Headers + APIName = $APIName } - $Result = Set-CIPPPerUserMFA @Request - $Body = @{ - Results = @($Result) + try { + $Result = Set-CIPPPerUserMFA @Request + $StatusCode = [HttpStatusCode]::OK + } catch { + $Result = $_.Exception.Message + $StatusCode = [HttpStatusCode]::InternalServerError } + + # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Body + StatusCode = $StatusCode + Body = @{ 'Results' = @($Result) } }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFAAllUsers.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFAAllUsers.ps1 index f1eec308807f..7a7c296b4016 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFAAllUsers.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFAAllUsers.ps1 @@ -6,17 +6,22 @@ function Invoke-ExecPerUserMFAAllUsers { .ROLE Identity.User.ReadWrite #> - Param( - $Request, - $TriggerMetadata - ) - $TenantFilter = $request.query.TenantFilter + Param($Request, $TriggerMetadata) + + $APIName = $Request.Params.CIPPEndpoint + $Headers = $Request.Headers + Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + # XXX Seems to be an unused endpoint? - Bobby + + $TenantFilter = $request.Query.tenantFilter $Users = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users' -tenantid $TenantFilter $Request = @{ - userId = $Users.id - TenantFilter = $tenantfilter - State = $Request.query.State - Headers = $Request.Headers + userId = $Users.id + TenantFilter = $TenantFilter + State = $Request.Query.State + Headers = $Request.Headers + APIName = $APIName } $Result = Set-CIPPPerUserMFA @Request $Body = @{ diff --git a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 index 0cb76246c828..7d5642fe37bc 100644 --- a/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPPerUserMFA.ps1 @@ -29,8 +29,10 @@ function Set-CIPPPerUserMFA { [string[]]$userId, [ValidateSet('enabled', 'disabled', 'enforced')] $State = 'enabled', - [string]$Headers = 'CIPP' + $Headers, + $APIName = 'Set-CIPPPerUserMFA' ) + try { $int = 0 $Body = @{ @@ -48,8 +50,7 @@ function Set-CIPPPerUserMFA { } } - $Requests = New-GraphBulkRequest -tenantid $tenantfilter -scope 'https://graph.microsoft.com/.default' -Requests @($Requests) -asapp $true - + $Requests = New-GraphBulkRequest -tenantid $TenantFilter -scope 'https://graph.microsoft.com/.default' -Requests @($Requests) -asapp $true "Successfully set Per user MFA State for $userId" $Users = foreach ($id in $userId) { @@ -61,10 +62,11 @@ function Set-CIPPPerUserMFA { } } Set-CIPPUserSchemaProperties -TenantFilter $TenantFilter -Users $Users - Write-LogMessage -headers $Headers -API 'Set-CIPPPerUserMFA' -message "Successfully set Per user MFA State to $State for $id" -Sev 'Info' -tenant $TenantFilter + Write-LogMessage -headers $Headers -API $APIName -message "Successfully set Per user MFA State to $State for $id" -Sev Info -tenant $TenantFilter } catch { $ErrorMessage = Get-CippException -Exception $_ - "Failed to set MFA State for $id. Error: $($ErrorMessage.NormalizedError)" - Write-LogMessage -headers $Headers -API 'Set-CIPPPerUserMFA' -message "Failed to set MFA State to $State for $id. Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -tenant $TenantFilter -LogData $ErrorMessage + $Result = "Failed to set MFA State to $State for $id. Error: $($ErrorMessage.NormalizedError)" + Write-LogMessage -headers $Headers -API $APIName -message $Result -Sev Error -tenant $TenantFilter -LogData $ErrorMessage + throw $Result } } From 6af751d7de8e20a385ce0725bb1d0f4df531d135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Mon, 14 Apr 2025 17:51:34 +0200 Subject: [PATCH 03/19] Tiny headers change --- .../Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 index c1669879e605..c22b0945e193 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1 @@ -17,7 +17,7 @@ function Invoke-ExecPerUserMFA { userId = $Request.Body.userId TenantFilter = $Request.Body.tenantFilter State = $Request.Body.State.value ? $Request.Body.State.value : $Request.Body.State - Headers = $Request.Headers + Headers = $Headers APIName = $APIName } try { From b7b96bf03bff39d02d0de04d4c2d38f0250c6b67 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 14 Apr 2025 13:00:11 -0400 Subject: [PATCH 04/19] fix CIPPURL --- .../Push-SchedulerCIPPNotifications.ps1 | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 index e684cc202f73..258ec34cd1c0 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 @@ -33,6 +33,12 @@ function Push-SchedulerCIPPNotifications { } Write-Information "Alerts: $($Currentlog.count) found" Write-Information "Standards: $($CurrentStandardsLogs.count) found" + + # Get the CIPP URL + $CippConfigTable = Get-CippTable -tablename Config + $CippConfig = Get-CIPPAzDataTableEntity @CippConfigTable -Filter "PartitionKey eq 'InstanceProperties' and RowKey eq 'CIPPURL'" + $CIPPURL = 'https://{0}' -f $CippConfig.Value + #email try try { if ($Config.email -like '*@*') { @@ -42,13 +48,13 @@ function Push-SchedulerCIPPNotifications { foreach ($tenant in ($CurrentLog.Tenant | Sort-Object -Unique)) { $Data = ($CurrentLog | Select-Object Message, API, Tenant, Username, Severity | Where-Object -Property tenant -EQ $tenant) $Subject = "$($Tenant): CIPP Alert: Alerts found starting at $((Get-Date).AddMinutes(-15))" - $HTMLContent = New-CIPPAlertTemplate -Data $Data -Format 'html' -InputObject 'table' + $HTMLContent = New-CIPPAlertTemplate -Data $Data -Format 'html' -InputObject 'table' -CIPPURL $CIPPURL Send-CIPPAlert -Type 'email' -Title $Subject -HTMLContent $HTMLContent.htmlcontent -TenantFilter $tenant -APIName 'Alerts' } } else { $Data = ($CurrentLog | Select-Object Message, API, Tenant, Username, Severity) $Subject = "CIPP Alert: Alerts found starting at $((Get-Date).AddMinutes(-15))" - $HTMLContent = New-CIPPAlertTemplate -Data $Data -Format 'html' -InputObject 'table' + $HTMLContent = New-CIPPAlertTemplate -Data $Data -Format 'html' -InputObject 'table' -CIPPURL $CIPPURL Send-CIPPAlert -Type 'email' -Title $Subject -HTMLContent $HTMLContent.htmlcontent -TenantFilter $tenant -APIName 'Alerts' } } @@ -56,7 +62,7 @@ function Push-SchedulerCIPPNotifications { foreach ($tenant in ($CurrentStandardsLogs.Tenant | Sort-Object -Unique)) { $Data = ($CurrentStandardsLogs | Where-Object -Property tenant -EQ $tenant) $Subject = "$($Tenant): Standards are out of sync for $tenant" - $HTMLContent = New-CIPPAlertTemplate -Data $Data -Format 'html' -InputObject 'standards' + $HTMLContent = New-CIPPAlertTemplate -Data $Data -Format 'html' -InputObject 'standards' -CIPPURL $CIPPURL Send-CIPPAlert -Type 'email' -Title $Subject -HTMLContent $HTMLContent.htmlcontent -TenantFilter $tenant -APIName 'Alerts' $updateStandards = $CurrentStandardsLogs | ForEach-Object { if ($_.PSObject.Properties.Name -contains 'sentAsAlert') { @@ -87,7 +93,7 @@ function Push-SchedulerCIPPNotifications { } if ($CurrentStandardsLogs) { - $JSONContent = New-CIPPAlertTemplate -Data $Data -Format 'json' -InputObject 'table' + $JSONContent = New-CIPPAlertTemplate -Data $Data -Format 'json' -InputObject 'table' -CIPPURL $CIPPURL $CurrentStandardsLogs | ConvertTo-Json -Compress Send-CIPPAlert -Type 'webhook' -JSONContent $JSONContent -TenantFilter $Tenant -APIName 'Alerts' $updateStandards = $CurrentStandardsLogs | ForEach-Object { @@ -110,7 +116,7 @@ function Push-SchedulerCIPPNotifications { try { foreach ($tenant in ($CurrentLog.Tenant | Sort-Object -Unique)) { $Data = ($CurrentLog | Select-Object Message, API, Tenant, Username, Severity | Where-Object -Property tenant -EQ $tenant) - $HTMLContent = New-CIPPAlertTemplate -Data $Data -Format 'html' -InputObject 'table' + $HTMLContent = New-CIPPAlertTemplate -Data $Data -Format 'html' -InputObject 'table' -CIPPURL $CIPPURL $Title = "$tenant CIPP Alert: Alerts found starting at $((Get-Date).AddMinutes(-15))" Send-CIPPAlert -Type 'psa' -Title $Title -HTMLContent $HTMLContent.htmlcontent -TenantFilter $tenant -APIName 'Alerts' $UpdateLogs = $CurrentLog | ForEach-Object { $_.SentAsAlert = $true; $_ } @@ -119,7 +125,7 @@ function Push-SchedulerCIPPNotifications { foreach ($standardsTenant in ($CurrentStandardsLogs.Tenant | Sort-Object -Unique)) { $Data = ($CurrentStandardsLogs | Where-Object -Property tenant -EQ $standardsTenant) $Subject = "$($standardsTenant): Standards are out of sync for $standardsTenant" - $HTMLContent = New-CIPPAlertTemplate -Data $Data -Format 'html' -InputObject 'standards' + $HTMLContent = New-CIPPAlertTemplate -Data $Data -Format 'html' -InputObject 'standards' -CIPPURL $CIPPURL Send-CIPPAlert -Type 'psa' -Title $Subject -HTMLContent $HTMLContent.htmlcontent -TenantFilter $standardsTenant -APIName 'Alerts' $updateStandards = $CurrentStandardsLogs | ForEach-Object { if ($_.PSObject.Properties.Name -contains 'sentAsAlert') { From 5c8cfa04c404e415d3593e07d2df014ca379c55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Mon, 14 Apr 2025 21:19:51 +0200 Subject: [PATCH 05/19] fix: add locale validation and fix setting locale if values are not null --- .../CIPPCore/Public/Set-CIPPMailboxLocale.ps1 | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPMailboxLocale.ps1 b/Modules/CIPPCore/Public/Set-CIPPMailboxLocale.ps1 index 5d3fde2bbdf1..d3d7fa740293 100644 --- a/Modules/CIPPCore/Public/Set-CIPPMailboxLocale.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPMailboxLocale.ps1 @@ -2,25 +2,32 @@ function Set-CippMailboxLocale { [CmdletBinding()] param ( $Headers, - $locale, - $username, + $Locale, + $Username, $APIName = 'Mailbox Locale', $TenantFilter ) try { + # Validate the locale. Also if the locale is not valid, it will throw an exception, not wasting a request. + if ([System.Globalization.CultureInfo]::GetCultureInfo($Locale).IsNeutralCulture) { + throw "$Locale is not a valid Locale. Neutral cultures are not supported." + } + $null = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Set-MailboxRegionalConfiguration' -cmdParams @{ - Identity = $username - Language = $locale + Identity = $Username + Language = $Locale LocalizeDefaultFolderName = $true + DateFormat = $null + TimeFormat = $null } -Anchor $username - $Result = "Set locale for $($username) to a $locale" - Write-LogMessage -headers $Headers -API $APIName -message $Result -Sev 'Info' -tenant $TenantFilter + $Result = "Set locale for $($Username) to $Locale" + Write-LogMessage -headers $Headers -API $APIName -message $Result -Sev Info -tenant $TenantFilter return $Result } catch { $ErrorMessage = Get-CippException -Exception $_ - $Result = "Could not set locale for $($username). Error: $($ErrorMessage.NormalizedError)" - Write-LogMessage -headers $Headers -API $APIName -message $Result -Sev 'Error' -tenant $TenantFilter -LogData $ErrorMessage + $Result = "Failed to set locale for $($Username). Error: $($ErrorMessage.NormalizedError)" + Write-LogMessage -headers $Headers -API $APIName -message $Result -Sev Error -tenant $TenantFilter -LogData $ErrorMessage throw $Result } } From 39ab54eea743cdc9ae571479847362f3ab75bf97 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:25:28 +0200 Subject: [PATCH 06/19] max depth --- .../Public/Compare-CIPPIntuneObject.ps1 | 92 ++++--------------- 1 file changed, 17 insertions(+), 75 deletions(-) diff --git a/Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1 b/Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1 index 03041d90cfa7..0361b7f80546 100644 --- a/Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1 +++ b/Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1 @@ -1,30 +1,4 @@ function Compare-CIPPIntuneObject { - <# - .SYNOPSIS - Compares two Intune objects and returns only the differences. - - .DESCRIPTION - This function takes two Intune objects and performs a comparison, returning only the properties that differ. - If no differences are found, it returns null. - It's useful for identifying changes between template objects and existing policies. - - .PARAMETER ReferenceObject - The reference Intune object to compare against. - - .PARAMETER DifferenceObject - The Intune object to compare with the reference object. - - .PARAMETER ExcludeProperties - Additional properties to exclude from the comparison. - - .EXAMPLE - $template = Get-CIPPIntunePolicy -tenantFilter $Tenant -DisplayName "Template Policy" -TemplateType "Device" - $existing = Get-CIPPIntunePolicy -tenantFilter $Tenant -DisplayName "Existing Policy" -TemplateType "Device" - $differences = Compare-CIPPIntuneObject -ReferenceObject $template -DifferenceObject $existing - - .NOTES - This function performs a comparison of objects, including nested properties. - #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] @@ -39,7 +13,6 @@ function Compare-CIPPIntuneObject { [string[]]$CompareType = @() ) if ($CompareType -ne 'Catalog') { - # Default properties to exclude from comparison $defaultExcludeProperties = @( 'id', 'createdDateTime', @@ -57,13 +30,9 @@ function Compare-CIPPIntuneObject { 'featureUpdatesPauseStartDate' ) - # Combine default and custom exclude properties $excludeProps = $defaultExcludeProperties + $ExcludeProperties - - # Create a list to store comparison results $result = [System.Collections.Generic.List[PSObject]]::new() - # Helper function to check if a property should be skipped function ShouldSkipProperty { param ( [string]$PropertyName @@ -73,25 +42,28 @@ function Compare-CIPPIntuneObject { $excludeProps -contains $PropertyName) } - # Recursive function to compare objects deeply function Compare-ObjectsRecursively { param ( - [Parameter(Mandatory = $true)] - $Object1, - - [Parameter(Mandatory = $true)] - $Object2, - - [Parameter(Mandatory = $false)] - [string]$PropertyPath = '' + [Parameter(Mandatory = $true)] $Object1, + [Parameter(Mandatory = $true)] $Object2, + [Parameter(Mandatory = $false)] [string]$PropertyPath = '', + [int]$Depth = 0, + [int]$MaxDepth = 15 ) - # If both objects are null or empty, they're equal + if ($Depth -ge $MaxDepth) { + $result.Add([PSCustomObject]@{ + Property = $PropertyPath + ExpectedValue = '[MaxDepthExceeded]' + ReceivedValue = '[MaxDepthExceeded]' + }) + return + } + if (($null -eq $Object1 -or $Object1 -eq '') -and ($null -eq $Object2 -or $Object2 -eq '')) { return } - # If one object is null but the other isn't, they're different if (($null -eq $Object1 -or $Object1 -eq '') -xor ($null -eq $Object2 -or $Object2 -eq '')) { $result.Add([PSCustomObject]@{ Property = $PropertyPath @@ -101,7 +73,6 @@ function Compare-CIPPIntuneObject { return } - # If objects are of different types, they're different if ($Object1.GetType() -ne $Object2.GetType()) { $result.Add([PSCustomObject]@{ Property = $PropertyPath @@ -111,9 +82,7 @@ function Compare-CIPPIntuneObject { return } - # Handle different object types if ($Object1 -is [System.Collections.IDictionary]) { - # Compare dictionaries $allKeys = @($Object1.Keys) + @($Object2.Keys) | Select-Object -Unique foreach ($key in $allKeys) { @@ -122,9 +91,8 @@ function Compare-CIPPIntuneObject { $newPath = if ($PropertyPath) { "$PropertyPath.$key" } else { $key } if ($Object1.ContainsKey($key) -and $Object2.ContainsKey($key)) { - #only run if both props are not null if ($Object1[$key] -and $Object2[$key]) { - Compare-ObjectsRecursively -Object1 $Object1[$key] -Object2 $Object2[$key] -PropertyPath $newPath + Compare-ObjectsRecursively -Object1 $Object1[$key] -Object2 $Object2[$key] -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth } } elseif ($Object1.ContainsKey($key)) { $result.Add([PSCustomObject]@{ @@ -141,14 +109,13 @@ function Compare-CIPPIntuneObject { } } } elseif ($Object1 -is [Array] -or $Object1 -is [System.Collections.IList]) { - # Compare arrays $maxLength = [Math]::Max($Object1.Count, $Object2.Count) for ($i = 0; $i -lt $maxLength; $i++) { $newPath = "$PropertyPath.$i" if ($i -lt $Object1.Count -and $i -lt $Object2.Count) { - Compare-ObjectsRecursively -Object1 $Object1[$i] -Object2 $Object2[$i] -PropertyPath $newPath + Compare-ObjectsRecursively -Object1 $Object1[$i] -Object2 $Object2[$i] -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth } elseif ($i -lt $Object1.Count) { $result.Add([PSCustomObject]@{ Property = $newPath @@ -164,7 +131,6 @@ function Compare-CIPPIntuneObject { } } } elseif ($Object1 -is [PSCustomObject] -or $Object1.PSObject.Properties.Count -gt 0) { - # Compare PSCustomObjects or objects with properties $allPropertyNames = @( $Object1.PSObject.Properties | Select-Object -ExpandProperty Name $Object2.PSObject.Properties | Select-Object -ExpandProperty Name @@ -178,9 +144,8 @@ function Compare-CIPPIntuneObject { $prop2Exists = $Object2.PSObject.Properties.Name -contains $propName if ($prop1Exists -and $prop2Exists) { - #only run if both props are not null if ($Object1.$propName -and $Object2.$propName) { - Compare-ObjectsRecursively -Object1 $Object1.$propName -Object2 $Object2.$propName -PropertyPath $newPath + Compare-ObjectsRecursively -Object1 $Object1.$propName -Object2 $Object2.$propName -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth } } elseif ($prop1Exists) { $result.Add([PSCustomObject]@{ @@ -197,7 +162,6 @@ function Compare-CIPPIntuneObject { } } } else { - # Compare primitive values $val1 = $Object1.ToString() $val2 = $Object2.ToString() @@ -211,7 +175,6 @@ function Compare-CIPPIntuneObject { } } - # Convert objects to PowerShell objects if they're not already $obj1 = if ($ReferenceObject -is [string]) { $ReferenceObject | ConvertFrom-Json -AsHashtable -Depth 100 } else { @@ -224,20 +187,16 @@ function Compare-CIPPIntuneObject { $DifferenceObject } - # Start the recursive comparison - #only do the compare if the objects are not null if ($obj1 -and $obj2) { Compare-ObjectsRecursively -Object1 $obj1 -Object2 $obj2 } - # If no differences found, return null if ($result.Count -eq 0) { return $null } } else { $intuneCollection = Get-Content .\intuneCollection.json | ConvertFrom-Json -ErrorAction SilentlyContinue - # Process reference object settings $referenceItems = $ReferenceObject.settings | ForEach-Object { $settingInstance = $_.settingInstance $intuneObj = $intuneCollection | Where-Object { $_.id -eq $settingInstance.settingDefinitionId } @@ -264,8 +223,6 @@ function Compare-CIPPIntuneObject { $child.choiceSettingValue.value } } - - # Add object to our temporary list [PSCustomObject]@{ Key = "GroupChild-$($child.settingDefinitionId)" Label = $childLabel @@ -331,7 +288,6 @@ function Compare-CIPPIntuneObject { $tempOutput } - # Process difference object settings $differenceItems = $DifferenceObject.settings | ForEach-Object { $settingInstance = $_.settingInstance $intuneObj = $intuneCollection | Where-Object { $_.id -eq $settingInstance.settingDefinitionId } @@ -358,8 +314,6 @@ function Compare-CIPPIntuneObject { $child.choiceSettingValue.value } } - - # Add object to our temporary list [PSCustomObject]@{ Key = "GroupChild-$($child.settingDefinitionId)" Label = $childLabel @@ -425,17 +379,14 @@ function Compare-CIPPIntuneObject { $tempOutput } - # Compare the items and create result $result = [System.Collections.Generic.List[PSObject]]::new() - # Group all items by Key for comparison $allKeys = @($referenceItems | Select-Object -ExpandProperty Key) + @($differenceItems | Select-Object -ExpandProperty Key) | Sort-Object -Unique foreach ($key in $allKeys) { $refItem = $referenceItems | Where-Object { $_.Key -eq $key } | Select-Object -First 1 $diffItem = $differenceItems | Where-Object { $_.Key -eq $key } | Select-Object -First 1 - # Get the setting definition ID from the key $settingId = $key if ($key -like 'Simple-*') { $settingId = $key.Substring(7) @@ -447,20 +398,15 @@ function Compare-CIPPIntuneObject { $settingId = $key.Substring(8) } - # Look up the setting in the collection $settingDefinition = $intuneCollection | Where-Object { $_.id -eq $settingId } - # Get the raw values $refRawValue = if ($refItem) { $refItem.Value } else { $null } $diffRawValue = if ($diffItem) { $diffItem.Value } else { $null } - # Try to translate the values to display names if they're option IDs $refValue = $refRawValue $diffValue = $diffRawValue - # If the setting has options, try to find the display name for the values if ($null -ne $settingDefinition -and $null -ne $settingDefinition.options) { - # For reference value if ($null -ne $refRawValue -and $refRawValue -match '_\d+$') { $option = $settingDefinition.options | Where-Object { $_.id -eq $refRawValue } if ($null -ne $option -and $null -ne $option.displayName) { @@ -468,7 +414,6 @@ function Compare-CIPPIntuneObject { } } - # For difference value if ($null -ne $diffRawValue -and $diffRawValue -match '_\d+$') { $option = $settingDefinition.options | Where-Object { $_.id -eq $diffRawValue } if ($null -ne $option -and $null -ne $option.displayName) { @@ -477,7 +422,6 @@ function Compare-CIPPIntuneObject { } } - # Use the display name for the property label if available $label = if ($null -ne $settingDefinition -and $null -ne $settingDefinition.displayName) { $settingDefinition.displayName } elseif ($refItem) { @@ -488,7 +432,6 @@ function Compare-CIPPIntuneObject { $key } - # Only add to result if values are different or one is missing if ($refRawValue -ne $diffRawValue -or $null -eq $refRawValue -or $null -eq $diffRawValue) { $result.Add([PSCustomObject]@{ Property = $label @@ -502,4 +445,3 @@ function Compare-CIPPIntuneObject { } return $result } - From f02f80d556e20ba120f4125429d8be71e49992ab Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:43:56 +0200 Subject: [PATCH 07/19] add short circuit for datetimes and other potential nested objects. --- .../Public/Compare-CIPPIntuneObject.ps1 | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1 b/Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1 index 0361b7f80546..579fdcd913f6 100644 --- a/Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1 +++ b/Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1 @@ -44,11 +44,16 @@ function Compare-CIPPIntuneObject { function Compare-ObjectsRecursively { param ( - [Parameter(Mandatory = $true)] $Object1, - [Parameter(Mandatory = $true)] $Object2, - [Parameter(Mandatory = $false)] [string]$PropertyPath = '', + [Parameter(Mandatory = $true)] + $Object1, + + [Parameter(Mandatory = $true)] + $Object2, + + [Parameter(Mandatory = $false)] + [string]$PropertyPath = '', [int]$Depth = 0, - [int]$MaxDepth = 15 + [int]$MaxDepth = 20 ) if ($Depth -ge $MaxDepth) { @@ -82,6 +87,21 @@ function Compare-CIPPIntuneObject { return } + # Short-circuit recursion for primitive types + $primitiveTypes = @([double], [decimal], [datetime], [timespan], [guid] ) + foreach ($type in $primitiveTypes) { + if ($Object1 -is $type -and $Object2 -is $type) { + if ($Object1 -ne $Object2) { + $result.Add([PSCustomObject]@{ + Property = $PropertyPath + ExpectedValue = $Object1 + ReceivedValue = $Object2 + }) + } + return + } + } + if ($Object1 -is [System.Collections.IDictionary]) { $allKeys = @($Object1.Keys) + @($Object2.Keys) | Select-Object -Unique @@ -197,6 +217,7 @@ function Compare-CIPPIntuneObject { } else { $intuneCollection = Get-Content .\intuneCollection.json | ConvertFrom-Json -ErrorAction SilentlyContinue + # Process reference object settings $referenceItems = $ReferenceObject.settings | ForEach-Object { $settingInstance = $_.settingInstance $intuneObj = $intuneCollection | Where-Object { $_.id -eq $settingInstance.settingDefinitionId } @@ -223,6 +244,8 @@ function Compare-CIPPIntuneObject { $child.choiceSettingValue.value } } + + # Add object to our temporary list [PSCustomObject]@{ Key = "GroupChild-$($child.settingDefinitionId)" Label = $childLabel @@ -288,6 +311,7 @@ function Compare-CIPPIntuneObject { $tempOutput } + # Process difference object settings $differenceItems = $DifferenceObject.settings | ForEach-Object { $settingInstance = $_.settingInstance $intuneObj = $intuneCollection | Where-Object { $_.id -eq $settingInstance.settingDefinitionId } @@ -314,6 +338,8 @@ function Compare-CIPPIntuneObject { $child.choiceSettingValue.value } } + + # Add object to our temporary list [PSCustomObject]@{ Key = "GroupChild-$($child.settingDefinitionId)" Label = $childLabel From 788e3f1ce26634fcf68aecf4dec63830002db9fe Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 14 Apr 2025 17:19:43 -0400 Subject: [PATCH 08/19] check settings for each template --- .../Standards/Invoke-CIPPStandardIntuneTemplate.ps1 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 index ed04a79091bd..332683e21ddb 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 @@ -122,8 +122,9 @@ function Invoke-CIPPStandardIntuneTemplate { } - if ($Settings.alert) { - foreach ($Template in $CompareList) { + if ($true -in $Settings.alert) { + foreach ($Template in $CompareList | Where-Object -Property alert -EQ $true) { + Write-Host "working on template alert: $($Template.displayname)" $AlertObj = $Template | Select-Object -Property displayname, description, compare, assignTo, excludeGroup, existingPolicyId if ($Template.compare) { Write-StandardsAlert -message "Template $($Template.displayname) does not match the expected configuration." -object $AlertObj -tenant $Tenant -standardName 'IntuneTemplate' -standardId $Settings.templateId @@ -139,8 +140,9 @@ function Invoke-CIPPStandardIntuneTemplate { } } - if ($Settings.report) { - foreach ($Template in $CompareList) { + if ($true -in $Settings.report) { + foreach ($Template in $CompareList | Where-Object -Property report -EQ $true) { + Write-Host "working on template report: $($Template.displayname)" $id = $Template.templateId $CompareObj = $Template.compare $state = $CompareObj ? $CompareObj : $true From bbb646dc17688f4e79474b0811a64d7004807603 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 14 Apr 2025 18:20:52 -0400 Subject: [PATCH 09/19] add actions to comparelist --- .../Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 index 332683e21ddb..43117a472282 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 @@ -84,6 +84,8 @@ function Invoke-CIPPStandardIntuneTemplate { assignTo = $Template.AssignTo excludeGroup = $Template.excludeGroup remediate = $Template.remediate + alert = $Template.alert + report = $Template.report existingPolicyId = $ExistingPolicy.id templateId = $Template.TemplateList.value customGroup = $Template.customGroup @@ -100,6 +102,8 @@ function Invoke-CIPPStandardIntuneTemplate { assignTo = $Template.AssignTo excludeGroup = $Template.excludeGroup remediate = $Template.remediate + alert = $Template.alert + report = $Template.report existingPolicyId = $ExistingPolicy.id templateId = $Template.TemplateList.value customGroup = $Template.customGroup @@ -148,6 +152,6 @@ function Invoke-CIPPStandardIntuneTemplate { $state = $CompareObj ? $CompareObj : $true Set-CIPPStandardsCompareField -FieldName "standards.IntuneTemplate.$id" -FieldValue $state -TenantFilter $Tenant } - Add-CIPPBPAField -FieldName "policy-$id" -FieldValue $Compare -StoreAs bool -Tenant $tenant + #Add-CIPPBPAField -FieldName "policy-$id" -FieldValue $Compare -StoreAs bool -Tenant $tenant } } From 4c39f6b315e25c26b6e41cca5b3b65e5c1c60ae7 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 14 Apr 2025 18:43:08 -0400 Subject: [PATCH 10/19] replace all . with _ --- Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 b/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 index f843b6f05123..1572537c0523 100644 --- a/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 @@ -7,7 +7,7 @@ function Set-CIPPStandardsCompareField { $Table = Get-CippTable -tablename 'CippStandardsReports' $TenantName = Get-Tenants | Where-Object -Property defaultDomainName -EQ $Tenant #if the fieldname does not contain standards. prepend it. - $FieldName = $FieldName.replace('standards.', 'standards_') + $FieldName = $FieldName.replace('.', '_') if ($FieldValue -is [System.Boolean]) { $fieldValue = [bool]$FieldValue } elseif ($FieldValue -is [string]) { From dc7545c381690a6b15c3336ac8b54c59cbdd17ce Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 14 Apr 2025 18:53:56 -0400 Subject: [PATCH 11/19] Update Set-CIPPStandardsCompareField.ps1 --- .../Public/Set-CIPPStandardsCompareField.ps1 | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 b/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 index 1572537c0523..c3b16be971fc 100644 --- a/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 @@ -18,24 +18,28 @@ function Set-CIPPStandardsCompareField { } $Existing = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'StandardReport' and RowKey eq '$($TenantName.defaultDomainName)'" - if ($Existing) { - $Existing = $Existing | Select-Object * -ExcludeProperty ETag, TimeStamp | ConvertTo-Json -Depth 10 -Compress | ConvertFrom-Json -AsHashtable - $Existing[$FieldName] = $FieldValue - $Existing['LastRefresh'] = [string]$(Get-Date (Get-Date).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z') - $Existing = [PSCustomObject]$Existing + try { + if ($Existing) { + $Existing = $Existing | Select-Object * -ExcludeProperty ETag, TimeStamp | ConvertTo-Json -Depth 10 -Compress | ConvertFrom-Json -AsHashtable + $Existing[$FieldName] = $FieldValue + $Existing['LastRefresh'] = [string]$(Get-Date (Get-Date).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z') + $Existing = [PSCustomObject]$Existing - Add-CIPPAzDataTableEntity @Table -Entity $Existing -Force - } else { - $Result = @{ - tenantFilter = "$($TenantName.defaultDomainName)" - GUID = "$($TenantName.customerId)" - RowKey = "$($TenantName.defaultDomainName)" - PartitionKey = 'StandardReport' - LastRefresh = [string]$(Get-Date (Get-Date).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z') - } - $Result[$FieldName] = $FieldValue - Add-CIPPAzDataTableEntity @Table -Entity $Result -Force + Add-CIPPAzDataTableEntity @Table -Entity $Existing -Force + } else { + $Result = @{ + tenantFilter = "$($TenantName.defaultDomainName)" + GUID = "$($TenantName.customerId)" + RowKey = "$($TenantName.defaultDomainName)" + PartitionKey = 'StandardReport' + LastRefresh = [string]$(Get-Date (Get-Date).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z') + } + $Result[$FieldName] = $FieldValue + Add-CIPPAzDataTableEntity @Table -Entity $Result -Force + } + Write-Information "Adding $FieldName to StandardCompare for $Tenant. content is $FieldValue" + } catch { + Write-Warning "Failed to add $FieldName to StandardCompare for $Tenant. content is $FieldValue. The error was: $($_.Exception.Message)" } - Write-Information "Adding $FieldName to StandardCompare for $Tenant. content is $FieldValue" } From 92af318927bb5b47f445ade12868cd62fda3626e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 14 Apr 2025 18:54:57 -0400 Subject: [PATCH 12/19] Update Set-CIPPStandardsCompareField.ps1 --- Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 b/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 index c3b16be971fc..883aee33c419 100644 --- a/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 @@ -7,7 +7,7 @@ function Set-CIPPStandardsCompareField { $Table = Get-CippTable -tablename 'CippStandardsReports' $TenantName = Get-Tenants | Where-Object -Property defaultDomainName -EQ $Tenant #if the fieldname does not contain standards. prepend it. - $FieldName = $FieldName.replace('.', '_') + $FieldName = $FieldName -replace '\.', '_' if ($FieldValue -is [System.Boolean]) { $fieldValue = [bool]$FieldValue } elseif ($FieldValue -is [string]) { From 4d8d0f294ed9761d7c59bad281ae2ef702a8d787 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 14 Apr 2025 19:24:03 -0400 Subject: [PATCH 13/19] fix sanitized table props --- .../CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 b/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 index 883aee33c419..a3d1aef9891b 100644 --- a/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPStandardsCompareField.ps1 @@ -6,8 +6,12 @@ function Set-CIPPStandardsCompareField { ) $Table = Get-CippTable -tablename 'CippStandardsReports' $TenantName = Get-Tenants | Where-Object -Property defaultDomainName -EQ $Tenant - #if the fieldname does not contain standards. prepend it. - $FieldName = $FieldName -replace '\.', '_' + + # Sanitize invalid c#/xml characters for Azure Tables + $FieldName = $FieldName.replace('standards.', 'standards_') + $FieldName = $FieldName.replace('IntuneTemplate.', 'IntuneTemplate_') + $FieldName = $FieldName -replace '-', '__' + if ($FieldValue -is [System.Boolean]) { $fieldValue = [bool]$FieldValue } elseif ($FieldValue -is [string]) { @@ -40,6 +44,6 @@ function Set-CIPPStandardsCompareField { } Write-Information "Adding $FieldName to StandardCompare for $Tenant. content is $FieldValue" } catch { - Write-Warning "Failed to add $FieldName to StandardCompare for $Tenant. content is $FieldValue. The error was: $($_.Exception.Message)" + Write-Warning "Failed to add $FieldName to StandardCompare for $Tenant. content is $FieldValue - $($_.Exception.Message)" } } From bf98c861e8f9de20665572482076f41b09d33a2d Mon Sep 17 00:00:00 2001 From: Roel van der Wegen Date: Tue, 15 Apr 2025 01:36:22 +0200 Subject: [PATCH 14/19] Create Get-ExoOnlineStringBytes.ps1 --- Modules/CIPPCore/Private/Get-ExoOnlineStringBytes.ps1 | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Modules/CIPPCore/Private/Get-ExoOnlineStringBytes.ps1 diff --git a/Modules/CIPPCore/Private/Get-ExoOnlineStringBytes.ps1 b/Modules/CIPPCore/Private/Get-ExoOnlineStringBytes.ps1 new file mode 100644 index 000000000000..067bd894b8b4 --- /dev/null +++ b/Modules/CIPPCore/Private/Get-ExoOnlineStringBytes.ps1 @@ -0,0 +1,11 @@ +function Get-ExoOnlineStringBytes { + param([string]$SizeString) + + # This exists because various exo cmdlets like to return a human readable string like "3.322 KB (3,402 bytes)" but not the raw bytes value + + if ($SizeString -match '\(([0-9,]+) bytes\)') { + return [int]($Matches[1] -replace ',','') + } + + return 0 +} From 00b1a5107d8d9665661ba7aed66f86c0ed4e2fc8 Mon Sep 17 00:00:00 2001 From: Roel van der Wegen Date: Tue, 15 Apr 2025 01:39:40 +0200 Subject: [PATCH 15/19] Convert TotalItemSize to bytes first --- .../Administration/Users/Invoke-ListUserMailboxDetails.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 index ba87bb0ed755..41832006069f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 @@ -164,7 +164,7 @@ function Invoke-ListUserMailboxDetails { $ProhibitSendQuotaString = $MailboxDetailedRequest.ProhibitSendQuota -split ' ' $ProhibitSendReceiveQuotaString = $MailboxDetailedRequest.ProhibitSendReceiveQuota -split ' ' $TotalItemSizeString = $StatsRequest.TotalItemSize -split ' ' - $TotalArchiveItemSizeString = $ArchiveSizeRequest.TotalItemSize -split ' ' + $TotalArchiveItemSizeString = Get-ExoOnlineStringBytes -SizeString $ArchiveSizeRequest.TotalItemSize.Value $ProhibitSendQuota = try { [math]::Round([float]($ProhibitSendQuotaString[0]), 2) } catch { 0 } $ProhibitSendReceiveQuota = try { [math]::Round([float]($ProhibitSendReceiveQuotaString[0]), 2) } catch { 0 } From 14d455ff665932936552622ab8a32e90995c0110 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 15 Apr 2025 00:55:33 -0400 Subject: [PATCH 16/19] fix property names --- .../Tenant/Standards/Invoke-ListStandardsCompare.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListStandardsCompare.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListStandardsCompare.ps1 index 6a71b7736a76..4144312551fd 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListStandardsCompare.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListStandardsCompare.ps1 @@ -25,10 +25,14 @@ Function Invoke-ListStandardsCompare { } else { $_.Value = [string]$_.Value } - $object | Add-Member -MemberType NoteProperty -Name $_.Name.Replace('standards_', 'standards.') -Value $_.Value -Force + + $Key = $_.Name.replace('standards_', 'standards.') + $Key = $Key.replace('IntuneTemplate_', 'IntuneTemplate.') + $Key = $Key -replace '__', '-' + + $object | Add-Member -MemberType NoteProperty -Name $Key -Value $_.Value -Force $object.PSObject.Properties.Remove($_.Name) } - } } From e9fed84d624a329fb18e8fd33c8e1258c0763252 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 15 Apr 2025 12:25:47 +0200 Subject: [PATCH 17/19] readd tenantid to alerts --- .../Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1 index bf47a28af856..bafe1e39fa59 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1 @@ -116,6 +116,7 @@ function Push-ExecScheduledCommand { '*email*' { Send-CIPPAlert -Type 'email' -Title $title -HTMLContent $HTML -TenantFilter $Tenant } '*webhook*' { $Webhook = [PSCustomObject]@{ + 'tenantId' = $TenantInfo.customerId 'Tenant' = $Tenant 'TaskInfo' = $Item.TaskInfo 'Results' = $Results From b32757fd4a350aa001fd075bac2c26a779df58e1 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 15 Apr 2025 10:22:12 -0400 Subject: [PATCH 18/19] better error handling in access check --- .../CIPP/Settings/Invoke-ExecAccessChecks.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecAccessChecks.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecAccessChecks.ps1 index 6624ed3ad3a7..612918dfe20b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecAccessChecks.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecAccessChecks.ps1 @@ -25,7 +25,7 @@ Function Invoke-ExecAccessChecks { if ($Request.Query.SkipCache -ne 'true' -or $Request.Query.SkipCache -ne $true) { try { $Cache = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq 'AccessPermissions' and Timestamp and Timestamp ge datetime'$TimestampFilter'" - $Results = $Cache.Data | ConvertFrom-Json + $Results = $Cache.Data | ConvertFrom-Json -ErrorAction Stop } catch { $Results = $null } @@ -62,7 +62,7 @@ Function Invoke-ExecAccessChecks { ExchangeTest = '' } if ($TenantCheck) { - $Data = @($TenantCheck.Data | ConvertFrom-Json) + $Data = @($TenantCheck.Data | ConvertFrom-Json -ErrorAction Stop) $TenantResult.GraphStatus = $Data.GraphStatus $TenantResult.ExchangeStatus = $Data.ExchangeStatus $TenantResult.GDAPRoles = $Data.GDAPRoles @@ -85,7 +85,7 @@ Function Invoke-ExecAccessChecks { $Results = @() } } catch { - Write-Host $_.Exception.Message + Write-Warning "Error running tenant access check - $($_.Exception.Message)" $Results = @() } } @@ -105,7 +105,7 @@ Function Invoke-ExecAccessChecks { if (!$Request.Query.SkipCache -eq 'true' -or !$Request.Query.SkipCache -eq $true) { try { $Cache = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq 'GDAPRelationships' and Timestamp ge datetime'$TimestampFilter'" - $Results = $Cache.Data | ConvertFrom-Json + $Results = $Cache.Data | ConvertFrom-Json -ErrorAction Stop } catch { $Results = $null } From 5e4e482b414ada6d33ce859c3dbf955be77e981f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 15 Apr 2025 10:39:12 -0400 Subject: [PATCH 19/19] up version --- version_latest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_latest.txt b/version_latest.txt index 18bb4182dd01..a5f017a0a348 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -7.5.0 +7.5.1