Skip to content

Commit cc1d55c

Browse files
authored
Merge pull request #213 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 11ae706 + 00b1a51 commit cc1d55c

File tree

9 files changed

+137
-122
lines changed

9 files changed

+137
-122
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
function Get-ExoOnlineStringBytes {
2+
param([string]$SizeString)
3+
4+
# 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
5+
6+
if ($SizeString -match '\(([0-9,]+) bytes\)') {
7+
return [int]($Matches[1] -replace ',','')
8+
}
9+
10+
return 0
11+
}

Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1

Lines changed: 30 additions & 62 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,7 +42,6 @@ function Compare-CIPPIntuneObject {
7342
$excludeProps -contains $PropertyName)
7443
}
7544

76-
# Recursive function to compare objects deeply
7745
function Compare-ObjectsRecursively {
7846
param (
7947
[Parameter(Mandatory = $true)]
@@ -83,15 +51,24 @@ function Compare-CIPPIntuneObject {
8351
$Object2,
8452

8553
[Parameter(Mandatory = $false)]
86-
[string]$PropertyPath = ''
54+
[string]$PropertyPath = '',
55+
[int]$Depth = 0,
56+
[int]$MaxDepth = 20
8757
)
8858

89-
# If both objects are null or empty, they're equal
59+
if ($Depth -ge $MaxDepth) {
60+
$result.Add([PSCustomObject]@{
61+
Property = $PropertyPath
62+
ExpectedValue = '[MaxDepthExceeded]'
63+
ReceivedValue = '[MaxDepthExceeded]'
64+
})
65+
return
66+
}
67+
9068
if (($null -eq $Object1 -or $Object1 -eq '') -and ($null -eq $Object2 -or $Object2 -eq '')) {
9169
return
9270
}
9371

94-
# If one object is null but the other isn't, they're different
9572
if (($null -eq $Object1 -or $Object1 -eq '') -xor ($null -eq $Object2 -or $Object2 -eq '')) {
9673
$result.Add([PSCustomObject]@{
9774
Property = $PropertyPath
@@ -101,7 +78,6 @@ function Compare-CIPPIntuneObject {
10178
return
10279
}
10380

104-
# If objects are of different types, they're different
10581
if ($Object1.GetType() -ne $Object2.GetType()) {
10682
$result.Add([PSCustomObject]@{
10783
Property = $PropertyPath
@@ -111,9 +87,22 @@ function Compare-CIPPIntuneObject {
11187
return
11288
}
11389

114-
# Handle different object types
90+
# Short-circuit recursion for primitive types
91+
$primitiveTypes = @([double], [decimal], [datetime], [timespan], [guid] )
92+
foreach ($type in $primitiveTypes) {
93+
if ($Object1 -is $type -and $Object2 -is $type) {
94+
if ($Object1 -ne $Object2) {
95+
$result.Add([PSCustomObject]@{
96+
Property = $PropertyPath
97+
ExpectedValue = $Object1
98+
ReceivedValue = $Object2
99+
})
100+
}
101+
return
102+
}
103+
}
104+
115105
if ($Object1 -is [System.Collections.IDictionary]) {
116-
# Compare dictionaries
117106
$allKeys = @($Object1.Keys) + @($Object2.Keys) | Select-Object -Unique
118107

119108
foreach ($key in $allKeys) {
@@ -122,9 +111,8 @@ function Compare-CIPPIntuneObject {
122111
$newPath = if ($PropertyPath) { "$PropertyPath.$key" } else { $key }
123112

124113
if ($Object1.ContainsKey($key) -and $Object2.ContainsKey($key)) {
125-
#only run if both props are not null
126114
if ($Object1[$key] -and $Object2[$key]) {
127-
Compare-ObjectsRecursively -Object1 $Object1[$key] -Object2 $Object2[$key] -PropertyPath $newPath
115+
Compare-ObjectsRecursively -Object1 $Object1[$key] -Object2 $Object2[$key] -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth
128116
}
129117
} elseif ($Object1.ContainsKey($key)) {
130118
$result.Add([PSCustomObject]@{
@@ -141,14 +129,13 @@ function Compare-CIPPIntuneObject {
141129
}
142130
}
143131
} elseif ($Object1 -is [Array] -or $Object1 -is [System.Collections.IList]) {
144-
# Compare arrays
145132
$maxLength = [Math]::Max($Object1.Count, $Object2.Count)
146133

147134
for ($i = 0; $i -lt $maxLength; $i++) {
148135
$newPath = "$PropertyPath.$i"
149136

150137
if ($i -lt $Object1.Count -and $i -lt $Object2.Count) {
151-
Compare-ObjectsRecursively -Object1 $Object1[$i] -Object2 $Object2[$i] -PropertyPath $newPath
138+
Compare-ObjectsRecursively -Object1 $Object1[$i] -Object2 $Object2[$i] -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth
152139
} elseif ($i -lt $Object1.Count) {
153140
$result.Add([PSCustomObject]@{
154141
Property = $newPath
@@ -164,7 +151,6 @@ function Compare-CIPPIntuneObject {
164151
}
165152
}
166153
} elseif ($Object1 -is [PSCustomObject] -or $Object1.PSObject.Properties.Count -gt 0) {
167-
# Compare PSCustomObjects or objects with properties
168154
$allPropertyNames = @(
169155
$Object1.PSObject.Properties | Select-Object -ExpandProperty Name
170156
$Object2.PSObject.Properties | Select-Object -ExpandProperty Name
@@ -178,9 +164,8 @@ function Compare-CIPPIntuneObject {
178164
$prop2Exists = $Object2.PSObject.Properties.Name -contains $propName
179165

180166
if ($prop1Exists -and $prop2Exists) {
181-
#only run if both props are not null
182167
if ($Object1.$propName -and $Object2.$propName) {
183-
Compare-ObjectsRecursively -Object1 $Object1.$propName -Object2 $Object2.$propName -PropertyPath $newPath
168+
Compare-ObjectsRecursively -Object1 $Object1.$propName -Object2 $Object2.$propName -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth
184169
}
185170
} elseif ($prop1Exists) {
186171
$result.Add([PSCustomObject]@{
@@ -197,7 +182,6 @@ function Compare-CIPPIntuneObject {
197182
}
198183
}
199184
} else {
200-
# Compare primitive values
201185
$val1 = $Object1.ToString()
202186
$val2 = $Object2.ToString()
203187

@@ -211,7 +195,6 @@ function Compare-CIPPIntuneObject {
211195
}
212196
}
213197

214-
# Convert objects to PowerShell objects if they're not already
215198
$obj1 = if ($ReferenceObject -is [string]) {
216199
$ReferenceObject | ConvertFrom-Json -AsHashtable -Depth 100
217200
} else {
@@ -224,13 +207,10 @@ function Compare-CIPPIntuneObject {
224207
$DifferenceObject
225208
}
226209

227-
# Start the recursive comparison
228-
#only do the compare if the objects are not null
229210
if ($obj1 -and $obj2) {
230211
Compare-ObjectsRecursively -Object1 $obj1 -Object2 $obj2
231212
}
232213

233-
# If no differences found, return null
234214
if ($result.Count -eq 0) {
235215
return $null
236216
}
@@ -425,17 +405,14 @@ function Compare-CIPPIntuneObject {
425405
$tempOutput
426406
}
427407

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

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

434412
foreach ($key in $allKeys) {
435413
$refItem = $referenceItems | Where-Object { $_.Key -eq $key } | Select-Object -First 1
436414
$diffItem = $differenceItems | Where-Object { $_.Key -eq $key } | Select-Object -First 1
437415

438-
# Get the setting definition ID from the key
439416
$settingId = $key
440417
if ($key -like 'Simple-*') {
441418
$settingId = $key.Substring(7)
@@ -447,28 +424,22 @@ function Compare-CIPPIntuneObject {
447424
$settingId = $key.Substring(8)
448425
}
449426

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

453-
# Get the raw values
454429
$refRawValue = if ($refItem) { $refItem.Value } else { $null }
455430
$diffRawValue = if ($diffItem) { $diffItem.Value } else { $null }
456431

457-
# Try to translate the values to display names if they're option IDs
458432
$refValue = $refRawValue
459433
$diffValue = $diffRawValue
460434

461-
# If the setting has options, try to find the display name for the values
462435
if ($null -ne $settingDefinition -and $null -ne $settingDefinition.options) {
463-
# For reference value
464436
if ($null -ne $refRawValue -and $refRawValue -match '_\d+$') {
465437
$option = $settingDefinition.options | Where-Object { $_.id -eq $refRawValue }
466438
if ($null -ne $option -and $null -ne $option.displayName) {
467439
$refValue = $option.displayName
468440
}
469441
}
470442

471-
# For difference value
472443
if ($null -ne $diffRawValue -and $diffRawValue -match '_\d+$') {
473444
$option = $settingDefinition.options | Where-Object { $_.id -eq $diffRawValue }
474445
if ($null -ne $option -and $null -ne $option.displayName) {
@@ -477,7 +448,6 @@ function Compare-CIPPIntuneObject {
477448
}
478449
}
479450

480-
# Use the display name for the property label if available
481451
$label = if ($null -ne $settingDefinition -and $null -ne $settingDefinition.displayName) {
482452
$settingDefinition.displayName
483453
} elseif ($refItem) {
@@ -488,7 +458,6 @@ function Compare-CIPPIntuneObject {
488458
$key
489459
}
490460

491-
# Only add to result if values are different or one is missing
492461
if ($refRawValue -ne $diffRawValue -or $null -eq $refRawValue -or $null -eq $diffRawValue) {
493462
$result.Add([PSCustomObject]@{
494463
Property = $label
@@ -502,4 +471,3 @@ function Compare-CIPPIntuneObject {
502471
}
503472
return $result
504473
}
505-

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFA.ps1

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,31 @@ function Invoke-ExecPerUserMFA {
66
.ROLE
77
Identity.User.ReadWrite
88
#>
9-
Param(
10-
$Request,
11-
$TriggerMetadata
12-
)
9+
Param($Request, $TriggerMetadata)
10+
11+
$APIName = $Request.Params.CIPPEndpoint
12+
$Headers = $Request.Headers
13+
Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug'
14+
1315

1416
$Request = @{
15-
userId = $Request.Body.userId
16-
TenantFilter = $Request.Body.TenantFilter
17-
State = $Request.Body.State.value ? $Request.Body.State.value : $Request.Body.State
18-
Headers = $Request.Headers
17+
userId = $Request.Body.userId
18+
TenantFilter = $Request.Body.tenantFilter
19+
State = $Request.Body.State.value ? $Request.Body.State.value : $Request.Body.State
20+
Headers = $Headers
21+
APIName = $APIName
1922
}
20-
$Result = Set-CIPPPerUserMFA @Request
21-
$Body = @{
22-
Results = @($Result)
23+
try {
24+
$Result = Set-CIPPPerUserMFA @Request
25+
$StatusCode = [HttpStatusCode]::OK
26+
} catch {
27+
$Result = $_.Exception.Message
28+
$StatusCode = [HttpStatusCode]::InternalServerError
2329
}
30+
31+
# Associate values to output bindings by calling 'Push-OutputBinding'.
2432
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
25-
StatusCode = [HttpStatusCode]::OK
26-
Body = $Body
33+
StatusCode = $StatusCode
34+
Body = @{ 'Results' = @($Result) }
2735
})
2836
}

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecPerUserMFAAllUsers.ps1

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,22 @@ function Invoke-ExecPerUserMFAAllUsers {
66
.ROLE
77
Identity.User.ReadWrite
88
#>
9-
Param(
10-
$Request,
11-
$TriggerMetadata
12-
)
13-
$TenantFilter = $request.query.TenantFilter
9+
Param($Request, $TriggerMetadata)
10+
11+
$APIName = $Request.Params.CIPPEndpoint
12+
$Headers = $Request.Headers
13+
Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug'
14+
15+
# XXX Seems to be an unused endpoint? - Bobby
16+
17+
$TenantFilter = $request.Query.tenantFilter
1418
$Users = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users' -tenantid $TenantFilter
1519
$Request = @{
16-
userId = $Users.id
17-
TenantFilter = $tenantfilter
18-
State = $Request.query.State
19-
Headers = $Request.Headers
20+
userId = $Users.id
21+
TenantFilter = $TenantFilter
22+
State = $Request.Query.State
23+
Headers = $Request.Headers
24+
APIName = $APIName
2025
}
2126
$Result = Set-CIPPPerUserMFA @Request
2227
$Body = @{

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ function Invoke-ListUserMailboxDetails {
164164
$ProhibitSendQuotaString = $MailboxDetailedRequest.ProhibitSendQuota -split ' '
165165
$ProhibitSendReceiveQuotaString = $MailboxDetailedRequest.ProhibitSendReceiveQuota -split ' '
166166
$TotalItemSizeString = $StatsRequest.TotalItemSize -split ' '
167-
$TotalArchiveItemSizeString = $ArchiveSizeRequest.TotalItemSize -split ' '
167+
$TotalArchiveItemSizeString = Get-ExoOnlineStringBytes -SizeString $ArchiveSizeRequest.TotalItemSize.Value
168168

169169
$ProhibitSendQuota = try { [math]::Round([float]($ProhibitSendQuotaString[0]), 2) } catch { 0 }
170170
$ProhibitSendReceiveQuota = try { [math]::Round([float]($ProhibitSendReceiveQuotaString[0]), 2) } catch { 0 }

0 commit comments

Comments
 (0)