From 521d8f32c620e821e6d956333462afa7f29cbaff Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:00:43 +0100 Subject: [PATCH 01/14] comment line --- Modules/CIPPCore/Public/Alerts/Get-CIPPAlertRestrictedUsers.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertRestrictedUsers.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertRestrictedUsers.ps1 index 8898cf4b0302..5e3992a1a14e 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertRestrictedUsers.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertRestrictedUsers.ps1 @@ -37,6 +37,6 @@ Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData } } catch { - Write-LogMessage -tenant $($TenantFilter) -message "Could not get restricted users for $($TenantFilter): $(Get-NormalizedError -message $_.Exception.message)" -severity 'Error' -API 'Get-CIPPAlertRestrictedUsers' -LogData (Get-CippException -Exception $_) + # Write-LogMessage -tenant $($TenantFilter) -message "Could not get restricted users for $($TenantFilter): $(Get-NormalizedError -message $_.Exception.message)" -severity 'Error' -API 'Get-CIPPAlertRestrictedUsers' -LogData (Get-CippException -Exception $_) } } From 9e5ec3d2ded03811225780005c06d67e346488be Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 12:01:16 -0500 Subject: [PATCH 02/14] move to debug loging --- .../CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 b/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 index 4afa5ee47d61..6a35cee4fa9e 100644 --- a/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 +++ b/Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1 @@ -17,7 +17,7 @@ function Test-CIPPAccess { $CIPPCoreModule = Get-Module -Name CIPPCore if ($CIPPCoreModule) { $PermissionsFileJson = Join-Path $CIPPCoreModule.ModuleBase 'lib' 'data' 'function-permissions.json' - + if (Test-Path $PermissionsFileJson) { try { $jsonData = Get-Content -Path $PermissionsFileJson -Raw | ConvertFrom-Json -AsHashtable @@ -25,7 +25,7 @@ function Test-CIPPAccess { foreach ($key in $jsonData.Keys) { $global:CIPPFunctionPermissions[$key] = $jsonData[$key] } - Write-Information "Loaded $($global:CIPPFunctionPermissions.Count) function permissions from JSON cache" + Write-Debug "Loaded $($global:CIPPFunctionPermissions.Count) function permissions from JSON cache" } catch { Write-Warning "Failed to load function permissions from JSON: $($_.Exception.Message)" } @@ -41,13 +41,13 @@ function Test-CIPPAccess { $PermissionData = $global:CIPPFunctionPermissions[$FunctionName] $APIRole = $PermissionData['Role'] $Functionality = $PermissionData['Functionality'] - Write-Information "Loaded function permission data from cache for '$FunctionName': Role='$APIRole', Functionality='$Functionality'" + Write-Debug "Loaded function permission data from cache for '$FunctionName': Role='$APIRole', Functionality='$Functionality'" } else { try { $Help = Get-Help $FunctionName -ErrorAction Stop $APIRole = $Help.Role $Functionality = $Help.Functionality - Write-Information "Loaded function permission data via Get-Help for '$FunctionName': Role='$APIRole', Functionality='$Functionality'" + Write-Debug "Loaded function permission data via Get-Help for '$FunctionName': Role='$APIRole', Functionality='$Functionality'" } catch { Write-Warning "Function '$FunctionName' not found" } From fca10c36e90f6212243a855923dd917cc8a4c59e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 12:01:41 -0500 Subject: [PATCH 03/14] fix member list in alignment --- .../Functions/Get-CIPPTenantAlignment.ps1 | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 index 5375089a2007..cb81201cfc29 100644 --- a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 @@ -111,18 +111,27 @@ function Get-CIPPTenantAlignment { if ($Template.tenantFilter -and $Template.tenantFilter.Count -gt 0) { # Extract tenant values from the tenantFilter array - $TenantValues = $Template.tenantFilter | ForEach-Object { - if ($_.type -eq 'group') { - ($TenantGroups | Where-Object -Property GroupName -EQ $_.value).Members.defaultDomainName + $TenantValues = [System.Collections.Generic.List[string]]::new() + foreach ($filterItem in $Template.tenantFilter) { + if ($filterItem.type -eq 'group') { + # Look up group members by Id (GUID in the value field) + $GroupMembers = $TenantGroups | Where-Object { $_.Id -eq $filterItem.value } + if ($GroupMembers -and $GroupMembers.Members) { + foreach ($member in $GroupMembers.Members.defaultDomainName) { + $TenantValues.Add($member) + } + } } else { - $_.value + $TenantValues.Add($filterItem.value) } } if ($TenantValues -contains 'AllTenants') { $AppliestoAllTenants = $true + } elseif ($TenantValues.Count -gt 0) { + $TemplateAssignedTenants = @($TenantValues) } else { - $TemplateAssignedTenants = $TenantValues + $TemplateAssignedTenants = @() } } else { $AppliestoAllTenants = $true From b4a141db6c2c12dfc797b873fca5eb50cc384d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 19 Dec 2025 19:40:28 +0100 Subject: [PATCH 04/14] Fix: Alert shows RecipientAddress as System.Object[] Fixes https://github.com/KelvinTegelaar/CIPP/issues/5114 --- .../Public/Alerts/Get-CIPPAlertQuarantineReleaseRequests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertQuarantineReleaseRequests.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertQuarantineReleaseRequests.ps1 index 37e710c5caeb..56182ece6f32 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertQuarantineReleaseRequests.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertQuarantineReleaseRequests.ps1 @@ -38,7 +38,7 @@ MessageId = $Message.MessageId Subject = $Message.Subject SenderAddress = $Message.SenderAddress - RecipientAddress = $Message.RecipientAddress + RecipientAddress = $Message.RecipientAddress -join '; ' Type = $Message.Type PolicyName = $Message.PolicyName ReleaseStatus = $Message.ReleaseStatus From f34bbcbcd95661faa709a63d61b48b4689928d6e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 14:42:30 -0500 Subject: [PATCH 05/14] Replace Write-AlertMessage with Write-LogMessage in TERRL alert Updated the error handling in Get-CIPPAlertTERRL.ps1 to use Write-LogMessage instead of Write-AlertMessage, adding more detailed logging with severity, API, and exception data. --- Modules/CIPPCore/Public/Alerts/Get-CIPPAlertTERRL.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertTERRL.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertTERRL.ps1 index 37db5976d94f..163cfa782469 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertTERRL.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertTERRL.ps1 @@ -35,6 +35,6 @@ function Get-CIPPAlertTERRL { } } } catch { - Write-AlertMessage -tenant $($TenantFilter) -message "Could not get TERRL status for $($TenantFilter): $(Get-NormalizedError -message $_.Exception.message)" + Write-LogMessage -tenant $($TenantFilter) -message "Could not get TERRL status for $($TenantFilter): $(Get-NormalizedError -message $_.Exception.message)" -severity 'Error' -API 'CIPPAlertTERRL' -LogData (Get-CippException -Exception $_) } } From 4fce1053da83f01731797717cc854e0372357383 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 15:02:34 -0500 Subject: [PATCH 06/14] Update Start-UpdateTokensTimer.ps1 --- .../Entrypoints/Timer Functions/Start-UpdateTokensTimer.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-UpdateTokensTimer.ps1 b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-UpdateTokensTimer.ps1 index 42d8bf955c72..416318bb4438 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-UpdateTokensTimer.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-UpdateTokensTimer.ps1 @@ -68,7 +68,7 @@ function Start-UpdateTokensTimer { Write-Information "Found $($ExpiredSecrets.Count) expired application secrets for $AppId. Removing them." foreach ($Secret in $ExpiredSecrets) { try { - New-GraphPostRequest -type DELETE -uri "https://graph.microsoft.com/v1.0/applications/$($PasswordCredentials.id)/removePassword" -Body "{`"keyId`":`"$($Secret.keyId)`"}" -NoAuthCheck $true -AsApp $true -ErrorAction Stop + New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/applications/$($PasswordCredentials.id)/removePassword" -Body "{`"keyId`":`"$($Secret.keyId)`"}" -NoAuthCheck $true -AsApp $true -ErrorAction Stop Write-Information "Removed expired application secret with keyId $($Secret.keyId)." } catch { Write-LogMessage -API 'Update Tokens' -message "Error removing expired application secret with keyId $($Secret.keyId), see Log Data for details." -sev 'CRITICAL' -LogData (Get-CippException -Exception $_) From 038e3e8f2c2126e1af8911256d93e04151d560e3 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 15:59:40 -0500 Subject: [PATCH 07/14] Add Reason and lastChangedByUser to policy deviations Extended the policy deviation objects in Get-CIPPDrift to include Reason and lastChangedByUser fields when available from existing drift states. This provides more context about policy changes and their origins. --- Modules/CIPPCore/Public/Get-CIPPDrift.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 index 465b094f7066..b93b683b67cd 100644 --- a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 @@ -280,6 +280,8 @@ function Get-CIPPDrift { } else { 'New' } + $reason = if ($ExistingDriftStates.ContainsKey($PolicyKey)) { $ExistingDriftStates[$PolicyKey].Reason } + $User = if ($ExistingDriftStates.ContainsKey($PolicyKey)) { $ExistingDriftStates[$PolicyKey].User } $PolicyDeviation = [PSCustomObject]@{ standardName = $PolicyKey standardDisplayName = "Intune - $TenantPolicyName" @@ -287,6 +289,8 @@ function Get-CIPPDrift { receivedValue = $TenantPolicy.Policy state = 'current' Status = $Status + Reason = $reason + lastChangedByUser = $User } $PolicyDeviations.Add($PolicyDeviation) } @@ -310,6 +314,8 @@ function Get-CIPPDrift { } else { 'New' } + $reason = if ($ExistingDriftStates.ContainsKey($PolicyKey)) { $ExistingDriftStates[$PolicyKey].Reason } + $User = if ($ExistingDriftStates.ContainsKey($PolicyKey)) { $ExistingDriftStates[$PolicyKey].User } $PolicyDeviation = [PSCustomObject]@{ standardName = $PolicyKey standardDisplayName = "Conditional Access - $($TenantCAPolicy.displayName)" @@ -317,6 +323,8 @@ function Get-CIPPDrift { receivedValue = $TenantCAPolicy | Out-String state = 'current' Status = $Status + Reason = $reason + lastChangedByUser = $User } $PolicyDeviations.Add($PolicyDeviation) } From 2c4f8606b4b1e08fc780d875f0a7c00a544bec27 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 17:06:59 -0500 Subject: [PATCH 08/14] Improve template matching by adding Source filter Updated policy template matching logic to include the Source (TenantFilter) property, ensuring templates are matched and updated per tenant. Also standardized usage of policy ID and display name variables for consistency and correctness. --- .../CIPPCore/Public/New-CIPPTemplateRun.ps1 | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Modules/CIPPCore/Public/New-CIPPTemplateRun.ps1 b/Modules/CIPPCore/Public/New-CIPPTemplateRun.ps1 index db427630a4be..18c92a483c1e 100644 --- a/Modules/CIPPCore/Public/New-CIPPTemplateRun.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPTemplateRun.ps1 @@ -114,7 +114,7 @@ function New-CIPPTemplateRun { foreach ($policy in $policies) { try { $Hash = Get-StringHash -String ($policy | ConvertTo-Json -Depth 100 -Compress) - $ExistingPolicy = $ExistingTemplates | Where-Object { $_.PartitionKey -eq 'CATemplate' -and $_.displayName -eq $policy.displayName } | Select-Object -First 1 + $ExistingPolicy = $ExistingTemplates | Where-Object { $_.PartitionKey -eq 'CATemplate' -and $_.displayName -eq $policy.displayName -and $_.Source -eq $TenantFilter } | Select-Object -First 1 if ($ExistingPolicy -and $ExistingPolicy.SHA -eq $Hash) { "CA Policy $($policy.displayName) found, SHA matches, skipping template creation" continue @@ -184,16 +184,16 @@ function New-CIPPTemplateRun { $Hash = Get-StringHash -String ($Policy | ConvertTo-Json -Depth 100 -Compress) $DisplayName = $Policy.displayName ?? $Policy.name - $ExistingPolicy = $ExistingTemplates | Where-Object { $_.PartitionKey -eq 'IntuneTemplate' -and $_.displayName -eq $DisplayName } | Select-Object -First 1 + $ExistingPolicy = $ExistingTemplates | Where-Object { $_.PartitionKey -eq 'IntuneTemplate' -and $_.displayName -eq $DisplayName -and $_.Source -eq $TenantFilter } | Select-Object -First 1 Write-Information "Processing Intune Configuration Policy $($DisplayName) - $($ExistingPolicy ? 'Existing template found' : 'No existing template found')" if ($ExistingPolicy -and $ExistingPolicy.SHA -eq $Hash) { - "Intune Configuration Policy $($Policy.displayName) found, SHA matches, skipping template creation" + "Intune Configuration Policy $($DisplayName) found, SHA matches, skipping template creation" continue } - $Template = New-CIPPIntuneTemplate -TenantFilter $TenantFilter -URLName $URLName -ID $Policy.ID + $Template = New-CIPPIntuneTemplate -TenantFilter $TenantFilter -URLName $URLName -ID $Policy.id if ($ExistingPolicy -and $ExistingPolicy.PartitionKey -eq 'IntuneTemplate') { "Policy $($Template.DisplayName) found, updating template" $object = [PSCustomObject]@{ @@ -246,14 +246,15 @@ function New-CIPPTemplateRun { 'intunecompliance' { Write-Information "Create Intune Compliance Policy Templates for $TenantFilter" New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies?$top=999' -tenantid $TenantFilter | ForEach-Object { + $Policy = $_ $Hash = Get-StringHash -String (ConvertTo-Json -Depth 100 -Compress -InputObject $_) - $ExistingPolicy = $ExistingTemplates | Where-Object { $_.displayName -eq $_.DisplayName } | Select-Object -First 1 + $ExistingPolicy = $ExistingTemplates | Where-Object { $Policy.displayName -eq $_.DisplayName -and $_.Source -eq $TenantFilter } | Select-Object -First 1 if ($ExistingPolicy -and $ExistingPolicy.SHA -eq $Hash) { "Intune Compliance Policy $($_.DisplayName) found, SHA matches, skipping template creation" continue } - $Template = New-CIPPIntuneTemplate -TenantFilter $TenantFilter -URLName 'deviceCompliancePolicies' -ID $_.ID + $Template = New-CIPPIntuneTemplate -TenantFilter $TenantFilter -URLName 'deviceCompliancePolicies' -ID $Policy.id if ($ExistingPolicy -and $ExistingPolicy.PartitionKey -eq 'IntuneTemplate') { "Intune Compliance Policy $($Template.DisplayName) found, updating template" $object = [PSCustomObject]@{ @@ -299,14 +300,15 @@ function New-CIPPTemplateRun { 'intuneprotection' { Write-Information "Create Intune Protection Policy Templates for $TenantFilter" New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceAppManagement/managedAppPolicies?$top=999' -tenantid $TenantFilter | ForEach-Object { + $Policy = $_ $Hash = Get-StringHash -String (ConvertTo-Json -Depth 100 -Compress -InputObject $_) - $ExistingPolicy = $ExistingTemplates | Where-Object { $_.displayName -eq $_.DisplayName } | Select-Object -First 1 + $ExistingPolicy = $ExistingTemplates | Where-Object { $Policy.displayName -eq $_.DisplayName -and $_.Source -eq $TenantFilter } | Select-Object -First 1 if ($ExistingPolicy -and $ExistingPolicy.SHA -eq $Hash) { "Intune Protection Policy $($_.DisplayName) found, SHA matches, skipping template creation" continue } - $Template = New-CIPPIntuneTemplate -TenantFilter $TenantFilter -URLName 'managedAppPolicies' -ID $_.ID + $Template = New-CIPPIntuneTemplate -TenantFilter $TenantFilter -URLName 'managedAppPolicies' -ID $Policy.id if ($ExistingPolicy -and $ExistingPolicy.PartitionKey -eq 'IntuneTemplate') { "Intune Protection Policy $($Template.DisplayName) found, updating template" $object = [PSCustomObject]@{ From 81d6913c19172824bbabeefcc25c23652ee14641 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 17:23:28 -0500 Subject: [PATCH 09/14] Add 'source' and 'isSynced' properties to template entities Introduces 'source' and 'isSynced' properties to template objects across multiple modules for improved traceability and synchronization status. Updates Import-CommunityTemplate and related functions to handle and propagate the new properties. --- .../Endpoint/MEM/Invoke-ListIntuneTemplates.ps1 | 5 ++++- .../Administration/Groups/Invoke-ListGroupTemplates.ps1 | 2 ++ .../Tenant/Conditional/Invoke-ListCAtemplates.ps1 | 2 ++ .../Tenant/Standards/Invoke-listStandardTemplates.ps1 | 2 ++ Modules/CIPPCore/Public/New-CIPPTemplateRun.ps1 | 2 +- Modules/CIPPCore/Public/Tools/Import-CommunityTemplate.ps1 | 6 +++++- 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ListIntuneTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ListIntuneTemplates.ps1 index f1db08619aed..a64228f7c2f6 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ListIntuneTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ListIntuneTemplates.ps1 @@ -39,7 +39,8 @@ function Invoke-ListIntuneTemplates { $data | Add-Member -NotePropertyName 'Type' -NotePropertyValue $JSONData.Type -Force $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.RowKey -Force $data | Add-Member -NotePropertyName 'package' -NotePropertyValue $_.Package -Force - $data | Add-Member -NotePropertyName 'isSynced' -NotePropertyValue (![string]::IsNullOrEmpty($_.SHA)) + $data | Add-Member -NotePropertyName 'isSynced' -NotePropertyValue (![string]::IsNullOrEmpty($_.SHA)) -Force + $data | Add-Member -NotePropertyName 'source' -NotePropertyValue $_.Source -Force $data } catch { @@ -65,6 +66,8 @@ function Invoke-ListIntuneTemplates { $data | Add-Member -NotePropertyName 'Type' -NotePropertyValue $JSONData.Type -Force $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.RowKey -Force $data | Add-Member -NotePropertyName 'package' -NotePropertyValue $_.Package -Force + $data | Add-Member -NotePropertyName 'source' -NotePropertyValue $_.Source -Force + $data | Add-Member -NotePropertyName 'isSynced' -NotePropertyValue (![string]::IsNullOrEmpty($_.SHA)) -Force $data } catch { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-ListGroupTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-ListGroupTemplates.ps1 index 70dacf0b7314..b58a93aa3ced 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-ListGroupTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-ListGroupTemplates.ps1 @@ -48,6 +48,8 @@ function Invoke-ListGroupTemplates { allowExternal = $data.allowExternal username = $data.username GUID = $_.RowKey + source = $_.Source + isSynced = (![string]::IsNullOrEmpty($_.SHA)) } } catch { Write-Information "Could not parse group template $($_.RowKey): $($_.Exception.Message)" diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListCAtemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListCAtemplates.ps1 index 6403c20af94e..5ce6a7ad6844 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListCAtemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ListCAtemplates.ps1 @@ -35,6 +35,8 @@ function Invoke-ListCAtemplates { $row = $_ $data = $row.JSON | ConvertFrom-Json -Depth 100 -ErrorAction Stop $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $row.GUID -Force + $data | Add-Member -NotePropertyName 'source' -NotePropertyValue $row.Source -Force + $data | Add-Member -NotePropertyName 'isSynced' -NotePropertyValue (![string]::IsNullOrEmpty($row.SHA)) -Force $data } catch { Write-Warning "Failed to process CA template: $($row.RowKey) - $($_.Exception.Message)" diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 index c6ada23304bd..cd8821ae41a2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 @@ -23,6 +23,8 @@ function Invoke-listStandardTemplates { } if ($Data) { $Data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.GUID -Force + $Data | Add-Member -NotePropertyName 'source' -NotePropertyValue $_.Source -Force + $Data | Add-Member -NotePropertyName 'isSynced' -NotePropertyValue (![string]::IsNullOrEmpty($_.SHA)) -Force if (!$Data.excludedTenants) { $Data | Add-Member -NotePropertyName 'excludedTenants' -NotePropertyValue @() -Force diff --git a/Modules/CIPPCore/Public/New-CIPPTemplateRun.ps1 b/Modules/CIPPCore/Public/New-CIPPTemplateRun.ps1 index 18c92a483c1e..a7f2176ab723 100644 --- a/Modules/CIPPCore/Public/New-CIPPTemplateRun.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPTemplateRun.ps1 @@ -64,7 +64,7 @@ function New-CIPPTemplateRun { if (!$ExistingTemplate -or $UpdateNeeded) { $Template = (Get-GitHubFileContents -FullName $TemplateSettings.templateRepo.value -Branch $TemplateSettings.templateRepoBranch.value -Path $File.path).content | ConvertFrom-Json - Import-CommunityTemplate -Template $Template -SHA $File.sha -MigrationTable $MigrationTable -LocationData $LocationData + Import-CommunityTemplate -Template $Template -SHA $File.sha -MigrationTable $MigrationTable -LocationData $LocationData -Source $TemplateSettings.templateRepo.value if ($UpdateNeeded) { Write-Information "Template $($File.name) needs to be updated as the SHA is different" "Template $($File.name) updated" diff --git a/Modules/CIPPCore/Public/Tools/Import-CommunityTemplate.ps1 b/Modules/CIPPCore/Public/Tools/Import-CommunityTemplate.ps1 index 6479c9bdec71..9ea1c1b98a76 100644 --- a/Modules/CIPPCore/Public/Tools/Import-CommunityTemplate.ps1 +++ b/Modules/CIPPCore/Public/Tools/Import-CommunityTemplate.ps1 @@ -9,6 +9,7 @@ function Import-CommunityTemplate { $SHA, $MigrationTable, $LocationData, + $Source, [switch]$Force ) @@ -64,6 +65,7 @@ function Import-CommunityTemplate { $NewJSON = [string]($NewJSON | ConvertTo-Json -Depth 100 -Compress) $Template.JSON = $NewJSON $Template | Add-Member -MemberType NoteProperty -Name SHA -Value $SHA -Force + $Template | Add-Member -MemberType NoteProperty -Name Source -Value $Source -Force Add-CIPPAzDataTableEntity @Table -Entity $Template -Force } else { if ($Template.mailNickname) { $Type = 'Group' } @@ -86,6 +88,7 @@ function Import-CommunityTemplate { SHA = $SHA GUID = $Template.id RowKey = $Template.id + Source = $Source } Add-CIPPAzDataTableEntity @Table -Entity $entity -Force break @@ -121,13 +124,13 @@ function Import-CommunityTemplate { } } - $entity = @{ JSON = "$RawJson" PartitionKey = 'CATemplate' SHA = $SHA GUID = $ID RowKey = $ID + Source = $Source } Add-CIPPAzDataTableEntity @Table -Entity $entity -Force break @@ -162,6 +165,7 @@ function Import-CommunityTemplate { SHA = $SHA GUID = $ID RowKey = $ID + Source = $Source } if ($Existing -and $Existing.Package) { From 74edf99a9008f561c687ec3ec209984cd1a41ef8 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 17:40:45 -0500 Subject: [PATCH 10/14] version up --- host.json | 4 ++-- version_latest.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/host.json b/host.json index 40ef5d0005a7..0e7bc9b1617d 100644 --- a/host.json +++ b/host.json @@ -16,9 +16,9 @@ "distributedTracingEnabled": false, "version": "None" }, - "defaultVersion": "8.8.1", + "defaultVersion": "8.8.2", "versionMatchStrategy": "Strict", "versionFailureStrategy": "Fail" } } -} \ No newline at end of file +} diff --git a/version_latest.txt b/version_latest.txt index eec6dacbd482..11f1d47dac93 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -8.8.1 +8.8.2 From b9ca96ca00169f3bf66376ee5e679ec43aa6d4db Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 18:03:38 -0500 Subject: [PATCH 11/14] Refine NameOnly logic in Get-CIPPBackup Updated the NameOnly parameter handling to filter out partitioned RowKeys and apply TenantFilter matching on RowKey when NameOnly is present. This improves the accuracy of backup name retrieval and ensures correct filtering based on tenant. --- Modules/CIPPCore/Public/Get-CIPPBackup.ps1 | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPBackup.ps1 b/Modules/CIPPCore/Public/Get-CIPPBackup.ps1 index e824402a7b85..444ee177359d 100644 --- a/Modules/CIPPCore/Public/Get-CIPPBackup.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPBackup.ps1 @@ -18,14 +18,22 @@ function Get-CIPPBackup { } if ($NameOnly.IsPresent) { - $Table.Property = @('PartitionKey', 'RowKey', 'Timestamp', 'OriginalEntityId') + $Table.Property = @('RowKey') } $Filter = $Conditions -join ' and ' $Table.Filter = $Filter - $Info = Get-CIPPAzDataTableEntity @Table -Debug - if ($TenantFilter) { - $Info = $Info | Where-Object { $_.TenantFilter -eq $TenantFilter } + $Info = Get-CIPPAzDataTableEntity @Table + + if ($NameOnly.IsPresent) { + $Info = $Info | Where-Object { $_.RowKey -notmatch '-part[0-9]+$' } + if ($TenantFilter) { + $Info = $Info | Where-Object { $_.RowKey -match "^$($TenantFilter)_" } + } + } else { + if ($TenantFilter) { + $Info = $Info | Where-Object { $_.TenantFilter -eq $TenantFilter } + } } return $Info } From 423a605ad681dc61fb3abecabc13d1910b88de72 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 18:07:38 -0500 Subject: [PATCH 12/14] Fix tenant filter logic in Get-CIPPBackup Updated the tenant filter condition to exclude 'AllTenants' from being treated as a specific tenant filter. This ensures that backups are not incorrectly filtered when 'AllTenants' is specified. --- Modules/CIPPCore/Public/Get-CIPPBackup.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/CIPPCore/Public/Get-CIPPBackup.ps1 b/Modules/CIPPCore/Public/Get-CIPPBackup.ps1 index 444ee177359d..4c714188f3c4 100644 --- a/Modules/CIPPCore/Public/Get-CIPPBackup.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPBackup.ps1 @@ -31,7 +31,7 @@ function Get-CIPPBackup { $Info = $Info | Where-Object { $_.RowKey -match "^$($TenantFilter)_" } } } else { - if ($TenantFilter) { + if ($TenantFilter -and $TenantFilter -ne 'AllTenants') { $Info = $Info | Where-Object { $_.TenantFilter -eq $TenantFilter } } } From 538b5a3ec2415f16ba7d6832c00adc2d055630d9 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 18:47:17 -0500 Subject: [PATCH 13/14] Add TenantFilter extraction to backup list output Updated Invoke-ExecListBackup to extract and include TenantFilter from RowKey when NameOnly is specified. Also excluded 'OriginalEntityId' from the listed properties. --- .../HTTP Functions/CIPP/Core/Invoke-ExecListBackup.ps1 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecListBackup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecListBackup.ps1 index bd7c0445bf3a..9d4bc0792383 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecListBackup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecListBackup.ps1 @@ -21,11 +21,12 @@ function Invoke-ExecListBackup { if ($NameOnly) { $Processed = foreach ($item in $Result) { - $properties = $item.PSObject.Properties | Where-Object { $_.Name -notin @('TenantFilter', 'ETag', 'PartitionKey', 'RowKey', 'Timestamp') -and $_.Value } + $properties = $item.PSObject.Properties | Where-Object { $_.Name -notin @('TenantFilter', 'ETag', 'PartitionKey', 'RowKey', 'Timestamp', 'OriginalEntityId') -and $_.Value } [PSCustomObject]@{ - BackupName = $item.RowKey - Timestamp = $item.Timestamp - Items = $properties.Name + TenantFilter = $item.RowKey -match '^(.*?)_' | ForEach-Object { $matches[1] } + BackupName = $item.RowKey + Timestamp = $item.Timestamp + Items = $properties.Name } } $Result = $Processed | Sort-Object Timestamp -Descending From 5f55eaf376f52d9f9e09002ab9241e3e6c6c297c Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 18:57:22 -0500 Subject: [PATCH 14/14] Update Invoke-ExecListBackup.ps1 --- .../CIPP/Core/Invoke-ExecListBackup.ps1 | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecListBackup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecListBackup.ps1 index 9d4bc0792383..d4cdfab364b2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecListBackup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecListBackup.ps1 @@ -21,12 +21,21 @@ function Invoke-ExecListBackup { if ($NameOnly) { $Processed = foreach ($item in $Result) { - $properties = $item.PSObject.Properties | Where-Object { $_.Name -notin @('TenantFilter', 'ETag', 'PartitionKey', 'RowKey', 'Timestamp', 'OriginalEntityId') -and $_.Value } - [PSCustomObject]@{ - TenantFilter = $item.RowKey -match '^(.*?)_' | ForEach-Object { $matches[1] } - BackupName = $item.RowKey - Timestamp = $item.Timestamp - Items = $properties.Name + $properties = $item.PSObject.Properties | Where-Object { $_.Name -notin @('TenantFilter', 'ETag', 'PartitionKey', 'RowKey', 'Timestamp', 'OriginalEntityId', 'SplitOverProps', 'PartIndex') -and $_.Value } + + if ($Type -eq 'Scheduled') { + [PSCustomObject]@{ + TenantFilter = $item.RowKey -match '^(.*?)_' | ForEach-Object { $matches[1] } + BackupName = $item.RowKey + Timestamp = $item.Timestamp + Items = $properties.Name + } + } else { + [PSCustomObject]@{ + BackupName = $item.RowKey + Timestamp = $item.Timestamp + } + } } $Result = $Processed | Sort-Object Timestamp -Descending