Skip to content

Commit da0d221

Browse files
authored
Merge pull request #658 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 338b49a + a02c796 commit da0d221

File tree

186 files changed

+2176
-21
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

186 files changed

+2176
-21
lines changed

Modules/CIPPCore/Public/Add-CIPPDbItem.ps1

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,7 @@ function Add-CIPPDbItem {
4242

4343
try {
4444
$Table = Get-CippTable -tablename 'CippReportingDB'
45-
#Get the existing type entries and nuke them. This ensures we don't have stale data.
46-
$Filter = "PartitionKey eq '{0}' and RowKey ge '{1}-' and RowKey lt '{1}0'" -f $TenantFilter, $Type
47-
$ExistingEntities = Get-CIPPAzDataTableEntity @Table -Filter $Filter
48-
if ($ExistingEntities) {
49-
Remove-AzDataTableEntity @Table -Entity $ExistingEntities -Force | Out-Null
50-
}
45+
5146

5247
if ($Count) {
5348
$Entity = @{
@@ -59,6 +54,12 @@ function Add-CIPPDbItem {
5954
Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force | Out-Null
6055

6156
} else {
57+
#Get the existing type entries and nuke them. This ensures we don't have stale data.
58+
$Filter = "PartitionKey eq '{0}' and RowKey ge '{1}-' and RowKey lt '{1}0'" -f $TenantFilter, $Type
59+
$ExistingEntities = Get-CIPPAzDataTableEntity @Table -Filter $Filter
60+
if ($ExistingEntities) {
61+
Remove-AzDataTableEntity @Table -Entity $ExistingEntities -Force | Out-Null
62+
}
6263
$Entities = foreach ($Item in $Data) {
6364
$ItemId = $Item.id ? $Item.id : $item.skuId
6465
@{

Modules/CIPPCore/Public/Add-CippTestResult.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function Add-CippTestResult {
4747
[Parameter(Mandatory = $true)]
4848
[string]$TestId,
4949

50-
[Parameter(Mandatory = $true)]
50+
[Parameter(Mandatory = $false)]
5151
[string]$testType = 'identity',
5252

5353
[Parameter(Mandatory = $true)]
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
function Get-CIPPAlertIntunePolicyConflicts {
2+
<#
3+
.FUNCTIONALITY
4+
Entrypoint
5+
#>
6+
[CmdletBinding()]
7+
param (
8+
[Parameter(Mandatory = $false)]
9+
[Alias('input')]
10+
$InputValue,
11+
$TenantFilter
12+
)
13+
14+
# Normalize JSON/string input to object when possible
15+
if ($InputValue -is [string]) {
16+
try {
17+
if ($InputValue.Trim().StartsWith('{')) {
18+
$InputValue = $InputValue | ConvertFrom-Json -ErrorAction Stop
19+
}
20+
} catch {
21+
# Leave as-is if parsing fails
22+
}
23+
}
24+
25+
$Config = [ordered]@{
26+
AlertEachIssue = $false # align with AlertEachAdmin convention (false = aggregated)
27+
IncludePolicies = $true
28+
IncludeApplications = $true
29+
AlertConflicts = $true
30+
AlertErrors = $true
31+
}
32+
33+
if ($InputValue -is [hashtable] -or $InputValue -is [pscustomobject]) {
34+
# Primary key follows AlertEach* convention; legacy Aggregate supported (true == aggregated)
35+
if ($null -ne $InputValue.AlertEachIssue) { $Config.AlertEachIssue = [bool]$InputValue.AlertEachIssue }
36+
if ($null -ne $InputValue.Aggregate) { $Config.AlertEachIssue = -not [bool]$InputValue.Aggregate }
37+
38+
$Config.IncludePolicies = if ($null -ne $InputValue.IncludePolicies) { [bool]$InputValue.IncludePolicies } else { $Config.IncludePolicies }
39+
$Config.IncludeApplications = if ($null -ne $InputValue.IncludeApplications) { [bool]$InputValue.IncludeApplications } else { $Config.IncludeApplications }
40+
$Config.AlertConflicts = if ($null -ne $InputValue.AlertConflicts) { [bool]$InputValue.AlertConflicts } else { $Config.AlertConflicts }
41+
$Config.AlertErrors = if ($null -ne $InputValue.AlertErrors) { [bool]$InputValue.AlertErrors } else { $Config.AlertErrors }
42+
} elseif ($InputValue -is [bool]) {
43+
# Back-compat for boolean toggle used as Aggregate previously
44+
$Config.AlertEachIssue = -not [bool]$InputValue
45+
}
46+
47+
if (-not $Config.IncludePolicies -and -not $Config.IncludeApplications) {
48+
return
49+
}
50+
51+
$AlertableStatuses = @()
52+
if ($Config.AlertErrors) { $AlertableStatuses += 'error', 'failed' }
53+
if ($Config.AlertConflicts) { $AlertableStatuses += 'conflict' }
54+
55+
if (-not $AlertableStatuses) {
56+
return
57+
}
58+
59+
$HasLicense = Test-CIPPStandardLicense -StandardName 'IntunePolicyStatus' -TenantFilter $TenantFilter -RequiredCapabilities @(
60+
'INTUNE_A',
61+
'MDM_Services',
62+
'EMS',
63+
'SCCM',
64+
'MICROSOFTINTUNEPLAN1'
65+
)
66+
67+
if (-not $HasLicense) {
68+
return
69+
}
70+
71+
$Issues = @()
72+
73+
if ($Config.IncludePolicies) {
74+
try {
75+
$ManagedDevices = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$select=id,deviceName,userPrincipalName&`$expand=deviceConfigurationStates(`$select=displayName,state,settingStates)" -tenantid $TenantFilter
76+
77+
foreach ($Device in $ManagedDevices) {
78+
$PolicyStates = $Device.deviceConfigurationStates | Where-Object { $_.state -and ($AlertableStatuses -contains $_.state) }
79+
foreach ($State in $PolicyStates) {
80+
$Issues += [PSCustomObject]@{
81+
Message = "Policy '$($State.displayName)' is $($State.state) on device '$($Device.deviceName)' for $($Device.userPrincipalName)."
82+
Tenant = $TenantFilter
83+
Type = 'Policy'
84+
PolicyName = $State.displayName
85+
IssueStatus = $State.state
86+
DeviceName = $Device.deviceName
87+
UserPrincipalName = $Device.userPrincipalName
88+
DeviceId = $Device.id
89+
}
90+
}
91+
}
92+
} catch {
93+
Write-AlertMessage -tenant $TenantFilter -message "Failed to query Intune policy states: $(Get-NormalizedError -message $_.Exception.Message)"
94+
}
95+
}
96+
97+
if ($Config.IncludeApplications) {
98+
try {
99+
$Applications = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?`$select=id,displayName&`$expand=deviceStatuses(`$select=installState,deviceName,userPrincipalName,deviceId)" -tenantid $TenantFilter
100+
101+
foreach ($App in $Applications) {
102+
$BadStatuses = $App.deviceStatuses | Where-Object {
103+
$_.installState -and ($AlertableStatuses -contains $_.installState.ToLowerInvariant())
104+
}
105+
106+
foreach ($Status in $BadStatuses) {
107+
$Issues += [PSCustomObject]@{
108+
Message = "App '$($App.displayName)' install is $($Status.installState) on device '$($Status.deviceName)' for $($Status.userPrincipalName)."
109+
Tenant = $TenantFilter
110+
Type = 'Application'
111+
AppName = $App.displayName
112+
IssueStatus = $Status.installState
113+
DeviceName = $Status.deviceName
114+
UserPrincipalName = $Status.userPrincipalName
115+
DeviceId = $Status.deviceId
116+
}
117+
}
118+
}
119+
} catch {
120+
Write-AlertMessage -tenant $TenantFilter -message "Failed to query Intune application states: $(Get-NormalizedError -message $_.Exception.Message)"
121+
}
122+
}
123+
124+
if (-not $Issues) {
125+
return
126+
}
127+
128+
if (-not $Config.AlertEachIssue) {
129+
$PolicyCount = ($Issues | Where-Object { $_.Type -eq 'Policy' }).Count
130+
$AppCount = ($Issues | Where-Object { $_.Type -eq 'Application' }).Count
131+
132+
$AlertData = @([PSCustomObject]@{
133+
Message = "Found $PolicyCount policy issues and $AppCount application issues in Intune."
134+
Tenant = $TenantFilter
135+
PolicyIssues = $PolicyCount
136+
AppIssues = $AppCount
137+
Issues = $Issues
138+
})
139+
} else {
140+
$AlertData = $Issues
141+
}
142+
143+
Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData
144+
}

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,13 @@ function Get-CIPPAlertOneDriveQuota {
3333
if ($UsagePercent -gt $InputValue) {
3434
$GBLeft = [math]::Round(($_.storageAllocatedInBytes - $_.storageUsedInBytes) / 1GB)
3535
[PSCustomObject]@{
36-
Details = @{
37-
Message = "$($_.ownerPrincipalName): OneDrive is $UsagePercent% full. OneDrive has $($GBLeft)GB storage left"
38-
Owner = $_.ownerPrincipalName
39-
UsagePercent = $UsagePercent
40-
GBLeft = $GBLeft
41-
StorageUsedInBytes = $_.storageUsedInBytes
42-
StorageAllocatedInBytes = $_.storageAllocatedInBytes
43-
Tenant = $TenantFilter
44-
}
36+
Message = "$($_.ownerPrincipalName): OneDrive is $UsagePercent% full. OneDrive has $($GBLeft)GB storage left"
37+
Owner = $_.ownerPrincipalName
38+
UsagePercent = $UsagePercent
39+
GBLeft = $GBLeft
40+
StorageUsedInBytes = $_.storageUsedInBytes
41+
StorageAllocatedInBytes = $_.storageAllocatedInBytes
42+
Tenant = $TenantFilter
4543
}
4644
}
4745

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Invoke-ListTests.ps1

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,23 @@ function Invoke-ListTests {
9393
$IdentityResults = $TestResultsData.TestResults | Where-Object { $_.TestType -eq 'Identity' }
9494
$DeviceResults = $TestResultsData.TestResults | Where-Object { $_.TestType -eq 'Devices' }
9595

96+
# Add descriptions from markdown files to each test result
97+
foreach ($TestResult in $TestResultsData.TestResults) {
98+
$MdFile = Get-ChildItem -Path 'Modules\CIPPCore\Public\Tests' -Filter "*$($TestResult.RowKey).md" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
99+
if ($MdFile) {
100+
try {
101+
$MdContent = Get-Content $MdFile.FullName -Raw -ErrorAction SilentlyContinue
102+
if ($MdContent) {
103+
$Description = ($MdContent -split '<!--- Results --->')[0].Trim()
104+
$Description = ($Description -split '%TestResult%')[0].Trim()
105+
$TestResult | Add-Member -NotePropertyName 'Description' -NotePropertyValue $Description -Force
106+
}
107+
} catch {
108+
#Test
109+
}
110+
}
111+
}
112+
96113
$TestCounts = @{
97114
Identity = @{
98115
Passed = @($IdentityResults | Where-Object { $_.Status -eq 'Passed' }).Count
@@ -114,11 +131,11 @@ function Invoke-ListTests {
114131

115132
$SecureScoreData = New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'SecureScore'
116133
if ($SecureScoreData) {
117-
$TestResultsData | Add-Member -NotePropertyName 'SecureScore' -NotePropertyValue $SecureScoreData -Force
134+
$TestResultsData | Add-Member -NotePropertyName 'SecureScore' -NotePropertyValue @($SecureScoreData) -Force
118135
}
119136
$MFAStateData = New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'MFAState'
120137
if ($MFAStateData) {
121-
$TestResultsData | Add-Member -NotePropertyName 'MFAState' -NotePropertyValue $MFAStateData -Force
138+
$TestResultsData | Add-Member -NotePropertyName 'MFAState' -NotePropertyValue @($MFAStateData) -Force
122139
}
123140

124141
$LicenseData = New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'LicenseOverview'

Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,14 @@ function Get-CIPPTenantAlignment {
125125
$TenantValues.Add($filterItem.value)
126126
}
127127
}
128-
129-
if ($TenantValues -contains 'AllTenants') {
128+
`
129+
if ($TenantValues -contains 'AllTenants') {
130130
$AppliestoAllTenants = $true
131131
} elseif ($TenantValues.Count -gt 0) {
132132
$TemplateAssignedTenants = @($TenantValues)
133133
} else {
134-
$TemplateAssignedTenants = @()
134+
# Filter was specified but resolved to no tenants (empty group) - skip this template
135+
continue
135136
}
136137
} else {
137138
$AppliestoAllTenants = $true
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Without owners, enterprise applications become orphaned assets that threat actors can exploit through credential harvesting and privilege escalation techniques, as these applications often retain elevated permissions and access to sensitive resources while lacking proper oversight and security governance. The elevation of privilege to owners can raise a security concern in some cases depending on the application's permissions, but more critically, applications without owner create a blind spot in security monitoring where threat actors can establish persistence by leveraging existing application permissions to access data or create backdoor accounts without triggering ownership-based detection mechanisms. When applications lack owners, security teams cannot effectively conduct application lifecycle management, leaving applications with potentially excessive permissions, outdated configurations, or compromised credentials that threat actors can discover through enumeration techniques and exploit to move laterally within the environment. The absence of ownership also prevents proper access reviews and permission audits, allowing threat actors to maintain long-term access through applications that should have been decommissioned or had their permissions reduced, ultimately providing persistent access vectors that can be leveraged for data exfiltration or further compromise of the environment.
2+
3+
4+
**Remediation action**
5+
6+
- [Assign owners to the application](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/assign-app-owners?pivots=portal)
7+
8+
<!--- Results --->
9+
%TestResult%
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
If policies for Windows Firewall aren't configured and assigned, threat actors can exploit unprotected endpoints to gain unauthorized access, move laterally, and escalate privileges within the environment. Without enforced firewall rules, attackers can bypass network segmentation, exfiltrate data, or deploy malware, increasing the risk of widespread compromise.
2+
3+
Enforcing Windows Firewall policies ensures consistent application of inbound and outbound traffic controls, reducing exposure to unauthorized access and supporting Zero Trust through network segmentation and device-level protection.
4+
5+
**Remediation action**
6+
7+
Configure and assign firewall policies for Windows in Intune to block unauthorized traffic and enforce consistent network protections across all managed devices:
8+
9+
- [Configure firewall policies for Windows devices](https://learn.microsoft.com/intune/intune-service/protect/endpoint-security-firewall-policy?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci). Intune uses two complementary profiles to manage firewall settings:
10+
- **Windows Firewall** - Use this profile to configure overall firewall behavior based on network type.
11+
- **Windows Firewall rules** - Use this profile to define traffic rules for apps, ports, or IPs, tailored to specific groups or workloads. This Intune profile also supports use of [reusable settings groups](https://learn.microsoft.com/intune/intune-service/protect/endpoint-security-firewall-policy?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci#add-reusable-settings-groups-to-profiles-for-firewall-rules) to help simplify management of common settings you use for different profile instances.
12+
- [Assign policies in Intune](https://learn.microsoft.com/intune/intune-service/configuration/device-profile-assign?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci#assign-a-policy-to-users-or-groups)
13+
14+
For more information, see:
15+
- [Available Windows Firewall settings](https://learn.microsoft.com/intune/intune-service/protect/endpoint-security-firewall-profile-settings?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci#windows-firewall-profile)
16+
<!--- Results --->
17+
%TestResult%
18+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
If compliance policies for Windows devices aren't configured and assigned, threat actors can exploit unmanaged or noncompliant endpoints to gain unauthorized access to corporate resources, bypass security controls, and persist within the environment. Without enforced compliance, devices can lack critical security configurations like BitLocker encryption, password requirements, firewall settings, and OS version controls. These gaps increase the risk of data leakage, privilege escalation, and lateral movement. Inconsistent device compliance weakens the organization’s security posture and makes it harder to detect and remediate threats before significant damage occurs.
2+
3+
Enforcing compliance policies ensures Windows devices meet core security requirements and supports Zero Trust by validating device health and reducing exposure to misconfigured endpoints.
4+
5+
**Remediation action**
6+
7+
Create and assign Intune compliance policies to Windows devices to enforce organizational standards for secure access and management:
8+
- [Create and assign Intune compliance policies](https://learn.microsoft.com/intune/intune-service/protect/create-compliance-policy?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci#create-the-policy)
9+
- [Review the Windows compliance settings you can manage with Intune](https://learn.microsoft.com/intune/intune-service/protect/compliance-policy-create-windows?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci)<!--- Results --->
10+
%TestResult%
11+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
If compliance policies for macOS devices aren't configured and assigned, threat actors can exploit unmanaged or noncompliant endpoints to gain unauthorized access to corporate resources, bypass security controls, and persist within the environment. Without enforced compliance, macOS devices can lack critical security configurations like data storage encryption, password requirements, and OS version controls. These gaps increase the risk of data leakage, privilege escalation, and lateral movement. Inconsistent device compliance weakens the organization’s security posture and makes it harder to detect and remediate threats before significant damage occurs.
2+
3+
Enforcing compliance policies ensures macOS devices meet core security requirements and supports Zero Trust by validating device health and reducing exposure to misconfigured endpoints.
4+
5+
**Remediation actions**
6+
7+
Create and assign Intune compliance policies to macOS devices to enforce organizational standards for secure access and management:
8+
- [Create and assign Intune compliance policies](https://learn.microsoft.com/intune/intune-service/protect/create-compliance-policy?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci#create-the-policy)
9+
- [Review the macOS compliance settings you can manage with Intune](https://learn.microsoft.com/intune/intune-service/protect/compliance-policy-create-mac-os?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci)
10+
<!--- Results --->
11+
%TestResult%
12+

0 commit comments

Comments
 (0)