Skip to content

Commit 39ab54e

Browse files
max depth
1 parent b7b96bf commit 39ab54e

File tree

1 file changed

+17
-75
lines changed

1 file changed

+17
-75
lines changed

Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1

Lines changed: 17 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,4 @@
11
function Compare-CIPPIntuneObject {
2-
<#
3-
.SYNOPSIS
4-
Compares two Intune objects and returns only the differences.
5-
6-
.DESCRIPTION
7-
This function takes two Intune objects and performs a comparison, returning only the properties that differ.
8-
If no differences are found, it returns null.
9-
It's useful for identifying changes between template objects and existing policies.
10-
11-
.PARAMETER ReferenceObject
12-
The reference Intune object to compare against.
13-
14-
.PARAMETER DifferenceObject
15-
The Intune object to compare with the reference object.
16-
17-
.PARAMETER ExcludeProperties
18-
Additional properties to exclude from the comparison.
19-
20-
.EXAMPLE
21-
$template = Get-CIPPIntunePolicy -tenantFilter $Tenant -DisplayName "Template Policy" -TemplateType "Device"
22-
$existing = Get-CIPPIntunePolicy -tenantFilter $Tenant -DisplayName "Existing Policy" -TemplateType "Device"
23-
$differences = Compare-CIPPIntuneObject -ReferenceObject $template -DifferenceObject $existing
24-
25-
.NOTES
26-
This function performs a comparison of objects, including nested properties.
27-
#>
282
[CmdletBinding()]
293
param(
304
[Parameter(Mandatory = $true)]
@@ -39,7 +13,6 @@ function Compare-CIPPIntuneObject {
3913
[string[]]$CompareType = @()
4014
)
4115
if ($CompareType -ne 'Catalog') {
42-
# Default properties to exclude from comparison
4316
$defaultExcludeProperties = @(
4417
'id',
4518
'createdDateTime',
@@ -57,13 +30,9 @@ function Compare-CIPPIntuneObject {
5730
'featureUpdatesPauseStartDate'
5831
)
5932

60-
# Combine default and custom exclude properties
6133
$excludeProps = $defaultExcludeProperties + $ExcludeProperties
62-
63-
# Create a list to store comparison results
6434
$result = [System.Collections.Generic.List[PSObject]]::new()
6535

66-
# Helper function to check if a property should be skipped
6736
function ShouldSkipProperty {
6837
param (
6938
[string]$PropertyName
@@ -73,25 +42,28 @@ function Compare-CIPPIntuneObject {
7342
$excludeProps -contains $PropertyName)
7443
}
7544

76-
# Recursive function to compare objects deeply
7745
function Compare-ObjectsRecursively {
7846
param (
79-
[Parameter(Mandatory = $true)]
80-
$Object1,
81-
82-
[Parameter(Mandatory = $true)]
83-
$Object2,
84-
85-
[Parameter(Mandatory = $false)]
86-
[string]$PropertyPath = ''
47+
[Parameter(Mandatory = $true)] $Object1,
48+
[Parameter(Mandatory = $true)] $Object2,
49+
[Parameter(Mandatory = $false)] [string]$PropertyPath = '',
50+
[int]$Depth = 0,
51+
[int]$MaxDepth = 15
8752
)
8853

89-
# If both objects are null or empty, they're equal
54+
if ($Depth -ge $MaxDepth) {
55+
$result.Add([PSCustomObject]@{
56+
Property = $PropertyPath
57+
ExpectedValue = '[MaxDepthExceeded]'
58+
ReceivedValue = '[MaxDepthExceeded]'
59+
})
60+
return
61+
}
62+
9063
if (($null -eq $Object1 -or $Object1 -eq '') -and ($null -eq $Object2 -or $Object2 -eq '')) {
9164
return
9265
}
9366

94-
# If one object is null but the other isn't, they're different
9567
if (($null -eq $Object1 -or $Object1 -eq '') -xor ($null -eq $Object2 -or $Object2 -eq '')) {
9668
$result.Add([PSCustomObject]@{
9769
Property = $PropertyPath
@@ -101,7 +73,6 @@ function Compare-CIPPIntuneObject {
10173
return
10274
}
10375

104-
# If objects are of different types, they're different
10576
if ($Object1.GetType() -ne $Object2.GetType()) {
10677
$result.Add([PSCustomObject]@{
10778
Property = $PropertyPath
@@ -111,9 +82,7 @@ function Compare-CIPPIntuneObject {
11182
return
11283
}
11384

114-
# Handle different object types
11585
if ($Object1 -is [System.Collections.IDictionary]) {
116-
# Compare dictionaries
11786
$allKeys = @($Object1.Keys) + @($Object2.Keys) | Select-Object -Unique
11887

11988
foreach ($key in $allKeys) {
@@ -122,9 +91,8 @@ function Compare-CIPPIntuneObject {
12291
$newPath = if ($PropertyPath) { "$PropertyPath.$key" } else { $key }
12392

12493
if ($Object1.ContainsKey($key) -and $Object2.ContainsKey($key)) {
125-
#only run if both props are not null
12694
if ($Object1[$key] -and $Object2[$key]) {
127-
Compare-ObjectsRecursively -Object1 $Object1[$key] -Object2 $Object2[$key] -PropertyPath $newPath
95+
Compare-ObjectsRecursively -Object1 $Object1[$key] -Object2 $Object2[$key] -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth
12896
}
12997
} elseif ($Object1.ContainsKey($key)) {
13098
$result.Add([PSCustomObject]@{
@@ -141,14 +109,13 @@ function Compare-CIPPIntuneObject {
141109
}
142110
}
143111
} elseif ($Object1 -is [Array] -or $Object1 -is [System.Collections.IList]) {
144-
# Compare arrays
145112
$maxLength = [Math]::Max($Object1.Count, $Object2.Count)
146113

147114
for ($i = 0; $i -lt $maxLength; $i++) {
148115
$newPath = "$PropertyPath.$i"
149116

150117
if ($i -lt $Object1.Count -and $i -lt $Object2.Count) {
151-
Compare-ObjectsRecursively -Object1 $Object1[$i] -Object2 $Object2[$i] -PropertyPath $newPath
118+
Compare-ObjectsRecursively -Object1 $Object1[$i] -Object2 $Object2[$i] -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth
152119
} elseif ($i -lt $Object1.Count) {
153120
$result.Add([PSCustomObject]@{
154121
Property = $newPath
@@ -164,7 +131,6 @@ function Compare-CIPPIntuneObject {
164131
}
165132
}
166133
} elseif ($Object1 -is [PSCustomObject] -or $Object1.PSObject.Properties.Count -gt 0) {
167-
# Compare PSCustomObjects or objects with properties
168134
$allPropertyNames = @(
169135
$Object1.PSObject.Properties | Select-Object -ExpandProperty Name
170136
$Object2.PSObject.Properties | Select-Object -ExpandProperty Name
@@ -178,9 +144,8 @@ function Compare-CIPPIntuneObject {
178144
$prop2Exists = $Object2.PSObject.Properties.Name -contains $propName
179145

180146
if ($prop1Exists -and $prop2Exists) {
181-
#only run if both props are not null
182147
if ($Object1.$propName -and $Object2.$propName) {
183-
Compare-ObjectsRecursively -Object1 $Object1.$propName -Object2 $Object2.$propName -PropertyPath $newPath
148+
Compare-ObjectsRecursively -Object1 $Object1.$propName -Object2 $Object2.$propName -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth
184149
}
185150
} elseif ($prop1Exists) {
186151
$result.Add([PSCustomObject]@{
@@ -197,7 +162,6 @@ function Compare-CIPPIntuneObject {
197162
}
198163
}
199164
} else {
200-
# Compare primitive values
201165
$val1 = $Object1.ToString()
202166
$val2 = $Object2.ToString()
203167

@@ -211,7 +175,6 @@ function Compare-CIPPIntuneObject {
211175
}
212176
}
213177

214-
# Convert objects to PowerShell objects if they're not already
215178
$obj1 = if ($ReferenceObject -is [string]) {
216179
$ReferenceObject | ConvertFrom-Json -AsHashtable -Depth 100
217180
} else {
@@ -224,20 +187,16 @@ function Compare-CIPPIntuneObject {
224187
$DifferenceObject
225188
}
226189

227-
# Start the recursive comparison
228-
#only do the compare if the objects are not null
229190
if ($obj1 -and $obj2) {
230191
Compare-ObjectsRecursively -Object1 $obj1 -Object2 $obj2
231192
}
232193

233-
# If no differences found, return null
234194
if ($result.Count -eq 0) {
235195
return $null
236196
}
237197
} else {
238198
$intuneCollection = Get-Content .\intuneCollection.json | ConvertFrom-Json -ErrorAction SilentlyContinue
239199

240-
# Process reference object settings
241200
$referenceItems = $ReferenceObject.settings | ForEach-Object {
242201
$settingInstance = $_.settingInstance
243202
$intuneObj = $intuneCollection | Where-Object { $_.id -eq $settingInstance.settingDefinitionId }
@@ -264,8 +223,6 @@ function Compare-CIPPIntuneObject {
264223
$child.choiceSettingValue.value
265224
}
266225
}
267-
268-
# Add object to our temporary list
269226
[PSCustomObject]@{
270227
Key = "GroupChild-$($child.settingDefinitionId)"
271228
Label = $childLabel
@@ -331,7 +288,6 @@ function Compare-CIPPIntuneObject {
331288
$tempOutput
332289
}
333290

334-
# Process difference object settings
335291
$differenceItems = $DifferenceObject.settings | ForEach-Object {
336292
$settingInstance = $_.settingInstance
337293
$intuneObj = $intuneCollection | Where-Object { $_.id -eq $settingInstance.settingDefinitionId }
@@ -358,8 +314,6 @@ function Compare-CIPPIntuneObject {
358314
$child.choiceSettingValue.value
359315
}
360316
}
361-
362-
# Add object to our temporary list
363317
[PSCustomObject]@{
364318
Key = "GroupChild-$($child.settingDefinitionId)"
365319
Label = $childLabel
@@ -425,17 +379,14 @@ function Compare-CIPPIntuneObject {
425379
$tempOutput
426380
}
427381

428-
# Compare the items and create result
429382
$result = [System.Collections.Generic.List[PSObject]]::new()
430383

431-
# Group all items by Key for comparison
432384
$allKeys = @($referenceItems | Select-Object -ExpandProperty Key) + @($differenceItems | Select-Object -ExpandProperty Key) | Sort-Object -Unique
433385

434386
foreach ($key in $allKeys) {
435387
$refItem = $referenceItems | Where-Object { $_.Key -eq $key } | Select-Object -First 1
436388
$diffItem = $differenceItems | Where-Object { $_.Key -eq $key } | Select-Object -First 1
437389

438-
# Get the setting definition ID from the key
439390
$settingId = $key
440391
if ($key -like 'Simple-*') {
441392
$settingId = $key.Substring(7)
@@ -447,28 +398,22 @@ function Compare-CIPPIntuneObject {
447398
$settingId = $key.Substring(8)
448399
}
449400

450-
# Look up the setting in the collection
451401
$settingDefinition = $intuneCollection | Where-Object { $_.id -eq $settingId }
452402

453-
# Get the raw values
454403
$refRawValue = if ($refItem) { $refItem.Value } else { $null }
455404
$diffRawValue = if ($diffItem) { $diffItem.Value } else { $null }
456405

457-
# Try to translate the values to display names if they're option IDs
458406
$refValue = $refRawValue
459407
$diffValue = $diffRawValue
460408

461-
# If the setting has options, try to find the display name for the values
462409
if ($null -ne $settingDefinition -and $null -ne $settingDefinition.options) {
463-
# For reference value
464410
if ($null -ne $refRawValue -and $refRawValue -match '_\d+$') {
465411
$option = $settingDefinition.options | Where-Object { $_.id -eq $refRawValue }
466412
if ($null -ne $option -and $null -ne $option.displayName) {
467413
$refValue = $option.displayName
468414
}
469415
}
470416

471-
# For difference value
472417
if ($null -ne $diffRawValue -and $diffRawValue -match '_\d+$') {
473418
$option = $settingDefinition.options | Where-Object { $_.id -eq $diffRawValue }
474419
if ($null -ne $option -and $null -ne $option.displayName) {
@@ -477,7 +422,6 @@ function Compare-CIPPIntuneObject {
477422
}
478423
}
479424

480-
# Use the display name for the property label if available
481425
$label = if ($null -ne $settingDefinition -and $null -ne $settingDefinition.displayName) {
482426
$settingDefinition.displayName
483427
} elseif ($refItem) {
@@ -488,7 +432,6 @@ function Compare-CIPPIntuneObject {
488432
$key
489433
}
490434

491-
# Only add to result if values are different or one is missing
492435
if ($refRawValue -ne $diffRawValue -or $null -eq $refRawValue -or $null -eq $diffRawValue) {
493436
$result.Add([PSCustomObject]@{
494437
Property = $label
@@ -502,4 +445,3 @@ function Compare-CIPPIntuneObject {
502445
}
503446
return $result
504447
}
505-

0 commit comments

Comments
 (0)