-
Notifications
You must be signed in to change notification settings - Fork 161
Expand file tree
/
Copy pathGraphSDK_Bitlocker_report.ps1
More file actions
156 lines (133 loc) · 8.76 KB
/
GraphSDK_Bitlocker_report.ps1
File metadata and controls
156 lines (133 loc) · 8.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#Requires -Version 3.0
#The script requires the following permissions:
# BitLockerKey.Read.All (required)
# Device.Read.All (optional, needed to retrieve device details)
# User.ReadBasic.All (optional, needed to retrieve device owner's UPN)
#For details on what the script does and how to run it, check: https://www.michev.info/blog/post/5950/reporting-on-bitlocker-recovery-keys-and-associated-devices
[CmdletBinding(SupportsShouldProcess)] #Make sure we can use -Verbose
Param([switch]$IncludeDeviceInfo,[switch]$IncludeDeviceOwner,[switch]$DeviceReport)
#==========================================================================
#Helper functions
#==========================================================================
function DriveType {
Param($Drive)
switch ($Drive) {
1 { "operatingSystemVolume" }
2 { "fixedDataVolume" }
3 { "removableDataVolume" }
4 { "unknownFutureValue" }
Default { "Unknown" }
}
}
#==========================================================================
#Main script starts here
#==========================================================================
#Handle parameter dependencies, device report requires device info and so on. We set $PSBoundParameters here, as we use it to determine the required scopes later on
if ($PSBoundParameters.ContainsKey("DeviceReport") -and $PSBoundParameters["DeviceReport"]) {
$PSBoundParameters["IncludeDeviceInfo"] = $true
$PSBoundParameters["IncludeDeviceOwner"] = $true
}
if ($PSBoundParameters.ContainsKey("IncludeDeviceOwner") -and $PSBoundParameters["IncludeDeviceOwner"]) {
$PSBoundParameters["IncludeDeviceInfo"] = $true
}
#Determine the required scopes, based on the parameters passed to the script
$RequiredScopes = switch ($PSBoundParameters.Keys) {
"IncludeDeviceInfo" { if ($PSBoundParameters["IncludeDeviceInfo"]) { "Device.Read.All" } }
"IncludeDeviceOwner" { if ($PSBoundParameters["IncludeDeviceOwner"]) { "User.ReadBasic.All" } } #Otherwise we only get the UserId
Default { "BitLockerKey.Read.All" }
}
Write-Verbose "Connecting to Graph API..."
Import-Module Microsoft.Graph.Identity.SignIns -Verbose:$false -ErrorAction Stop
try {
Connect-MgGraph -Scopes $RequiredScopes -verbose:$false -ErrorAction Stop -NoWelcome
}
catch { throw $_ }
#Check if we have all the required permissions
$CurrentScopes = (Get-MgContext).Scopes
if ($RequiredScopes | ? {$_ -notin $CurrentScopes }) { Write-Error "The access token does not have the required permissions, rerun the script and consent to the missing scopes!" -ErrorAction Stop }
#If requested, retrieve the device details
if ($PSBoundParameters["IncludeDeviceInfo"]) {
Write-Verbose "Retrieving device details..."
$Devices = @()
if ($PSBoundParameters["IncludeDeviceOwner"]) {
Write-Verbose "Retrieving device owner..."
$Devices = Get-MgDevice -All -ExpandProperty registeredOwners -ErrorAction Stop -Verbose:$false
}
else { $Devices = Get-MgDevice -All -ErrorAction Stop -Verbose:$false }
#Filter out devices with ID of 00000000-0000-0000-0000-000000000000
$Devices = $Devices | ? {$Device.Id -ne "00000000-0000-0000-0000-000000000000" -or $Device.DeviceId -ne "00000000-0000-0000-0000-000000000000"}
if ($Devices) { Write-Verbose "Retrieved $($Devices.Count) devices" }
else { Write-Verbose "No devices found"; continue }
#Prepare the device object to be used later on
if ($PSBoundParameters["DeviceReport"]) {
$Devices | Add-Member -MemberType NoteProperty -Name "BitLockerKeyId" -Value $null
$Devices | Add-Member -MemberType NoteProperty -Name "BitLockerRecoveryKey" -Value $null
$Devices | Add-Member -MemberType NoteProperty -Name "BitLockerDriveType" -Value $null
$Devices | Add-Member -MemberType NoteProperty -Name "BitLockerBackedUp" -Value $null
}
$Devices | % { Add-Member -InputObject $_ -MemberType NoteProperty -Name "DeviceOwner" -Value (&{if ($_.registeredOwners) { $_.registeredOwners[0].AdditionalProperties.userPrincipalName } else { "N/A" }}) }
}
#Get the list of application objects within the tenant.
$Keys = @()
#Get the list of BitLocker keys
Write-Verbose "Retrieving BitLocker keys..."
$Keys = Get-MgInformationProtectionBitlockerRecoveryKey -All -ErrorAction Stop -Verbose:$false
#Cycle through the keys and retrieve the key
Write-Verbose "Retrieving BitLocker Recovery keys..."
foreach ($Key in $Keys) {
#Skip stale/dummy devices
if ($Key.DeviceId -eq "00000000-0000-0000-0000-000000000000") {
Write-Warning "BitLocker key with ID $($Key.Id) has a device ID of 00000000-0000-0000-0000-000000000000, skipping..."
continue
}
#Get the BitLocker key details
$RecoveryKey = Get-MgInformationProtectionBitlockerRecoveryKey -BitlockerRecoveryKeyId $Key.Id -Property key -ErrorAction Stop -Verbose:$false
$Key.Key = (&{if ($RecoveryKey.Key) { $RecoveryKey.Key } else { "N/A" }})
$Key | Add-Member -MemberType NoteProperty -Name "BitLockerKeyId" -Value $Key.Id
$Key | Add-Member -MemberType NoteProperty -Name "BitLockerRecoveryKey" -Value $Key.Key
$Key | Add-Member -MemberType NoteProperty -Name "BitLockerDriveType" -Value (DriveType $Key.VolumeType)
$Key | Add-Member -MemberType NoteProperty -Name "BitLockerBackedUp" -Value (&{if ($Key.CreatedDateTime) { Get-Date($Key.CreatedDateTime) -Format g } else { "N/A" }})
#If requested, include the device details
if ($PSBoundParameters["IncludeDeviceInfo"]) {
$Device = $Devices | ? { $Key.DeviceId -eq $_.DeviceId }
if (!$Device) {
Write-Warning "Device with ID $($Key.DeviceId) not found!"
$Key | Add-Member -MemberType NoteProperty -Name "DeviceName" -Value "Device not found"
continue
}
if ($Device.Id -eq "00000000-0000-0000-0000-000000000000" -or $Device.DeviceId -eq "00000000-0000-0000-0000-000000000000") {
Write-Warning "Stale/dummy device found for key $($Key.DeviceId), skipping..."
$Key | Add-Member -MemberType NoteProperty -Name "DeviceName" -Value "Stale/Dummy Device"
continue
}
#If building a device report, add the BitLocker key details to the device object
if ($PSBoundParameters["DeviceReport"]) {
$Device.BitLockerKeyId = $Key.Id
$Device.BitLockerRecoveryKey = $Key.Key
$Device.BitLockerDriveType = (DriveType $Key.VolumeType)
$Device.BitLockerBackedUp = (&{if ($Key.CreatedDateTime) { Get-Date($Key.CreatedDateTime) -Format g } else { "N/A" }})
}
$Key | Add-Member -MemberType NoteProperty -Name "DeviceName" -Value $Device.DisplayName
$Key | Add-Member -MemberType NoteProperty -Name "DeviceGUID" -Value $Device.Id #key actually used by the stupid module...
$Key | Add-Member -MemberType NoteProperty -Name "DeviceOS" -Value $Device.OperatingSystem
$Key | Add-Member -MemberType NoteProperty -Name "DeviceTrustType" -Value $Device.TrustType
$Key | Add-Member -MemberType NoteProperty -Name "DeviceMDM" -Value $Device.AdditionalProperties.managementType #can be null! ALWAYS null when using a filter...
$Key | Add-Member -MemberType NoteProperty -Name "DeviceCompliant" -Value $Device.isCompliant #can be null!
$Key | Add-Member -MemberType NoteProperty -Name "DeviceRegistered" -Value (&{if ($Device.registrationDateTime) { Get-Date($Device.registrationDateTime) -Format g } else { "N/A" }})
$Key | Add-Member -MemberType NoteProperty -Name "DeviceLastActivity" -Value (&{if ($Device.approximateLastSignInDateTime) { Get-Date($Device.approximateLastSignInDateTime) -Format g } else { "N/A" }})
#If requested, include the device owner
if ($PSBoundParameters["IncludeDeviceOwner"]) {
$Key | Add-Member -MemberType NoteProperty -Name "DeviceOwner" -Value (&{if ($Device.registeredOwners) { $Device.registeredOwners[0].AdditionalProperties.userPrincipalName } else { "N/A" }})
}
}
}
#Export the result to CSV file
if ($PSBoundParameters["DeviceReport"]) {
#BitLocker keys are at the front, followed by the device details. Cleaned up few "internal" properties, adjust the list below as needed
$ExcludeProps = @("AdditionalProperties","AlternativeSecurityIds","complianceExpirationDateTime","deviceMetadata","deviceVersion","memberOf","PhysicalIds","SystemLabels","transitiveMemberOf","RegisteredOwners","RegisteredUsers")
$Devices | select * -ExcludeProperty $ExcludeProps | Export-CSV -nti -Path "$((Get-Date).ToString('yyyy-MM-dd_HH-mm-ss'))_BitLockerKeys.csv"
}
else {
$Keys | select * -ExcludeProperty Id,VolumeType,AdditionalProperties,CreatedDateTime,Key | Export-CSV -nti -Path "$((Get-Date).ToString('yyyy-MM-dd_HH-mm-ss'))_BitLockerKeys.csv"
}
Write-Verbose "Output exported to $($PWD)\$((Get-Date).ToString('yyyy-MM-dd_HH-mm-ss'))_BitLockerKeys.csv"