Skip to content

Commit 2010f54

Browse files
authored
Merge pull request #800 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 9dfde27 + 9c9b3a1 commit 2010f54

6 files changed

Lines changed: 310 additions & 4 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ function Push-CIPPDBCacheData {
5959
'AppRoleAssignments'
6060
'LicenseOverview'
6161
'MFAState'
62+
'BitlockerKeys'
6263
)
6364

6465
foreach ($CacheFunction in $BasicCacheFunctions) {
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
function Invoke-ExecBitlockerSearch {
2+
<#
3+
.FUNCTIONALITY
4+
Entrypoint,AnyTenant
5+
.ROLE
6+
Endpoint.Device.Read
7+
#>
8+
[CmdletBinding()]
9+
param($Request, $TriggerMetadata)
10+
11+
try {
12+
# Get search parameters from query string or POST body
13+
$KeyId = $Request.Query.keyId ?? $Request.Body.keyId
14+
$DeviceId = $Request.Query.deviceId ?? $Request.Body.deviceId
15+
$Limit = $Request.Query.limit ?? $Request.Body.limit ?? 0
16+
17+
# Handle tenant filtering with access control
18+
$AllowedTenants = Test-CIPPAccess -Request $Request -TenantList
19+
20+
$TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter
21+
if ($AllowedTenants -notcontains 'AllTenants') {
22+
if ($TenantFilter) {
23+
# Verify user has access to requested tenant
24+
$TenantList = Get-Tenants | Select-Object -ExpandProperty defaultDomainName
25+
if ($TenantList -notcontains $TenantFilter) {
26+
return [HttpResponseContext]@{
27+
StatusCode = [HttpStatusCode]::Forbidden
28+
Body = @{
29+
error = "Access denied to tenant: $TenantFilter"
30+
}
31+
}
32+
}
33+
} else {
34+
$TenantFilter = Get-Tenants | Select-Object -ExpandProperty defaultDomainName
35+
}
36+
} elseif (-not $TenantFilter) {
37+
$TenantFilter = 'allTenants'
38+
}
39+
40+
# Build parameters for Search-CIPPBitlockerKeys
41+
$SearchParams = @{}
42+
43+
if ($TenantFilter) {
44+
$SearchParams.TenantFilter = $TenantFilter
45+
Write-Information "Filtering by tenant: $TenantFilter"
46+
}
47+
48+
if ($KeyId) {
49+
$SearchParams.KeyId = $KeyId
50+
Write-Information "Searching for key ID: $KeyId"
51+
} elseif ($DeviceId) {
52+
$SearchParams.DeviceId = $DeviceId
53+
Write-Information "Searching for device ID: $DeviceId"
54+
} else {
55+
return [HttpResponseContext]@{
56+
StatusCode = [HttpStatusCode]::BadRequest
57+
Body = @{
58+
error = 'No search criteria provided. Please provide keyId or deviceId.'
59+
}
60+
}
61+
}
62+
63+
if ($Limit -gt 0) {
64+
$SearchParams.Limit = [int]$Limit
65+
}
66+
67+
# Execute the search
68+
$Results = Search-CIPPBitlockerKeys @SearchParams
69+
70+
Write-Information "Found $($Results.Count) BitLocker key record(s)"
71+
72+
# Format results for output
73+
$OutputResults = @($Results | ForEach-Object {
74+
[PSCustomObject]@{
75+
tenant = $_.Tenant
76+
keyId = $_.Data.id
77+
createdDateTime = $_.Data.createdDateTime
78+
volumeType = $_.Data.volumeType
79+
deviceId = $_.Data.deviceId
80+
deviceName = $_.Data.deviceName
81+
operatingSystem = $_.Data.operatingSystem
82+
osVersion = $_.Data.osVersion
83+
lastSignIn = $_.Data.lastSignIn
84+
accountEnabled = $_.Data.accountEnabled
85+
trustType = $_.Data.trustType
86+
deviceFound = $_.Data.deviceFound
87+
timestamp = $_.Timestamp
88+
}
89+
})
90+
91+
return [HttpResponseContext]@{
92+
StatusCode = [HttpStatusCode]::OK
93+
Body = @{
94+
Results = $OutputResults
95+
Count = $OutputResults.Count
96+
}
97+
}
98+
99+
} catch {
100+
Write-Information "Error occurred during BitLocker key search: $($_.Exception.Message)"
101+
Write-Information $_.InvocationInfo.PositionMessage
102+
return [HttpResponseContext]@{
103+
StatusCode = [HttpStatusCode]::InternalServerError
104+
Body = @{
105+
error = "Failed to search for BitLocker keys: $($_.Exception.Message)"
106+
}
107+
}
108+
}
109+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
function Search-CIPPBitlockerKeys {
2+
<#
3+
.SYNOPSIS
4+
Search for BitLocker recovery keys and merge with device information
5+
6+
.DESCRIPTION
7+
Searches cached BitLocker recovery keys and automatically enriches results with device information
8+
by cross-referencing the deviceId with Devices or ManagedDevices data.
9+
10+
.PARAMETER TenantFilter
11+
Tenant domain or GUID to search. If not specified, searches all tenants.
12+
13+
.PARAMETER KeyId
14+
Optional BitLocker recovery key ID to search for. If not specified, returns all keys.
15+
16+
.PARAMETER DeviceId
17+
Optional device ID to filter BitLocker keys by device.
18+
19+
.PARAMETER SearchTerms
20+
Optional search terms to filter results (searches across all BitLocker key fields).
21+
22+
.PARAMETER Limit
23+
Maximum number of results to return. Default is unlimited (0).
24+
25+
.EXAMPLE
26+
Search-CIPPBitlockerKeys -TenantFilter 'contoso.onmicrosoft.com' -KeyId '8911a878-b631-47e8-b5e8-bcb00e586c74'
27+
28+
.EXAMPLE
29+
Search-CIPPBitlockerKeys -DeviceId '1b418b08-a0c6-4db1-95cd-08a9b943b70e'
30+
31+
.EXAMPLE
32+
Search-CIPPBitlockerKeys -SearchTerms 'device-name'
33+
34+
.FUNCTIONALITY
35+
Internal
36+
#>
37+
[CmdletBinding()]
38+
param(
39+
[Parameter(Mandatory = $false)]
40+
[string]$TenantFilter,
41+
42+
[Parameter(Mandatory = $false)]
43+
[string]$KeyId,
44+
45+
[Parameter(Mandatory = $false)]
46+
[string]$DeviceId,
47+
48+
[Parameter(Mandatory = $false)]
49+
[string[]]$SearchTerms,
50+
51+
[Parameter(Mandatory = $false)]
52+
[int]$Limit = 0
53+
)
54+
55+
try {
56+
# Build search parameters
57+
$SearchParams = @{
58+
Types = @('BitlockerKeys')
59+
}
60+
61+
if ($TenantFilter) {
62+
$SearchParams.TenantFilter = @($TenantFilter)
63+
}
64+
65+
# Determine what to search for
66+
if ($KeyId) {
67+
$SearchParams.SearchTerms = @($KeyId)
68+
} elseif ($DeviceId) {
69+
$SearchParams.SearchTerms = @($DeviceId)
70+
} elseif ($SearchTerms) {
71+
$SearchParams.SearchTerms = $SearchTerms
72+
} else {
73+
# If no search criteria, search for a pattern that matches any GUID or just get all
74+
$SearchParams.SearchTerms = @('[a-f0-9]{8}-')
75+
}
76+
77+
if ($Limit -gt 0) {
78+
$SearchParams.Limit = $Limit
79+
}
80+
81+
Write-Verbose "Searching for BitLocker keys with params: $($SearchParams | ConvertTo-Json -Compress)"
82+
83+
# Search for BitLocker keys
84+
$BitlockerResults = Search-CIPPDbData @SearchParams
85+
86+
if (-not $BitlockerResults -or $BitlockerResults.Count -eq 0) {
87+
Write-Verbose 'No BitLocker keys found'
88+
return @()
89+
}
90+
91+
Write-Verbose "Found $($BitlockerResults.Count) BitLocker key(s)"
92+
93+
# Enrich each result with device information
94+
$EnrichedResults = foreach ($Result in $BitlockerResults) {
95+
$BitlockerData = $Result.Data
96+
$DeviceInfo = $null
97+
98+
if ($BitlockerData.deviceId) {
99+
Write-Verbose "Looking up device info for deviceId: $($BitlockerData.deviceId)"
100+
101+
# Try to find device in Devices collection first
102+
try {
103+
$DeviceSearch = Search-CIPPDbData -TenantFilter $Result.Tenant -Types 'Devices' -SearchTerms $BitlockerData.deviceId -Limit 1
104+
if ($DeviceSearch -and $DeviceSearch.Count -gt 0) {
105+
$DeviceInfo = $DeviceSearch[0].Data
106+
Write-Verbose "Found device in Devices collection: $($DeviceInfo.displayName)"
107+
}
108+
} catch {
109+
Write-Verbose "Error searching Devices: $($_.Exception.Message)"
110+
}
111+
112+
# If not found in Devices, try ManagedDevices
113+
if (-not $DeviceInfo) {
114+
try {
115+
$DeviceSearch = Search-CIPPDbData -TenantFilter $Result.Tenant -Types 'ManagedDevices' -SearchTerms $BitlockerData.deviceId -Limit 1
116+
if ($DeviceSearch -and $DeviceSearch.Count -gt 0) {
117+
$DeviceInfo = $DeviceSearch[0].Data
118+
Write-Verbose "Found device in ManagedDevices collection: $($DeviceInfo.deviceName)"
119+
}
120+
} catch {
121+
Write-Verbose "Error searching ManagedDevices: $($_.Exception.Message)"
122+
}
123+
}
124+
}
125+
126+
# Create enriched result
127+
$EnrichedData = [PSCustomObject]@{
128+
# BitLocker key information
129+
id = $BitlockerData.id
130+
createdDateTime = $BitlockerData.createdDateTime
131+
volumeType = $BitlockerData.volumeType
132+
deviceId = $BitlockerData.deviceId
133+
134+
# Device information (if found)
135+
deviceName = if ($DeviceInfo) { $DeviceInfo.displayName ?? $DeviceInfo.deviceName } else { $null }
136+
operatingSystem = if ($DeviceInfo) { $DeviceInfo.operatingSystem } else { $null }
137+
osVersion = if ($DeviceInfo) { $DeviceInfo.operatingSystemVersion ?? $DeviceInfo.osVersion } else { $null }
138+
lastSignIn = if ($DeviceInfo) { $DeviceInfo.approximateLastSignInDateTime ?? $DeviceInfo.lastSyncDateTime } else { $null }
139+
accountEnabled = if ($DeviceInfo) { $DeviceInfo.accountEnabled ?? $DeviceInfo.isCompliant } else { $null }
140+
trustType = if ($DeviceInfo) { $DeviceInfo.trustType ?? $DeviceInfo.joinType } else { $null }
141+
142+
# Metadata
143+
deviceFound = $null -ne $DeviceInfo
144+
}
145+
146+
[PSCustomObject]@{
147+
Tenant = $Result.Tenant
148+
Type = $Result.Type
149+
RowKey = $Result.RowKey
150+
Data = $EnrichedData
151+
Timestamp = $Result.Timestamp
152+
}
153+
}
154+
155+
Write-Verbose "Returning $($EnrichedResults.Count) enriched result(s)"
156+
return $EnrichedResults
157+
158+
} catch {
159+
Write-LogMessage -API 'SearchBitlockerKeys' -tenant $TenantFilter -message "Failed to search BitLocker keys: $($_.Exception.Message)" -sev Error
160+
throw
161+
}
162+
}

Modules/CIPPCore/Public/Search-CIPPDbData.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ function Search-CIPPDbData {
6868
'Users', 'Domains', 'ConditionalAccessPolicies', 'ManagedDevices', 'Organization',
6969
'Groups', 'Roles', 'LicenseOverview', 'IntuneDeviceCompliancePolicies', 'SecureScore',
7070
'SecureScoreControlProfiles', 'Mailboxes', 'CASMailbox', 'MailboxPermissions',
71-
'OneDriveUsage', 'MailboxUsage', 'Devices', 'AllRoles', 'Licenses', 'DeviceCompliancePolicies'
71+
'OneDriveUsage', 'MailboxUsage', 'Devices', 'AllRoles', 'Licenses', 'DeviceCompliancePolicies',
72+
'BitlockerKeys'
7273
)]
7374
[string[]]$Types,
7475

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
function Set-CIPPDBCacheBitlockerKeys {
2+
<#
3+
.SYNOPSIS
4+
Caches all BitLocker recovery keys for a tenant
5+
6+
.PARAMETER TenantFilter
7+
The tenant to cache BitLocker keys for
8+
9+
.PARAMETER QueueId
10+
The queue ID to update with total tasks (optional)
11+
#>
12+
[CmdletBinding()]
13+
param(
14+
[Parameter(Mandatory = $true)]
15+
[string]$TenantFilter,
16+
[string]$QueueId
17+
)
18+
19+
try {
20+
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching BitLocker recovery keys' -sev Debug
21+
22+
$BitlockerKeys = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/informationProtection/bitlocker/recoveryKeys' -tenantid $TenantFilter
23+
if (!$BitlockerKeys) { $BitlockerKeys = @() }
24+
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'BitlockerKeys' -Data $BitlockerKeys
25+
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'BitlockerKeys' -Data $BitlockerKeys -Count
26+
$BitlockerKeys = $null
27+
28+
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Cached BitLocker recovery keys successfully' -sev Debug
29+
30+
} catch {
31+
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message "Failed to cache BitLocker recovery keys: $($_.Exception.Message)" -sev Error
32+
}
33+
}

Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDelegateSentItems.ps1

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ function Invoke-CIPPStandardDelegateSentItems {
5353
$CurrentValue = if (!$Mailboxes) {
5454
[PSCustomObject]@{ state = 'Configured correctly' }
5555
} else {
56-
[PSCustomObject]@{ NonCompliantMailboxes = $Mailboxes | Select-Object -Property UserPrincipalName, MessageCopyForSendOnBehalfEnabled, MessageCopyForSentAsEnabled }
56+
[PSCustomObject]@{ NonCompliantMailboxes = $Mailboxes | Select-Object -Property UPN, MessageCopyForSendOnBehalfEnabled, MessageCopyForSentAsEnabled }
5757
}
5858
$ExpectedValue = [PSCustomObject]@{
5959
state = 'Configured correctly'
@@ -66,7 +66,7 @@ function Invoke-CIPPStandardDelegateSentItems {
6666
@{
6767
CmdletInput = @{
6868
CmdletName = 'Set-Mailbox'
69-
Parameters = @{Identity = $Mailbox.UserPrincipalName ; MessageCopyForSendOnBehalfEnabled = $true; MessageCopyForSentAsEnabled = $true }
69+
Parameters = @{Identity = $Mailbox.UPN ; MessageCopyForSendOnBehalfEnabled = $true; MessageCopyForSentAsEnabled = $true }
7070
}
7171
}
7272
}
@@ -97,7 +97,7 @@ function Invoke-CIPPStandardDelegateSentItems {
9797
}
9898

9999
if ($Settings.report -eq $true) {
100-
$Filtered = $Mailboxes | Select-Object -Property UserPrincipalName, MessageCopyForSendOnBehalfEnabled, MessageCopyForSentAsEnabled
100+
$Filtered = $Mailboxes | Select-Object -Property UPN, MessageCopyForSendOnBehalfEnabled, MessageCopyForSentAsEnabled
101101
Set-CIPPStandardsCompareField -FieldName 'standards.DelegateSentItems' -CurrentValue $CurrentValue -ExpectedValue $ExpectedValue -TenantFilter $Tenant
102102
Add-CIPPBPAField -FieldName 'DelegateSentItems' -FieldValue $Filtered -StoreAs json -Tenant $Tenant
103103
}

0 commit comments

Comments
 (0)