Skip to content

Commit 96cdd44

Browse files
authored
Merge pull request #110 from KelvinTegelaar/master
[pull] master from KelvinTegelaar:master
2 parents fa13e10 + fa00351 commit 96cdd44

33 files changed

+653
-126
lines changed

CIPPTimers.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"Id": "9b0c8e50-f798-49db-9a8b-dbcc0fcadeea",
7676
"Command": "Start-StandardsOrchestrator",
7777
"Description": "Orchestrator to process standards",
78-
"Cron": "0 0 */4 * * *",
78+
"Cron": "0 0 */12 * * *",
7979
"Priority": 4,
8080
"RunOnProcessor": true,
8181
"PreferredProcessor": "standards"

Modules/CIPPCore/Public/Alerts/Get-CIPPAlertGroupMembershipChange.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function Get-CIPPAlertGroupMembershipChange {
1919
$AuditLogs = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?`$filter=activityDateTime ge $OneHourAgo and (activityDisplayName eq 'Add member to group' or activityDisplayName eq 'Remove member from group')" -tenantid $TenantFilter
2020

2121
$AlertData = foreach ($Log in $AuditLogs) {
22-
$Member = ($Log.targetResources | Where-Object { $_.type -in @('User', 'ServicePrincipal') })[0]
22+
$Member = ($Log.targetResources | Where-Object { $_.type -in @('User', 'ServicePrincipal', 'Group') })[0]
2323
$GroupProp = ($Member.modifiedProperties | Where-Object { $_.displayName -eq 'Group.DisplayName' })
2424
$GroupDisplayName = (($GroupProp.newValue ?? $GroupProp.oldValue) -replace '"', '')
2525
if (!$GroupDisplayName -or !($MonitoredGroups | Where-Object { $GroupDisplayName -like $_ })) { continue }

Modules/CIPPCore/Public/Compare-CIPPIntuneObject.ps1

Lines changed: 90 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ function Compare-CIPPIntuneObject {
3434
'agents',
3535
'isSynced'
3636
'locationInfo',
37-
'templateId'
37+
'templateId',
38+
'source'
3839
)
3940

4041
$excludeProps = $defaultExcludeProperties + $ExcludeProperties
@@ -49,6 +50,38 @@ function Compare-CIPPIntuneObject {
4950
$excludeProps -contains $PropertyName)
5051
}
5152

53+
function ShouldCompareAsUnorderedSet {
54+
param (
55+
[string]$PropertyPath
56+
)
57+
# Properties that should be compared as sets (order doesn't matter)
58+
$unorderedSetPatterns = @(
59+
'includeGroups',
60+
'excludeGroups',
61+
'includeUsers',
62+
'excludeUsers',
63+
'includeApplications',
64+
'excludeApplications',
65+
'includeRoles',
66+
'excludeRoles',
67+
'includePlatforms',
68+
'excludePlatforms',
69+
'includeLocations',
70+
'excludeLocations',
71+
'includeDevices',
72+
'excludeDevices',
73+
'includeGuestOrExternalUserTypes',
74+
'excludeGuestOrExternalUserTypes'
75+
)
76+
77+
foreach ($pattern in $unorderedSetPatterns) {
78+
if ($PropertyPath -match "\.$pattern(\.\d+)?$" -or $PropertyPath -eq $pattern) {
79+
return $true
80+
}
81+
}
82+
return $false
83+
}
84+
5285
function Compare-ObjectsRecursively {
5386
param (
5487
[Parameter(Mandatory = $true)]
@@ -136,25 +169,65 @@ function Compare-CIPPIntuneObject {
136169
}
137170
}
138171
} elseif ($Object1 -is [Array] -or $Object1 -is [System.Collections.IList]) {
139-
$maxLength = [Math]::Max($Object1.Count, $Object2.Count)
140-
141-
for ($i = 0; $i -lt $maxLength; $i++) {
142-
$newPath = "$PropertyPath.$i"
143-
144-
if ($i -lt $Object1.Count -and $i -lt $Object2.Count) {
145-
Compare-ObjectsRecursively -Object1 $Object1[$i] -Object2 $Object2[$i] -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth
146-
} elseif ($i -lt $Object1.Count) {
172+
# Check if this array should be compared as an unordered set
173+
if (ShouldCompareAsUnorderedSet -PropertyPath $PropertyPath) {
174+
# For unordered sets, compare contents regardless of order
175+
if ($Object1.Count -ne $Object2.Count) {
176+
# Different lengths - report the difference
147177
$result.Add([PSCustomObject]@{
148-
Property = $newPath
149-
ExpectedValue = $Object1[$i]
150-
ReceivedValue = ''
178+
Property = $PropertyPath
179+
ExpectedValue = "Array with $($Object1.Count) items"
180+
ReceivedValue = "Array with $($Object2.Count) items"
151181
})
152182
} else {
153-
$result.Add([PSCustomObject]@{
154-
Property = $newPath
155-
ExpectedValue = ''
156-
ReceivedValue = $Object2[$i]
157-
})
183+
# Same length - check if all items exist in both arrays
184+
$array1Sorted = @($Object1 | Sort-Object)
185+
$array2Sorted = @($Object2 | Sort-Object)
186+
187+
for ($i = 0; $i -lt $array1Sorted.Count; $i++) {
188+
$item1 = $array1Sorted[$i]
189+
$item2 = $array2Sorted[$i]
190+
191+
# For primitive types, direct comparison
192+
if ($item1 -is [string] -or $item1 -is [int] -or $item1 -is [long] -or $item1 -is [bool] -or $item1 -is [double] -or $item1 -is [decimal]) {
193+
if ($item1 -ne $item2) {
194+
# Items don't match even after sorting - arrays have different contents
195+
$result.Add([PSCustomObject]@{
196+
Property = $PropertyPath
197+
ExpectedValue = ($Object1 -join ', ')
198+
ReceivedValue = ($Object2 -join ', ')
199+
})
200+
break
201+
}
202+
} else {
203+
# For complex objects, recursively compare with a generic path
204+
$newPath = "$PropertyPath[$i]"
205+
Compare-ObjectsRecursively -Object1 $item1 -Object2 $item2 -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth
206+
}
207+
}
208+
}
209+
} else {
210+
# Ordered array comparison (original behavior)
211+
$maxLength = [Math]::Max($Object1.Count, $Object2.Count)
212+
213+
for ($i = 0; $i -lt $maxLength; $i++) {
214+
$newPath = "$PropertyPath.$i"
215+
216+
if ($i -lt $Object1.Count -and $i -lt $Object2.Count) {
217+
Compare-ObjectsRecursively -Object1 $Object1[$i] -Object2 $Object2[$i] -PropertyPath $newPath -Depth ($Depth + 1) -MaxDepth $MaxDepth
218+
} elseif ($i -lt $Object1.Count) {
219+
$result.Add([PSCustomObject]@{
220+
Property = $newPath
221+
ExpectedValue = $Object1[$i]
222+
ReceivedValue = ''
223+
})
224+
} else {
225+
$result.Add([PSCustomObject]@{
226+
Property = $newPath
227+
ExpectedValue = ''
228+
ReceivedValue = $Object2[$i]
229+
})
230+
}
158231
}
159232
}
160233
} elseif ($Object1 -is [PSCustomObject] -or $Object1.PSObject.Properties.Count -gt 0) {

Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-UpdatePermissionsQueue.ps1

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,24 @@ function Push-UpdatePermissionsQueue {
2525
$DomainRefreshRequired = $true
2626
}
2727
Write-Information 'Updating permissions'
28-
Add-CIPPApplicationPermission -RequiredResourceAccess 'CIPPDefaults' -ApplicationId $env:ApplicationID -tenantfilter $Item.customerId
29-
Add-CIPPDelegatedPermission -RequiredResourceAccess 'CIPPDefaults' -ApplicationId $env:ApplicationID -tenantfilter $Item.customerId
30-
Write-LogMessage -tenant $Item.defaultDomainName -tenantId $Item.customerId -message "Updated permissions for $($Item.displayName)" -Sev 'Info' -API 'UpdatePermissionsQueue'
28+
$AppResults = Add-CIPPApplicationPermission -RequiredResourceAccess 'CIPPDefaults' -ApplicationId $env:ApplicationID -tenantfilter $Item.customerId
29+
$DelegatedResults = Add-CIPPDelegatedPermission -RequiredResourceAccess 'CIPPDefaults' -ApplicationId $env:ApplicationID -tenantfilter $Item.customerId
30+
31+
# Check for permission failures (excluding service principal creation failures)
32+
$AllResults = @($AppResults) + @($DelegatedResults)
33+
$PermissionFailures = $AllResults | Where-Object {
34+
$_ -like '*Failed*' -and
35+
$_ -notlike '*Failed to create service principal*'
36+
}
37+
38+
if ($PermissionFailures) {
39+
$Status = 'Failed'
40+
$FailureMessage = ($PermissionFailures -join '; ')
41+
Write-LogMessage -tenant $Item.defaultDomainName -tenantId $Item.customerId -message "Permission update completed with failures for $($Item.displayName): $FailureMessage" -Sev 'Warn' -API 'UpdatePermissionsQueue'
42+
} else {
43+
$Status = 'Success'
44+
Write-LogMessage -tenant $Item.defaultDomainName -tenantId $Item.customerId -message "Updated permissions for $($Item.displayName)" -Sev 'Info' -API 'UpdatePermissionsQueue'
45+
}
3146

3247
if ($Item.defaultDomainName -ne 'PartnerTenant') {
3348
Write-Information 'Pushing CIPP-SAM admin roles'
@@ -38,11 +53,15 @@ function Push-UpdatePermissionsQueue {
3853
$unixtime = [int64](([datetime]::UtcNow) - (Get-Date '1/1/1970')).TotalSeconds
3954
$GraphRequest = @{
4055
LastApply = "$unixtime"
56+
LastStatus = "$Status"
4157
applicationId = "$($env:ApplicationID)"
4258
Tenant = "$($Item.customerId)"
4359
PartitionKey = 'Tenant'
4460
RowKey = "$($Item.customerId)"
4561
}
62+
if ($PermissionFailures) {
63+
$GraphRequest.LastError = $FailureMessage
64+
}
4665
Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force
4766

4867
if ($DomainRefreshRequired) {
@@ -53,5 +72,6 @@ function Push-UpdatePermissionsQueue {
5372
}
5473
} catch {
5574
Write-Information "Error updating permissions for $($Item.displayName)"
75+
Write-LogMessage -tenant $Item.defaultDomainName -tenantId $Item.customerId -message "Error updating permissions for $($Item.displayName) - $($_.Exception.Message)" -Sev 'Error' -API 'UpdatePermissionsQueue'
5676
}
5777
}

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecDnsConfig.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ function Invoke-ExecDnsConfig {
33
.FUNCTIONALITY
44
Entrypoint
55
.ROLE
6-
CIPP.AppSettings.ReadWrite
6+
Tenant.Domains.ReadWrite
77
#>
88
[CmdletBinding()]
99
param($Request, $TriggerMetadata)

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Contacts/Invoke-ListContactTemplates.ps1

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
Function Invoke-ListContactTemplates {
1+
function Invoke-ListContactTemplates {
22
<#
33
.FUNCTIONALITY
44
Entrypoint,AnyTenant
55
.ROLE
6-
Exchange.Read
6+
Exchange.Contact.Read
77
#>
88
[CmdletBinding()]
99
param($Request, $TriggerMetadata)
@@ -39,9 +39,9 @@ Function Invoke-ListContactTemplates {
3939
if (-not $Templates) {
4040
Write-LogMessage -headers $Headers -API $APIName -message "Template with ID $RequestedID not found" -sev 'Warn'
4141
return ([HttpResponseContext]@{
42-
StatusCode = [HttpStatusCode]::NotFound
43-
Body = @{ Error = "Template with ID $RequestedID not found" }
44-
})
42+
StatusCode = [HttpStatusCode]::NotFound
43+
Body = @{ Error = "Template with ID $RequestedID not found" }
44+
})
4545
return
4646
}
4747
} else {

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,27 @@ function Invoke-ListMailboxes {
99
param($Request, $TriggerMetadata)
1010
# Interact with query parameters or the body of the request.
1111
$TenantFilter = $Request.Query.tenantFilter
12+
$UseReportDB = $Request.Query.UseReportDB
13+
1214
try {
15+
# If UseReportDB is specified, retrieve from report database
16+
if ($UseReportDB -eq 'true') {
17+
try {
18+
$GraphRequest = Get-CIPPMailboxesReport -TenantFilter $TenantFilter -ErrorAction Stop
19+
$StatusCode = [HttpStatusCode]::OK
20+
} catch {
21+
Write-Host "Error retrieving mailboxes from report database: $($_.Exception.Message)"
22+
$StatusCode = [HttpStatusCode]::InternalServerError
23+
$GraphRequest = $_.Exception.Message
24+
}
25+
26+
return ([HttpResponseContext]@{
27+
StatusCode = $StatusCode
28+
Body = @($GraphRequest)
29+
})
30+
}
31+
32+
# Original live EXO logic
1333
$Select = 'id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses,WhenSoftDeleted,IsInactiveMailbox,ForwardingSmtpAddress,DeliverToMailboxAndForward,ForwardingAddress,HiddenFromAddressListsEnabled,ExternalDirectoryObjectId,MessageCopyForSendOnBehalfEnabled,MessageCopyForSentAsEnabled,PersistedCapabilities,LitigationHoldEnabled,LitigationHoldDate,LitigationHoldDuration,ComplianceTagHoldApplied,RetentionHoldEnabled,InPlaceHolds,RetentionPolicy'
1434
$ExoRequest = @{
1535
tenantid = $TenantFilter

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Transport/Invoke-AddEditTransportRule.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ function Invoke-AddEditTransportRule {
8282
$DeleteMessage = $Request.Body.DeleteMessage
8383
$Quarantine = $Request.Body.Quarantine
8484
$RedirectMessageTo = $Request.Body.RedirectMessageTo
85+
$RouteMessageOutboundConnector = $Request.Body.RouteMessageOutboundConnector
8586
$BlindCopyTo = $Request.Body.BlindCopyTo
8687
$CopyTo = $Request.Body.CopyTo
8788
$ModerateMessageByUser = $Request.Body.ModerateMessageByUser
@@ -436,6 +437,7 @@ function Invoke-AddEditTransportRule {
436437
if ($null -ne $RedirectMessageTo -and $RedirectMessageTo.Count -gt 0) {
437438
$ruleParams.Add('RedirectMessageTo', $RedirectMessageTo)
438439
}
440+
if ($null -ne$RouteMessageOutboundConnector) {$ruleParams.Add('RouteMessageOutboundConnector', $RouteMessageOutboundConnector)}
439441
if ($null -ne $BlindCopyTo -and $BlindCopyTo.Count -gt 0) { $ruleParams.Add('BlindCopyTo', $BlindCopyTo) }
440442
if ($null -ne $CopyTo -and $CopyTo.Count -gt 0) { $ruleParams.Add('CopyTo', $CopyTo) }
441443
if ($null -ne $ModerateMessageByUser -and $ModerateMessageByUser.Count -gt 0) {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ function Invoke-CIPPOffboardingJob {
155155
}
156156
}
157157
@{
158-
Condition = { ![string]::IsNullOrEmpty($Options.OnedriveAccess) }
158+
Condition = { $Options.OnedriveAccess.Count -gt 0 }
159159
Cmdlet = 'Set-CIPPSharePointPerms'
160160
Parameters = @{
161161
tenantFilter = $TenantFilter
@@ -166,7 +166,7 @@ function Invoke-CIPPOffboardingJob {
166166
}
167167
}
168168
@{
169-
Condition = { ![string]::IsNullOrEmpty($Options.AccessNoAutomap) }
169+
Condition = { $Options.AccessNoAutomap.Count -gt 0 }
170170
Cmdlet = 'Set-CIPPMailboxAccess'
171171
Parameters = @{
172172
tenantFilter = $TenantFilter
@@ -179,7 +179,7 @@ function Invoke-CIPPOffboardingJob {
179179
}
180180
}
181181
@{
182-
Condition = { ![string]::IsNullOrEmpty($Options.AccessAutomap) }
182+
Condition = { $Options.AccessAutomap.Count -gt 0 }
183183
Cmdlet = 'Set-CIPPMailboxAccess'
184184
Parameters = @{
185185
tenantFilter = $TenantFilter

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,10 @@ function Invoke-EditUser {
104104
value = 'Set-CIPPUserLicense'
105105
}
106106
Parameters = [pscustomobject]@{
107-
UserId = $UserObj.id
108-
APIName = 'Sherweb License Assignment'
109-
AddLicenses = $licenses
107+
UserId = $UserObj.id
108+
APIName = 'Sherweb License Assignment'
109+
AddLicenses = $licenses
110+
UserPrincipalName = $UserPrincipalName
110111
}
111112
ScheduledTime = 0 #right now, which is in the next 15 minutes and should cover most cases.
112113
PostExecution = @{
@@ -124,12 +125,12 @@ function Invoke-EditUser {
124125
$Results.Add( 'Success. User license is already correct.' )
125126
} else {
126127
if ($UserObj.removeLicenses) {
127-
$licResults = Set-CIPPUserLicense -UserId $UserObj.id -TenantFilter $UserObj.tenantFilter -RemoveLicenses $CurrentLicenses.assignedLicenses.skuId -Headers $Headers -APIName $APIName
128+
$licResults = Set-CIPPUserLicense -UserPrincipalName $UserPrincipalName -UserId $UserObj.id -TenantFilter $UserObj.tenantFilter -RemoveLicenses $CurrentLicenses.assignedLicenses.skuId -Headers $Headers -APIName $APIName
128129
$Results.Add($licResults)
129130
} else {
130131
#Remove all objects from $CurrentLicenses.assignedLicenses.skuId that are in $licenses
131132
$RemoveLicenses = $CurrentLicenses.assignedLicenses.skuId | Where-Object { $_ -notin $licenses }
132-
$licResults = Set-CIPPUserLicense -UserId $UserObj.id -TenantFilter $UserObj.tenantFilter -RemoveLicenses $RemoveLicenses -AddLicenses $licenses -Headers $Headers -APIName $APIName
133+
$licResults = Set-CIPPUserLicense -UserPrincipalName $UserPrincipalName -UserId $UserObj.id -TenantFilter $UserObj.tenantFilter -RemoveLicenses $RemoveLicenses -AddLicenses $licenses -Headers $Headers -APIName $APIName
133134
$Results.Add($licResults)
134135
}
135136

0 commit comments

Comments
 (0)