Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d37f630
Support missing CIDR prefix and dedupe maxBits calc
JohnDuprey Feb 20, 2026
f674c92
Normalize default tenant groups JSON
JohnDuprey Feb 20, 2026
b6e89c2
Add log entry to Invoke-AddAlert
JohnDuprey Feb 20, 2026
5f240c1
fix quarantine return
JohnDuprey Feb 20, 2026
e9a01a9
Add cleanup rule and use OData timestamp filters
JohnDuprey Feb 21, 2026
80c4477
cleanup logging
JohnDuprey Feb 21, 2026
68e8562
Fix drift comparison issue for NotifyOutboundSpamRecipients
chris-dewey-1991 Feb 22, 2026
d06acf0
Fix self-service license handling and logging
chris-dewey-1991 Feb 22, 2026
518d970
Use Graph bulk API and improve group handling
JohnDuprey Feb 23, 2026
aa885f1
Standardize log severity and propagate headers
JohnDuprey Feb 23, 2026
fdc5462
Add Invoke-ListDBCache HTTP entrypoint
JohnDuprey Feb 23, 2026
641a37a
Update Invoke-CIPPStandardUserSubmissions.ps1
TecharyJames Feb 23, 2026
fa42620
Better tenant lookup
Feb 23, 2026
362cdac
removed for prettiness sake
KelvinTegelaar Feb 23, 2026
affc8b8
Merge branch 'dev' of https://github.com/KelvinTegelaar/CIPP-API into…
KelvinTegelaar Feb 23, 2026
6c38c16
Add device local admin standard function
Zacgoose Feb 24, 2026
d8318a8
Merge pull request #1843 from Zacgoose/local-admin
KelvinTegelaar Feb 24, 2026
6c57949
Merge pull request #1840 from chris-dewey-1991/StandardDisableSelfSer…
KelvinTegelaar Feb 24, 2026
4633840
Merge pull request #1842 from TecharyJames/User-Submissions--fix
KelvinTegelaar Feb 24, 2026
57cab3b
Merge pull request #1839 from chris-dewey-1991/StandardOutBoundSpamAlert
KelvinTegelaar Feb 24, 2026
7baeb92
fixes setup wizard to allow temproary headers.
KelvinTegelaar Feb 24, 2026
ee5b862
Merge branch 'dev' of https://github.com/KelvinTegelaar/CIPP-API into…
KelvinTegelaar Feb 24, 2026
c6d1a49
remove secret logging line for local dev
KelvinTegelaar Feb 24, 2026
68bbf04
fixed #5444
KelvinTegelaar Feb 24, 2026
7f1ed60
contact emails
KelvinTegelaar Feb 24, 2026
4ff8732
maximum kilobytes of internet(fixes remove empty array and returns it…
KelvinTegelaar Feb 24, 2026
733ca03
Add remediate/report to proper StandardTemplate object
JohnDuprey Feb 24, 2026
7ec3326
bump version
JohnDuprey Feb 24, 2026
8433bf1
Merge pull request #1844 from KelvinTegelaar/dev
JohnDuprey Feb 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Modules/CIPPCore/Public/Add-CIPPWin32LobAppContent.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ function Add-CIPPWin32LobAppContent {
if ($CommitStateReq.uploadState -like '*fail*') {
$errorMsg = "Commit failed. Upload state: $($CommitStateReq.uploadState)"
if ($Headers) {
Write-LogMessage -Headers $Headers -API $APIName -message $errorMsg -Sev 'Warning' -tenant $TenantFilter
Write-LogMessage -Headers $Headers -API $APIName -message $errorMsg -sev 'Warn' -tenant $TenantFilter
}
throw $errorMsg
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#Add rerun protection: This Monitor can only run once every hour.
$Rerun = Test-CIPPRerun -TenantFilter $TenantFilter -Type 'ExchangeMonitor' -API 'Get-CIPPAlertQuarantineReleaseRequests'
if ($Rerun) {
return $true
return
}
$HasLicense = Test-CIPPStandardLicense -StandardName 'QuarantineReleaseRequests' -TenantFilter $TenantFilter -RequiredCapabilities @(
'EXCHANGE_S_STANDARD',
Expand All @@ -25,7 +25,7 @@
)

if (-not $HasLicense) {
return $true
return
}

try {
Expand Down
4 changes: 2 additions & 2 deletions Modules/CIPPCore/Public/Authentication/Test-IpInRange.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ function Test-IpInRange {
$IP = [System.Net.IPAddress]::Parse($IPAddress)
$rangeParts = $Range -split '/'
$networkAddr = [System.Net.IPAddress]::Parse($rangeParts[0])
$prefix = [int]$rangeParts[1]
$maxBits = if ($networkAddr.AddressFamily -eq 'InterNetworkV6') { 128 } else { 32 }
$prefix = if ($rangeParts.Count -gt 1) { [int]$rangeParts[1] } else { $maxBits }

if ($networkAddr.AddressFamily -ne $IP.AddressFamily) {
return $false
}

$ipBig = ConvertIpToBigInteger $IP
$netBig = ConvertIpToBigInteger $networkAddr
$maxBits = if ($networkAddr.AddressFamily -eq 'InterNetworkV6') { 128 } else { 32 }
$shift = $maxBits - $prefix
$mask = [System.Numerics.BigInteger]::Pow(2, $shift) - [System.Numerics.BigInteger]::One
$invertedMask = [System.Numerics.BigInteger]::MinusOne -bxor $mask
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ function Push-CIPPOffboardingComplete {
$TaskInfo = $Item.Parameters.TaskInfo
$TenantFilter = $Item.Parameters.TenantFilter
$Username = $Item.Parameters.Username
$Headers = $Item.Parameters.Headers
$Results = $Item.Results # Results come from orchestrator, not Parameters

try {
Expand Down Expand Up @@ -102,19 +103,19 @@ function Push-CIPPOffboardingComplete {
TaskState = 'Completed'
}

Write-LogMessage -API 'Offboarding' -tenant $TenantFilter -message "Offboarding completed successfully for $Username" -sev Info
Write-LogMessage -API 'Offboarding' -tenant $TenantFilter -message "Offboarding completed successfully for $Username" -sev Info -headers $Headers

# Send post-execution alerts if configured
if ($TaskInfo.PostExecution -and $ProcessedResults) {
Send-CIPPScheduledTaskAlert -Results $ProcessedResults -TaskInfo $TaskInfo -TenantFilter $TenantFilter
}
}

Write-LogMessage -API 'Offboarding' -tenant $TenantFilter -message "Offboarding completed for $Username" -sev Info -headers $Headers
return "Offboarding completed for $Username"

} catch {
$ErrorMsg = "Failed to complete offboarding for $Username : $($_.Exception.Message)"
Write-LogMessage -API 'Offboarding' -tenant $TenantFilter -message $ErrorMsg -sev Error
Write-LogMessage -API 'Offboarding' -tenant $TenantFilter -message $ErrorMsg -sev Error -headers $Headers -LogData (Get-CippException -Exception $_)
throw $ErrorMsg
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function Push-CIPPOffboardingTask {
Write-Information "Executing offboarding cmdlet: $Cmdlet"

# Check if cmdlet exists
$CmdletInfo = Get-Command -Name $Cmdlet -ErrorAction SilentlyContinue
$CmdletInfo = Get-Command -Name $Cmdlet -Module CIPPCore -ErrorAction SilentlyContinue
if (-not $CmdletInfo) {
throw "Cmdlet $Cmdlet does not exist"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,12 @@ function Push-ExecScheduledCommand {
}
Write-LogMessage -API 'Scheduler_UserTasks' -tenant $Tenant -tenantid $TenantInfo.customerId -message "Failed to execute task $($task.Name): $errorMessage" -sev Error -LogData (Get-CippExceptionData -Exception $_.Exception)
}
Write-Information 'Sending task results to target. Updating the task state.'

# For orchestrator-based commands, skip post-execution alerts as they will be handled by the orchestrator's post-execution function
if ($Results -and $Item.Command -notin $OrchestratorBasedCommands) {
Write-Information "Sending task results to post execution target(s): $($Task.PostExecution -join ', ')."
Send-CIPPScheduledTaskAlert -Results $Results -TaskInfo $task -TenantFilter $Tenant -TaskType $TaskType
}
Write-Information 'Sent the results to the target. Updating the task state.'

try {
# For orchestrator-based commands, skip task state update as it will be handled by post-execution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function Invoke-ListScheduledItemDetails {
}
} catch {
# If JSON parsing fails, use raw value
Write-LogMessage -API $APIName -message "Error parsing Task.Results as JSON: $_" -Sev 'Warning'
Write-LogMessage -API $APIName -message "Error parsing Task.Results as JSON: $_" -sev 'Warn'
$ResultData = $Task.Results
}
} else {
Expand Down Expand Up @@ -155,7 +155,7 @@ function Invoke-ListScheduledItemDetails {
try {
$ParsedResults = $Result.Results | ConvertFrom-Json -ErrorAction Stop
} catch {
Write-LogMessage -API $APIName -message "Failed to parse result as JSON: $_" -Sev 'Warning'
Write-LogMessage -API $APIName -message "Failed to parse result as JSON: $_" -sev 'Warn'
# On failure, keep as string
$ParsedResults = $Result.Results
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ function Invoke-ExecApiClient {
$Body = @{ Results = "API client $ClientId not found or not a valid CIPP-API application" }
}
} catch {
Write-LogMessage -headers $Request.Headers -API 'ExecApiClient' -message "Failed to remove app registration for $ClientId" -Sev 'Warning'
Write-LogMessage -headers $Request.Headers -API 'ExecApiClient' -message "Failed to remove app registration for $ClientId" -sev 'Warn'
}
}
default {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function Invoke-ExecCreateDefaultGroups {
$Table = Get-CippTable -tablename 'TenantGroups'
$Results = [System.Collections.Generic.List[object]]::new()
$ExistingGroups = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'TenantGroup' and Type eq 'dynamic'"
$DefaultGroups = '[{"PartitionKey":"TenantGroup","RowKey":"369d985e-0fba-48f9-844f-9f793b10a12c","Description":"This group does not have a license for intune, nor a license for Entra ID Premium","Description@type":null,"DynamicRules":"[{\"property\":\"availableServicePlan\",\"operator\":\"notIn\",\"value\":[{\"label\":\"Microsoft Intune\",\"value\":\"INTUNE_A\",\"id\":\"c1ec4a95-1f05-45b3-a911-aa3fa01094f5\"}]},{\"property\":\"availableServicePlan\",\"operator\":\"notIn\",\"value\":[{\"label\":\"Microsoft Entra ID P1\",\"value\":\"AAD_PREMIUM\",\"id\":\"41781fb2-bc02-4b7c-bd55-b576c07bb09d\"}]}]","DynamicRules@type":null,"GroupType":"dynamic","GroupType@type":null,"RuleLogic":"and","RuleLogic@type":null,"Name":"Not Intune and Entra Premium Capable","Name@type":null},{"PartitionKey":"TenantGroup","RowKey":"4dbca08b-7dc5-4e0f-bc25-14a90c8e0941","Description":"This group has atleast one Business Premium License available","Description@type":null,"DynamicRules":"[{\"property\":\"availableLicense\",\"operator\":\"in\",\"value\":[{\"label\":\"Microsoft 365 Business Premium\",\"value\":\"SPB\"}]},{\"property\":\"availableLicense\",\"operator\":\"in\",\"value\":[{\"label\":\"Microsoft 365 Business Premium (no Teams)\",\"value\":\"Microsoft_365_ Business_ Premium_(no Teams)\"}]},{\"property\":\"availableLicense\",\"operator\":\"in\",\"value\":[{\"label\":\"Microsoft 365 Business Premium Donation\",\"value\":\"Microsoft_365_Business_Premium_Donation_(Non_Profit_Pricing)\"}]},{\"property\":\"availableLicense\",\"operator\":\"in\",\"value\":[{\"label\":\"Microsoft 365 Business Premium EEA (no Teams)\",\"value\":\"Office_365_w\/o_Teams_Bundle_Business_Premium\"}]}]","DynamicRules@type":null,"GroupType":"dynamic","GroupType@type":null,"RuleLogic":"or","RuleLogic@type":null,"Name":"Business Premium License available","Name@type":null},{"PartitionKey":"TenantGroup","RowKey":"703c0e69-84a8-4dcf-a1c2-4986d2ccc850","Description":"This group does have a license for Entra Premium but does not have a license for Intune","Description@type":null,"DynamicRules":"[{\"property\":\"availableServicePlan\",\"operator\":\"in\",\"value\":[{\"label\":\"Microsoft Entra ID P1\",\"value\":\"AAD_PREMIUM\",\"id\":\"41781fb2-bc02-4b7c-bd55-b576c07bb09d\"}]},{\"property\":\"availableServicePlan\",\"operator\":\"notIn\",\"value\":[{\"label\":\"Microsoft Intune\",\"value\":\"INTUNE_A\",\"id\":\"c1ec4a95-1f05-45b3-a911-aa3fa01094f5\"}]}]","DynamicRules@type":null,"GroupType":"dynamic","GroupType@type":null,"RuleLogic":"and","RuleLogic@type":null,"Name":"Entra Premium Capable, Not Intune Capable","Name@type":null},{"PartitionKey":"TenantGroup","RowKey":"c1dadbc0-f0b4-448c-a2e6-e1938ba102e0","Description":"This group has Intune and Entra ID Premium available","Description@type":null,"DynamicRules":"{\"property\":\"availableServicePlan\",\"operator\":\"in\",\"value\":[{\"label\":\"Microsoft Intune\",\"value\":\"INTUNE_A\"},{\"label\":\"Microsoft Entra ID P1\",\"value\":\"AAD_PREMIUM\"}]}","DynamicRules@type":null,"GroupType":"dynamic","GroupType@type":null,"RuleLogic":"and","RuleLogic@type":null,"Name":"Entra ID Premium and Intune Capable","Name@type":null}]' | ConvertFrom-Json
$DefaultGroups = '[{"PartitionKey":"TenantGroup","RowKey":"369d985e-0fba-48f9-844f-9f793b10a12c","Description":"This group does not have a license for intune, nor a license for Entra ID Premium","Description@type":null,"DynamicRules":"[{\"property\":\"availableServicePlan\",\"operator\":\"notIn\",\"value\":[{\"label\":\"Microsoft Intune\",\"value\":\"INTUNE_A\",\"id\":\"c1ec4a95-1f05-45b3-a911-aa3fa01094f5\"}]},{\"property\":\"availableServicePlan\",\"operator\":\"notIn\",\"value\":[{\"label\":\"Microsoft Entra ID P1\",\"value\":\"AAD_PREMIUM\",\"id\":\"41781fb2-bc02-4b7c-bd55-b576c07bb09d\"}]}]","DynamicRules@type":null,"GroupType":"dynamic","GroupType@type":null,"RuleLogic":"and","RuleLogic@type":null,"Name":"Not Intune and Entra Premium Capable","Name@type":null},{"PartitionKey":"TenantGroup","RowKey":"4dbca08b-7dc5-4e0f-bc25-14a90c8e0941","Description":"This group has atleast one Business Premium License available","DynamicRules":"{\"property\":\"availableLicense\",\"operator\":\"in\",\"value\":[{\"label\":\"Microsoft 365 Business Premium\",\"value\":\"SPB\",\"guid\":\"cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46\"},{\"label\":\"Microsoft 365 Business Premium (no Teams)\",\"value\":\"Microsoft_365_ Business_ Premium_(no Teams)\",\"guid\":\"00e1ec7b-e4a3-40d1-9441-b69b597ab222\"},{\"label\":\"Microsoft 365 Business Premium Donation\",\"value\":\"Microsoft_365_Business_Premium_Donation_(Non_Profit_Pricing)\",\"guid\":\"24c35284-d768-4e53-84d9-b7ae73dddf69\"},{\"label\":\"Microsoft 365 Business Premium EEA (no Teams)\",\"value\":\"Office_365_w/o_Teams_Bundle_Business_Premium\",\"guid\":\"a3f586b6-8cce-4d9b-99d6-55238397f77a\"}]}","GroupType":"dynamic","Name":"Business Premium License available","RuleLogic":"or"},{"PartitionKey":"TenantGroup","RowKey":"703c0e69-84a8-4dcf-a1c2-4986d2ccc850","Description":"This group does have a license for Entra Premium but does not have a license for Intune","Description@type":null,"DynamicRules":"[{\"property\":\"availableServicePlan\",\"operator\":\"in\",\"value\":[{\"label\":\"Microsoft Entra ID P1\",\"value\":\"AAD_PREMIUM\",\"id\":\"41781fb2-bc02-4b7c-bd55-b576c07bb09d\"}]},{\"property\":\"availableServicePlan\",\"operator\":\"notIn\",\"value\":[{\"label\":\"Microsoft Intune\",\"value\":\"INTUNE_A\",\"id\":\"c1ec4a95-1f05-45b3-a911-aa3fa01094f5\"}]}]","DynamicRules@type":null,"GroupType":"dynamic","GroupType@type":null,"RuleLogic":"and","RuleLogic@type":null,"Name":"Entra Premium Capable, Not Intune Capable","Name@type":null},{"PartitionKey":"TenantGroup","RowKey":"c1dadbc0-f0b4-448c-a2e6-e1938ba102e0","Description":"This group has Intune and Entra ID Premium available","Description@type":null,"DynamicRules":"{\"property\":\"availableServicePlan\",\"operator\":\"in\",\"value\":[{\"label\":\"Microsoft Intune\",\"value\":\"INTUNE_A\"},{\"label\":\"Microsoft Entra ID P1\",\"value\":\"AAD_PREMIUM\"}]}","DynamicRules@type":null,"GroupType":"dynamic","GroupType@type":null,"RuleLogic":"and","RuleLogic@type":null,"Name":"Entra ID Premium and Intune Capable","Name@type":null}]' | ConvertFrom-Json


foreach ($Group in $DefaultGroups) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ function Invoke-ExecAccessTest {
# Filter didn't work, try direct lookup by UPN (works if UPN is unique identifier)
$User = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$UPN" -tenantid $env:TenantID -NoAuthCheck $true
} catch {
Write-LogMessage -Headers $Headers -API $APIName -message "Could not find user $UPN in partner tenant: $($_.Exception.Message)" -Sev 'Warning'
Write-LogMessage -Headers $Headers -API $APIName -message "Could not find user $UPN in partner tenant: $($_.Exception.Message)" -sev 'Warn'
}

# If user not found, return error
Expand Down Expand Up @@ -212,7 +212,7 @@ function Invoke-ExecAccessTest {
}
}
} catch {
Write-LogMessage -Headers $Headers -API $APIName -message "Could not get user group memberships: $($_.Exception.Message)" -Sev 'Warning'
Write-LogMessage -Headers $Headers -API $APIName -message "Could not get user group memberships: $($_.Exception.Message)" -sev 'Warn'
}

# ============================================================================
Expand Down Expand Up @@ -296,7 +296,7 @@ function Invoke-ExecAccessTest {
})
}
} catch {
Write-LogMessage -Headers $Headers -API $APIName -message "Could not get access assignments for relationship ${RelationshipName}: $($_.Exception.Message)" -Sev 'Warning'
Write-LogMessage -Headers $Headers -API $APIName -message "Could not get access assignments for relationship ${RelationshipName}: $($_.Exception.Message)" -sev 'Warn'
}
}

Expand Down Expand Up @@ -346,7 +346,7 @@ function Invoke-ExecAccessTest {

Write-LogMessage -Headers $Headers -API $APIName -message "Fetched $($AllGroups.Count) total groups, $($GroupLookup.Count) in lookup" -Sev 'Debug'
} catch {
Write-LogMessage -Headers $Headers -API $APIName -message "Could not fetch all groups: $($_.Exception.Message). Will use fallback for missing groups." -Sev 'Warning'
Write-LogMessage -Headers $Headers -API $APIName -message "Could not fetch all groups: $($_.Exception.Message). Will use fallback for missing groups." -sev 'Warn'
}

# ========================================================================
Expand Down Expand Up @@ -387,12 +387,12 @@ function Invoke-ExecAccessTest {
$GroupId = $Assignment.value.accessContainer.accessContainerId
$Assignment = $Assignment.value
} else {
Write-LogMessage -Headers $Headers -API $APIName -message "Access assignment missing accessContainer: $($Assignment | ConvertTo-Json -Compress)" -Sev 'Warning'
Write-LogMessage -Headers $Headers -API $APIName -message "Access assignment missing accessContainer: $($Assignment | ConvertTo-Json -Compress)" -sev 'Warn'
continue
}

if ([string]::IsNullOrWhiteSpace($GroupId)) {
Write-LogMessage -Headers $Headers -API $APIName -message "Access assignment has empty accessContainerId: $($Assignment | ConvertTo-Json -Compress)" -Sev 'Warning'
Write-LogMessage -Headers $Headers -API $APIName -message "Access assignment has empty accessContainerId: $($Assignment | ConvertTo-Json -Compress)" -sev 'Warn'
continue
}

Expand All @@ -405,7 +405,7 @@ function Invoke-ExecAccessTest {
}

if (-not $Roles -or $Roles.Count -eq 0) {
Write-LogMessage -Headers $Headers -API $APIName -message "Access assignment for group $GroupId has no roles assigned" -Sev 'Warning'
Write-LogMessage -Headers $Headers -API $APIName -message "Access assignment for group $GroupId has no roles assigned" -sev 'Warn'
$Roles = @()
}

Expand All @@ -420,7 +420,7 @@ function Invoke-ExecAccessTest {
id = $GroupId
displayName = "Unknown Group ($GroupId)"
}
Write-LogMessage -Headers $Headers -API $APIName -message "Group $GroupId not found in lookup, using fallback" -Sev 'Warning'
Write-LogMessage -Headers $Headers -API $APIName -message "Group $GroupId not found in lookup, using fallback" -sev 'Warn'
}

# Process the assignment even if group lookup failed - we still have the group ID and roles
Expand Down Expand Up @@ -585,12 +585,12 @@ function Invoke-ExecAccessTest {
} elseif ($Role -is [string]) {
$RoleId = $Role
} else {
Write-LogMessage -Headers $Headers -API $APIName -message "Role object missing roleDefinitionId: $($Role | ConvertTo-Json -Compress)" -Sev 'Warning'
Write-LogMessage -Headers $Headers -API $APIName -message "Role object missing roleDefinitionId: $($Role | ConvertTo-Json -Compress)" -sev 'Warn'
continue
}

if ([string]::IsNullOrWhiteSpace($RoleId)) {
Write-LogMessage -Headers $Headers -API $APIName -message "Role has empty roleDefinitionId for group $GroupId" -Sev 'Warning'
Write-LogMessage -Headers $Headers -API $APIName -message "Role has empty roleDefinitionId for group $GroupId" -sev 'Warn'
continue
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ function Invoke-ListCustomVariables {
}
}
} catch {
Write-LogMessage -API $APIName -message "Could not retrieve tenant-specific variables for $TenantFilter : $($_.Exception.Message)" -Sev 'Warning'
Write-LogMessage -API $APIName -message "Could not retrieve tenant-specific variables for $TenantFilter : $($_.Exception.Message)" -sev 'Warn'
}
}

Expand Down
Loading