Skip to content

Commit 773b5e8

Browse files
fix intune compare and template deployment
1 parent 1751bc2 commit 773b5e8

File tree

2 files changed

+153
-113
lines changed

2 files changed

+153
-113
lines changed

Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1

Lines changed: 147 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -61,140 +61,179 @@ function Compare-CIPPIntuneObject {
6161
$excludeProps = $defaultExcludeProperties + $ExcludeProperties
6262

6363
# Create a list to store comparison results
64-
$compareProperties = [System.Collections.Generic.List[PSObject]]::new()
65-
66-
# Clean up objects by removing excluded properties
67-
$obj1 = $ReferenceObject | Select-Object * -ExcludeProperty @($excludeProps | ForEach-Object { $_ })
68-
$obj2 = $DifferenceObject | Select-Object * -ExcludeProperty @($excludeProps | ForEach-Object { $_ })
64+
$result = [System.Collections.Generic.List[PSObject]]::new()
6965

70-
# Skip OData properties and excluded properties
71-
$skipProps = [System.Collections.Generic.List[string]]::new()
72-
foreach ($propName in ($obj1.PSObject.Properties | Select-Object Name).Name) {
73-
if ($propName -like '*@OData*' -or $propName -like '#microsoft.graph*' -or $excludeProps -contains $propName) {
74-
$skipProps.Add($propName)
75-
}
66+
# Helper function to check if a property should be skipped
67+
function ShouldSkipProperty {
68+
param (
69+
[string]$PropertyName
70+
)
71+
return ($PropertyName -like '*@OData*' -or
72+
$PropertyName -like '#microsoft.graph*' -or
73+
$excludeProps -contains $PropertyName)
7674
}
7775

78-
# Define core properties to compare first
79-
$coreProps = @('displayName', 'Description', 'Id')
80-
$postProps = @('Advertisements')
81-
$skipPropertiesToCompare = @()
76+
# Recursive function to compare objects deeply
77+
function Compare-ObjectsRecursively {
78+
param (
79+
[Parameter(Mandatory = $true)]
80+
$Object1,
8281

83-
# Compare core properties
84-
foreach ($propName in $coreProps) {
85-
if (-not ($obj1.PSObject.Properties | Where-Object Name -EQ $propName)) {
86-
continue
87-
}
88-
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
89-
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
90-
91-
$value1 = if ($null -eq $val1) { '' } else { $val1.ToString().Trim('"') }
92-
$value2 = if ($null -eq $val2) { '' } else { $val2.ToString().Trim('"') }
82+
[Parameter(Mandatory = $true)]
83+
$Object2,
9384

94-
$match = ($value1 -eq $value2)
85+
[Parameter(Mandatory = $false)]
86+
[string]$PropertyPath = ""
87+
)
9588

96-
if (-not $match) {
97-
$compareProperties.Add([PSCustomObject]@{
98-
PropertyName = $propName
99-
Object1Value = $value1
100-
Object2Value = $value2
101-
Match = $match
102-
})
89+
# If both objects are null or empty, they're equal
90+
if (($null -eq $Object1 -or $Object1 -eq '') -and ($null -eq $Object2 -or $Object2 -eq '')) {
91+
return
10392
}
104-
}
105-
106-
# Compare all other properties
107-
$addedProps = [System.Collections.Generic.List[string]]::new()
108-
foreach ($propName in ($obj1.PSObject.Properties | Select-Object Name).Name) {
109-
if ($propName -in $coreProps) { continue }
110-
if ($propName -in $postProps) { continue }
111-
if ($propName -in $skipProps) { continue }
112-
113-
if ($propName -like '*@OData*' -or $propName -like '#microsoft.graph*') { continue }
11493

115-
$addedProps.Add($propName)
116-
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
117-
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
118-
119-
$value1 = if ($null -eq $val1) { '' } else { $val1.ToString().Trim('"') }
120-
$value2 = if ($null -eq $val2) { '' } else { $val2.ToString().Trim('"') }
121-
122-
$match = ($value1 -eq $value2)
94+
# If one object is null but the other isn't, they're different
95+
if (($null -eq $Object1 -or $Object1 -eq '') -xor ($null -eq $Object2 -or $Object2 -eq '')) {
96+
$result.Add([PSCustomObject]@{
97+
Property = $PropertyPath
98+
ExpectedValue = if ($null -eq $Object1) { '' } else { $Object1 }
99+
ReceivedValue = if ($null -eq $Object2) { '' } else { $Object2 }
100+
})
101+
return
102+
}
123103

124-
if (-not $match) {
125-
$compareProperties.Add([PSCustomObject]@{
126-
PropertyName = $propName
127-
Object1Value = $value1
128-
Object2Value = $value2
129-
Match = $match
130-
})
104+
# If objects are of different types, they're different
105+
if ($Object1.GetType() -ne $Object2.GetType()) {
106+
$result.Add([PSCustomObject]@{
107+
Property = $PropertyPath
108+
ExpectedValue = $Object1
109+
ReceivedValue = $Object2
110+
})
111+
return
131112
}
132-
}
133113

134-
# Check for properties in obj2 that aren't in obj1
135-
foreach ($propName in ($obj2.PSObject.Properties | Select-Object Name).Name) {
136-
if ($propName -in $coreProps) { continue }
137-
if ($propName -in $postProps) { continue }
138-
if ($propName -in $skipProps) { continue }
139-
if ($propName -in $addedProps) { continue }
114+
# Handle different object types
115+
if ($Object1 -is [System.Collections.IDictionary]) {
116+
# Compare dictionaries
117+
$allKeys = @($Object1.Keys) + @($Object2.Keys) | Select-Object -Unique
140118

141-
if ($propName -like '*@OData*' -or $propName -like '#microsoft.graph*') { continue }
119+
foreach ($key in $allKeys) {
120+
if (ShouldSkipProperty -PropertyName $key) { continue }
142121

143-
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
144-
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
122+
$newPath = if ($PropertyPath) { "$PropertyPath.$key" } else { $key }
145123

146-
$value1 = if ($null -eq $val1) { '' } else { $val1.ToString().Trim('"') }
147-
$value2 = if ($null -eq $val2) { '' } else { $val2.ToString().Trim('"') }
124+
if ($Object1.ContainsKey($key) -and $Object2.ContainsKey($key)) {
125+
Compare-ObjectsRecursively -Object1 $Object1[$key] -Object2 $Object2[$key] -PropertyPath $newPath
126+
}
127+
elseif ($Object1.ContainsKey($key)) {
128+
$result.Add([PSCustomObject]@{
129+
Property = $newPath
130+
ExpectedValue = $Object1[$key]
131+
ReceivedValue = ''
132+
})
133+
}
134+
else {
135+
$result.Add([PSCustomObject]@{
136+
Property = $newPath
137+
ExpectedValue = ''
138+
ReceivedValue = $Object2[$key]
139+
})
140+
}
141+
}
142+
}
143+
elseif ($Object1 -is [Array] -or $Object1 -is [System.Collections.IList]) {
144+
# Compare arrays
145+
$maxLength = [Math]::Max($Object1.Count, $Object2.Count)
148146

149-
$match = ($value1 -eq $value2)
147+
for ($i = 0; $i -lt $maxLength; $i++) {
148+
$newPath = "$PropertyPath.$i"
150149

151-
if (-not $match) {
152-
$compareProperties.Add([PSCustomObject]@{
153-
PropertyName = $propName
154-
Object1Value = $value1
155-
Object2Value = $value2
156-
Match = $match
150+
if ($i -lt $Object1.Count -and $i -lt $Object2.Count) {
151+
Compare-ObjectsRecursively -Object1 $Object1[$i] -Object2 $Object2[$i] -PropertyPath $newPath
152+
}
153+
elseif ($i -lt $Object1.Count) {
154+
$result.Add([PSCustomObject]@{
155+
Property = $newPath
156+
ExpectedValue = $Object1[$i]
157+
ReceivedValue = ''
158+
})
159+
}
160+
else {
161+
$result.Add([PSCustomObject]@{
162+
Property = $newPath
163+
ExpectedValue = ''
164+
ReceivedValue = $Object2[$i]
165+
})
166+
}
167+
}
168+
}
169+
elseif ($Object1 -is [PSCustomObject] -or $Object1.PSObject.Properties.Count -gt 0) {
170+
# Compare PSCustomObjects or objects with properties
171+
$allPropertyNames = @(
172+
$Object1.PSObject.Properties | Select-Object -ExpandProperty Name
173+
$Object2.PSObject.Properties | Select-Object -ExpandProperty Name
174+
) | Select-Object -Unique
175+
176+
foreach ($propName in $allPropertyNames) {
177+
if (ShouldSkipProperty -PropertyName $propName) { continue }
178+
179+
$newPath = if ($PropertyPath) { "$PropertyPath.$propName" } else { $propName }
180+
$prop1Exists = $Object1.PSObject.Properties.Name -contains $propName
181+
$prop2Exists = $Object2.PSObject.Properties.Name -contains $propName
182+
183+
if ($prop1Exists -and $prop2Exists) {
184+
Compare-ObjectsRecursively -Object1 $Object1.$propName -Object2 $Object2.$propName -PropertyPath $newPath
185+
}
186+
elseif ($prop1Exists) {
187+
$result.Add([PSCustomObject]@{
188+
Property = $newPath
189+
ExpectedValue = $Object1.$propName
190+
ReceivedValue = ''
191+
})
192+
}
193+
else {
194+
$result.Add([PSCustomObject]@{
195+
Property = $newPath
196+
ExpectedValue = ''
197+
ReceivedValue = $Object2.$propName
198+
})
199+
}
200+
}
201+
}
202+
else {
203+
# Compare primitive values
204+
$val1 = $Object1.ToString()
205+
$val2 = $Object2.ToString()
206+
207+
if ($val1 -ne $val2) {
208+
$result.Add([PSCustomObject]@{
209+
Property = $PropertyPath
210+
ExpectedValue = $val1
211+
ReceivedValue = $val2
157212
})
213+
}
158214
}
159215
}
160216

161-
# Compare post properties (like Advertisements)
162-
foreach ($propName in $postProps) {
163-
if (-not ($obj1.PSObject.Properties | Where-Object Name -EQ $propName)) {
164-
continue
165-
}
166-
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
167-
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
168-
169-
$value1 = if ($null -eq $val1) { '' } else { $val1.ToString().Trim('"') }
170-
$value2 = if ($null -eq $val2) { '' } else { $val2.ToString().Trim('"') }
171-
172-
$match = ($value1 -eq $value2)
217+
# Convert objects to PowerShell objects if they're not already
218+
$obj1 = if ($ReferenceObject -is [string]) {
219+
$ReferenceObject | ConvertFrom-Json -AsHashtable -Depth 100
220+
} else {
221+
$ReferenceObject
222+
}
173223

174-
if (-not $match) {
175-
$compareProperties.Add([PSCustomObject]@{
176-
PropertyName = $propName
177-
Object1Value = $value1
178-
Object2Value = $value2
179-
Match = $match
180-
})
181-
}
224+
$obj2 = if ($DifferenceObject -is [string]) {
225+
$DifferenceObject | ConvertFrom-Json -AsHashtable -Depth 100
226+
} else {
227+
$DifferenceObject
182228
}
183229

230+
# Start the recursive comparison
231+
Compare-ObjectsRecursively -Object1 $obj1 -Object2 $obj2
232+
184233
# If no differences found, return null
185-
if ($compareProperties.Count -eq 0) {
234+
if ($result.Count -eq 0) {
186235
return $null
187236
}
188-
189-
# Convert to a more user-friendly format
190-
$result = [System.Collections.Generic.List[PSObject]]::new()
191-
foreach ($diff in $compareProperties) {
192-
$result.Add([PSCustomObject]@{
193-
Property = $diff.PropertyName
194-
ExpectedValue = $diff.Object1Value
195-
ReceivedValue = $diff.Object2Value
196-
})
197-
}
198237
} else {
199238
$intuneCollection = Get-Content .\intuneCollection.json | ConvertFrom-Json -ErrorAction SilentlyContinue
200239

Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,20 +65,21 @@ function Invoke-CIPPStandardIntuneTemplate {
6565
excludeGroup = $Template.excludeGroup
6666
remediate = $Template.remediate
6767
existingPolicyId = $ExistingPolicy.id
68+
templateId = $Template.TemplateList.value
6869
}
6970
} else {
7071
[PSCustomObject]@{
7172
MatchFailed = $false
7273
displayname = $displayname
7374
description = $description
74-
compare = $Compare
75+
compare = $false
7576
rawJSON = $RawJSON
7677
body = $Request.body
7778
assignTo = $Template.AssignTo
7879
excludeGroup = $Template.excludeGroup
7980
remediate = $Template.remediate
8081
existingPolicyId = $ExistingPolicy.id
81-
82+
templateId = $Template.TemplateList.value
8283
}
8384
}
8485
}
@@ -117,9 +118,9 @@ function Invoke-CIPPStandardIntuneTemplate {
117118

118119
if ($Settings.report) {
119120
foreach ($Template in $CompareList) {
120-
$id = $Template.body.RowKey
121-
$Compare = $Template.compare
122-
$state = $Compare ? $Compare : $true
121+
$id = $Template.templateId
122+
$CompareObj = $Template.compare
123+
$state = $CompareObj ? $CompareObj : $true
123124
Set-CIPPStandardsCompareField -FieldName "standards.IntuneTemplate.$id" -FieldValue $state -TenantFilter $Tenant
124125
}
125126
Add-CIPPBPAField -FieldName "policy-$id" -FieldValue $Compare -StoreAs bool -Tenant $tenant

0 commit comments

Comments
 (0)