Skip to content

Commit e9a0756

Browse files
authored
Merge branch 'KelvinTegelaar:master' into master
2 parents 2572be5 + a9d5c4e commit e9a0756

82 files changed

Lines changed: 2854 additions & 826 deletions

File tree

Some content is hidden

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

Config/CIPPTimers.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@
241241
"Id": "5e8a9b4c-2d6f-4a3e-b7c1-9d0e5f3a8b2c",
242242
"Command": "Start-IntuneReportExportOrchestrator",
243243
"Description": "Submit Intune report-export jobs ahead of nightly DB cache run",
244-
"Cron": "0 0 2 * * *",
244+
"Cron": "0 0 1 * * *",
245245
"Priority": 22,
246246
"RunOnProcessor": true,
247247
"TZOffset": true,

Config/ConversionTable.csv

Lines changed: 123 additions & 0 deletions
Large diffs are not rendered by default.

Config/FeatureFlags.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,20 @@
8181
],
8282
"Pages": [],
8383
"Hidden": false
84+
},
85+
{
86+
"Id": "AppInsights",
87+
"Name": "App Insights",
88+
"Description": "App Insights page not used in NG",
89+
"Enabled": true,
90+
"AllowUserToggle": false,
91+
"Timers": [],
92+
"Endpoints": [
93+
"ExecAppInsightsQuery"
94+
],
95+
"Pages": [
96+
"/cipp/advanced/diagnostics"
97+
],
98+
"Hidden": true
8499
}
85-
]
100+
]
File renamed without changes.

Config/standards.json

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,8 @@
378378
"cat": "Global Standards",
379379
"tag": ["CIS M365 7.0.0 (1.3.6)", "CustomerLockBoxEnabled"],
380380
"appliesToTest": ["CIS_1_3_6"],
381-
"helpText": "**Requires Entra ID P2.** Enables Customer Lockbox that offers an approval process for Microsoft support to access organization data",
382-
"docsDescription": "**Requires Entra ID P2.** Customer Lockbox ensures that Microsoft can't access your content to do service operations without your explicit approval. Customer Lockbox ensures only authorized requests allow access to your organizations data.",
381+
"helpText": "**Requires CustomerLockbox (E5, E7, A5, Purview Addon for BP, EDU or FL)** Enables Customer Lockbox that offers an approval process for Microsoft support to access organization data",
382+
"docsDescription": "**Requires CustomerLockbox (E5, E7, A5, Purview Addon for BP, EDU or FL)** Customer Lockbox ensures that Microsoft can't access your content to do service operations without your explicit approval. Customer Lockbox ensures only authorized requests allow access to your organizations data.",
383383
"executiveText": "Requires explicit organizational approval before Microsoft support staff can access company data for service operations. This provides an additional layer of data protection and ensures the organization maintains control over who can access sensitive business information, even during technical support scenarios.",
384384
"addedComponent": [],
385385
"label": "Enable Customer Lockbox",
@@ -4157,12 +4157,10 @@
41574157
"defaultValue": false
41584158
},
41594159
{
4160-
"type": "autoComplete",
4161-
"multiple": true,
4162-
"creatable": true,
4160+
"type": "LanguageCodeMultiSelect",
41634161
"required": false,
41644162
"name": "standards.SpamFilterPolicy.LanguageBlockList",
4165-
"label": "Languages to block (uppercase ISO 639-1 two-letter)",
4163+
"label": "Languages to block (ISO 639-1 two-letter)",
41664164
"condition": {
41674165
"field": "standards.SpamFilterPolicy.EnableLanguageBlockList",
41684166
"compareType": "is",
@@ -4176,12 +4174,10 @@
41764174
"defaultValue": false
41774175
},
41784176
{
4179-
"type": "autoComplete",
4180-
"multiple": true,
4181-
"creatable": true,
4177+
"type": "CountryCodeMultiSelect",
41824178
"required": false,
41834179
"name": "standards.SpamFilterPolicy.RegionBlockList",
4184-
"label": "Regions to block (uppercase ISO 3166-1 two-letter)",
4180+
"label": "Regions to block (ISO 3166-1 two-letter)",
41854181
"condition": {
41864182
"field": "standards.SpamFilterPolicy.EnableRegionBlockList",
41874183
"compareType": "is",
@@ -5865,7 +5861,7 @@
58655861
{
58665862
"type": "switch",
58675863
"name": "standards.TeamsFederationConfiguration.AllowTeamsConsumer",
5868-
"label": "Allow users to communicate with other organizations"
5864+
"label": "Allow users to communicate with consumer Teams accounts"
58695865
},
58705866
{
58715867
"type": "autoComplete",
@@ -7255,6 +7251,12 @@
72557251
"label": "Block Android if partner data unavailable",
72567252
"defaultValue": false
72577253
},
7254+
{
7255+
"type": "switch",
7256+
"name": "standards.DefenderCompliancePolicy.grantMobileThreatDefensePartnerRole",
7257+
"label": "Grant MTD role to MDE on enrolled Android COBO/COPE devices",
7258+
"defaultValue": false
7259+
},
72587260
{
72597261
"type": "switch",
72607262
"name": "standards.DefenderCompliancePolicy.ConnectIos",
@@ -7264,13 +7266,19 @@
72647266
{
72657267
"type": "switch",
72667268
"name": "standards.DefenderCompliancePolicy.ConnectIosCompliance",
7267-
"label": "Connect iOS 13.0+ (App-based MAM)",
7269+
"label": "Connect iOS/iPadOS devices for app protection policy evaluation (MAM)",
72687270
"defaultValue": false
72697271
},
72707272
{
72717273
"type": "switch",
72727274
"name": "standards.DefenderCompliancePolicy.appSync",
7273-
"label": "Enable App Sync for iOS",
7275+
"label": "Enable App Sync (sending application inventory) for iOS/iPadOS devices",
7276+
"defaultValue": false
7277+
},
7278+
{
7279+
"type": "switch",
7280+
"name": "standards.DefenderCompliancePolicy.allowPartnerToCollectIosPersonalApplicationMetadata",
7281+
"label": "Send full application inventory data on personally-owned iOS/iPadOS devices",
72747282
"defaultValue": false
72757283
},
72767284
{
@@ -7282,13 +7290,13 @@
72827290
{
72837291
"type": "switch",
72847292
"name": "standards.DefenderCompliancePolicy.allowPartnerToCollectIosCertificateMetadata",
7285-
"label": "Collect certificate metadata from iOS",
7293+
"label": "Enable Certificate Sync for iOS/iPadOS devices",
72867294
"defaultValue": false
72877295
},
72887296
{
72897297
"type": "switch",
72907298
"name": "standards.DefenderCompliancePolicy.allowPartnerToCollectIosPersonalCertificateMetadata",
7291-
"label": "Collect personal certificate metadata from iOS",
7299+
"label": "Send full certificate inventory data on personally-owned iOS/iPadOS devices",
72927300
"defaultValue": false
72937301
},
72947302
{

Modules/CIPPActivityTriggers/Public/Entrypoints/Activity Triggers/Maintenance/Push-TableCleanupTask.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function Push-TableCleanupTask {
1616
if ($Table) {
1717
Write-Information "Deleting table $($Table.Context.TableName)"
1818
try {
19-
Remove-AzDataTable -Context $Table.Context -Force
19+
Remove-AzDataTable -Context $Table.Context
2020
} catch {
2121
#Write-LogMessage -API 'TableCleanup' -message "Failed to delete table $($Table.Context.TableName)" -sev Error -LogData (Get-CippException -Exception $_)
2222
}

Modules/CIPPActivityTriggers/Public/Entrypoints/Activity Triggers/Push-IntuneReportExportSubmit.ps1

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ function Push-IntuneReportExportSubmit {
2525
'UserId', 'UserName', 'EmailAddress'
2626
)
2727
}
28+
'AppInstallStatusAggregate' {
29+
@(
30+
'ApplicationId', 'DisplayName', 'Publisher', 'Platform', 'AppVersion', 'AppPlatform',
31+
'InstalledDeviceCount', 'FailedDeviceCount', 'FailedUserCount',
32+
'PendingInstallDeviceCount', 'NotInstalledDeviceCount', 'FailedDevicePercentage'
33+
)
34+
}
2835
default { throw "Unknown Intune report '$ReportName'" }
2936
}
3037

Modules/CIPPAlerts/Public/Alerts/Get-CIPPAlertIntunePolicyConflicts.ps1

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ function Get-CIPPAlertIntunePolicyConflicts {
4949
}
5050

5151
$AlertableStatuses = @(
52-
if ($Config.AlertErrors) { 'error'; 'failed' }
52+
if ($Config.AlertErrors) { 'error' }
5353
if ($Config.AlertConflicts) { 'conflict' }
5454
)
5555

56-
if (-not $AlertableStatuses) {
56+
if (-not $AlertableStatuses -and -not ($Config.IncludeApplications -and $Config.AlertErrors)) {
5757
return
5858
}
5959

@@ -64,56 +64,66 @@ function Get-CIPPAlertIntunePolicyConflicts {
6464

6565
$Issues = [System.Collections.Generic.List[object]]::new()
6666

67-
if ($Config.IncludePolicies) {
68-
try {
69-
$ManagedDevices = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$select=id,deviceName,userPrincipalName&`$expand=deviceConfigurationStates(`$select=displayName,state,settingStates)" -tenantid $TenantFilter
70-
71-
foreach ($Device in $ManagedDevices) {
72-
$PolicyStates = $Device.deviceConfigurationStates | Where-Object { $_.state -and ($AlertableStatuses -contains $_.state) }
73-
foreach ($State in $PolicyStates) {
74-
$Issues.Add([PSCustomObject]@{
75-
Message = "Policy '$($State.displayName)' is $($State.state) on device '$($Device.deviceName)' for $($Device.userPrincipalName)."
76-
Tenant = $TenantFilter
77-
Type = 'Policy'
78-
PolicyName = $State.displayName
79-
IssueStatus = $State.state
80-
DeviceName = $Device.deviceName
81-
UserPrincipalName = $Device.userPrincipalName
82-
DeviceId = $Device.id
83-
})
67+
if ($Config.IncludePolicies -and $AlertableStatuses) {
68+
$PolicySources = @(
69+
@{ Type = 'IntuneDeviceCompliancePolicies'; Kind = 'Compliance' }
70+
@{ Type = 'IntuneDeviceConfigurations'; Kind = 'Configuration' }
71+
)
72+
73+
foreach ($Source in $PolicySources) {
74+
try {
75+
$PolicyItems = Get-CIPPDbItem -TenantFilter $TenantFilter -Type $Source.Type | Where-Object { $_.RowKey -notlike '*-Count' }
76+
foreach ($PolicyItem in $PolicyItems) {
77+
$Policy = try { $PolicyItem.Data | ConvertFrom-Json -ErrorAction Stop } catch { $null }
78+
if (-not $Policy.id) { continue }
79+
80+
$StatusItems = Get-CIPPDbItem -TenantFilter $TenantFilter -Type "$($Source.Type)_$($Policy.id)" | Where-Object { $_.RowKey -notlike '*-Count' }
81+
foreach ($StatusItem in $StatusItems) {
82+
$State = try { $StatusItem.Data | ConvertFrom-Json -ErrorAction Stop } catch { $null }
83+
if (-not $State.status -or ($AlertableStatuses -notcontains $State.status.ToLowerInvariant())) { continue }
84+
85+
$Issues.Add([PSCustomObject]@{
86+
Message = "$($Source.Kind) policy '$($Policy.displayName)' is $($State.status) on device '$($State.deviceDisplayName)' for $($State.userPrincipalName)."
87+
Tenant = $TenantFilter
88+
Type = 'Policy'
89+
PolicyType = $Source.Kind
90+
PolicyName = $Policy.displayName
91+
IssueStatus = $State.status
92+
DeviceName = $State.deviceDisplayName
93+
UserPrincipalName = $State.userPrincipalName
94+
DeviceId = $State.id
95+
})
96+
}
8497
}
98+
} catch {
99+
$ErrorMessage = Get-CippException -Exception $_
100+
Write-LogMessage -API 'Alerts' -tenant $TenantFilter -message "Failed to read cached $($Source.Kind) policy states: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage
85101
}
86-
} catch {
87-
$ErrorMessage = Get-CippException -Exception $_
88-
Write-LogMessage -API 'Alerts' -tenant $TenantFilter -message "Failed to query Intune policy states: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage
89102
}
90103
}
91104

92-
if ($Config.IncludeApplications) {
105+
if ($Config.IncludeApplications -and $Config.AlertErrors) {
93106
try {
94-
$Applications = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?`$select=id,displayName&`$expand=deviceStatuses(`$select=installState,deviceName,userPrincipalName,deviceId)" -tenantid $TenantFilter
95-
96-
foreach ($App in $Applications) {
97-
$BadStatuses = $App.deviceStatuses | Where-Object {
98-
$_.installState -and ($AlertableStatuses -contains $_.installState.ToLowerInvariant())
99-
}
100-
101-
foreach ($Status in $BadStatuses) {
102-
$Issues.Add([PSCustomObject]@{
103-
Message = "App '$($App.displayName)' install is $($Status.installState) on device '$($Status.deviceName)' for $($Status.userPrincipalName)."
104-
Tenant = $TenantFilter
105-
Type = 'Application'
106-
AppName = $App.displayName
107-
IssueStatus = $Status.installState
108-
DeviceName = $Status.deviceName
109-
UserPrincipalName = $Status.userPrincipalName
110-
DeviceId = $Status.deviceId
111-
})
112-
}
107+
$AppItems = Get-CIPPDbItem -TenantFilter $TenantFilter -Type 'IntuneAppInstallStatusAggregate' | Where-Object { $_.RowKey -notlike '*-Count' }
108+
foreach ($AppItem in $AppItems) {
109+
$App = try { $AppItem.Data | ConvertFrom-Json -ErrorAction Stop } catch { $null }
110+
if (-not $App -or [int]($App.failedDeviceCount) -le 0) { continue }
111+
112+
$Issues.Add([PSCustomObject]@{
113+
Message = "App '$($App.displayName)' failed to install on $($App.failedDeviceCount) device(s) ($($App.failedDevicePercentage)%)."
114+
Tenant = $TenantFilter
115+
Type = 'Application'
116+
AppName = $App.displayName
117+
IssueStatus = 'failed'
118+
FailedDeviceCount = [int]$App.failedDeviceCount
119+
FailedUserCount = [int]$App.failedUserCount
120+
FailedPercentage = $App.failedDevicePercentage
121+
Platform = $App.platform
122+
})
113123
}
114124
} catch {
115125
$ErrorMessage = Get-CippException -Exception $_
116-
Write-LogMessage -API 'Alerts' -tenant $TenantFilter -message "Failed to query Intune application states: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage
126+
Write-LogMessage -API 'Alerts' -tenant $TenantFilter -message "Failed to read cached Intune app install status: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage
117127
}
118128
}
119129

Modules/CIPPCore/CIPPCore.psd1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
# RequiredModules = @()
4646

4747
# Assemblies that must be loaded prior to importing this module
48-
# RequiredAssemblies = @()
48+
RequiredAssemblies = @('..\..\Shared\CIPPSharp\bin\CIPPSharp.dll')
4949

5050
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
5151
# ScriptsToProcess = @()

Modules/CIPPCore/Public/AuditLogs/New-CIPPAuditLogSearchResultsCache.ps1

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@ function New-CIPPAuditLogSearchResultsCache {
7272
Add-CIPPAzDataTableEntity @CacheWebhooksTable -Entity $cacheEntity -Force
7373
}
7474
Write-Information "Successfully cached search ID: $($SearchId) for tenant: $TenantFilter"
75+
76+
try {
77+
$PrefetchIPs = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
78+
foreach ($sr in $searchResults) {
79+
$cip = $sr.auditData.clientip
80+
if (![string]::IsNullOrWhiteSpace($cip)) { $null = $PrefetchIPs.Add(([string]$cip).Trim()) }
81+
}
82+
if ($PrefetchIPs.Count -gt 0) {
83+
$null = Get-CIPPGeoIPLocationBatch -IPs @($PrefetchIPs)
84+
Write-Information "Geo prefetch: warmed cache for $($PrefetchIPs.Count) distinct IP(s) (search $SearchId)"
85+
}
86+
} catch {
87+
Write-Information "Geo prefetch during ingestion failed for search ${SearchId}: $($_.Exception.Message)"
88+
}
89+
7590
try {
7691
$FailedDownloadsTable = Get-CippTable -TableName 'FailedAuditLogDownloads'
7792
$failedEntities = Get-CIPPAzDataTableEntity @FailedDownloadsTable -Filter "PartitionKey eq '$TenantFilter' and SearchId eq '$SearchId'"

0 commit comments

Comments
 (0)