Enforce BitLocker startup PIN on Windows with Intune
The purpose of this blog post is to inform you how to enforce a BitLocker startup Pin for standard users. I was inspired by the solution of Oliver Kieselbach, but his solution was user-driven and not enforced so I decided to change some settings, make a proactive remediation script, and create a custom Compliance check to enforce the BitLocker startup pin.
Requirements:
Original solution
By default, a standard user is not able to set a BitLocker startup pin. Only a local admin can set the pin code, so there must be another method. I did some research, and I found the solution of Oliver Kieselbach. You can find his blog and the original content here.
Enforced solution
The solution did not force the user to set the startup pin, but I wanted to enforce it, so I decided to modify his solution. I change some settings in his code, so the user cannot close the pin code diagram anymore. Created the Win32 app and set it as a required install so everything was on the device. The last piece of the puzzle was to enforce it with a proactive remediation script and a custom compliance policy.
What did I change?
File: popup.ps1
Line: 1
Original:
# Author: Oliver Kieselbach (oliverkieselbach.com)
Changed to:
# Author: Oliver Kieselbach (oliverkieselbach.com) & Rene Laas (endpointcave.com)
File: popup.ps1
Line: 12
Add:
# - 02/23/2022 Added simple PIN i.e. 000000 check # added by Rene Laas - Endpointcave.com
File: popup.ps1
Line: 66
Add:
# Test of all numbers in array are the same
# Function Added by Rene Laas Endpointcave.com
function Test-Number
{
param (
[String]
$NumberString
)
$array = $NumberString.ToCharArray()
If ( @($array | Select -Unique).Count -eq 1 )
{
return $true
}
Else
{
return $false
}
}
if(!(Test-Path $PSScriptRoot\BitLockerRunning.txt))
{
File: popup.ps1
Line: 113
Original:
$language = Get-Content language.json -Encoding UTF8 | ConvertFrom-Json
Changed to:
$language = Get-Content "C:\progra~2\BitLockerPINDialog\language.json" -Encoding UTF8 | ConvertFrom-Json # Modified by Rene Laas Endpointcave - Change json location
File: popup.ps1
Line: 176
Original:
$pathPINFile = $(Join-Path -Path "$env:SystemRoot\tracing" -ChildPath "168ba6df825678e4da1a.tmp")
Changed to:
#$pathPINFile = $(Join-Path -Path "$env:SystemRoot\tracing" -ChildPath "168ba6df825678e4da1a.tmp")
$pathPINFile = "C:\PinTemp\PIN-prompted.txt" # Modified by Rene Laas Endpointcave.com
File: popup.ps1
Line: 183
Add:
Remove-Item -Path $PSScriptRoot\BitLockerRunning.txt -Force # Added by Rene Laas Endpointcave.com
File: popup.ps1
Line: 241
Original:
$formBitLockerStartupPIN.ClientSize = '445, 271'
Changed to:
$formBitLockerStartupPIN.ClientSize = '445, 305' # Modified by Rene Laas Endpointcave - change 271 to 305
File: popup.ps1
Line: 1783
Add:
$formBitLockerStartupPIN.ControlBox = $False # Added by Rene Laas Endpointcave
File: popup.ps1
Line: 1794
Original:
$labelPINIsNotEqual.Location = '275, 181'
Changed to:
$labelPINIsNotEqual.Location = '275, 210' # Modified by Rene Laas Endpointcave - from 181 to 210
File: popup.ps1
Line: 1805
Original:
$labelRetypePIN.Location = '26, 146'
Changed to:
$labelRetypePIN.Location = '26, 186' # Modified by Rene Laas Endpointcave - from 146 to 186
File: popup.ps1
Line: 1815
Original:
$labelNewPIN.Location = '26, 105'
Changed to:
$labelNewPIN.Location = '26, 145' # Modified by Rene Laas Endpointcave - from 105 to 145
File: popup.ps1
Line: 1834
Original:
$panelBottom.Controls.Add($buttonCancel)
Changed to:
#$panelBottom.Controls.Add($buttonCancel) # Disabled by Rene Laas Endpointcave
File: popup.ps1
Line: 1837
Original:
$panelBottom.Location = '-1, 209'
Changed to:
$panelBottom.Location = '-1, 243' # Modified by Rene Laas Endpointcave - from 209 to 243
File: popup.ps1
Line: 1855
Original:
$buttonSetPIN.Location = '225, 17'
Changed to:
$buttonSetPIN.Location = '320, 17' # Modified by Rene Laas Endpointcave - from 225 to 320
File: popup.ps1
Line: 1877
Original:
$textboxRetypedPin.Location = '140, 143'
Changed to:
$textboxRetypedPin.Location = '180, 183' # Modified by Rene Laas Endpointcave - from 140 to 180 and 143 to 183
File: popup.ps1
Line: 1886
Original:
$textboxNewPin.Location = '140, 102'
Changed to:
$textboxNewPin.Location = '180, 142' # Modified by Rene Laas Endpointcave - from 140 to 180 and 102 to 142
File: popup.ps1
Line: 1901
Add:
}
Else
{
# Check if file is older/newer
$lastWrite = (get-item $PSScriptRoot\BitLockerRunning.txt).LastWriteTime
$timespan = new-timespan -days 1
if (((get-date) - $lastWrite) -ge $timespan)
{
Write-Output "BitLockerRunning.txt is created more than 1 day ago, deleting file and waiting for next cycle."
Remove-Item -Path $PSScriptRoot\BitLockerRunning.txt -ForceRemove-Item -Path $PSScriptRoot\BitLockerRunning.txt -Force
}
else
{
Write-Output "BitLockerRunning.txt is created less than 1 day ago."
}
}
File: Setbitlockerpin.ps1
Line: 13
Original:
.\ServiceUI.exe -process:Explorer.exe "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -WindowStyle Hidden -Ex bypass -file "$PSScriptRoot\Popup.ps1"
Changed to:
C:\progra~2\BitLockerPINDialog\ServiceUI.exe -process:Explorer.exe "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -WindowStyle Hidden -Ex bypass -file "C:\progra~2\BitLockerPINDialog\Popup.ps1" # Modified by Rene Laas Endpointcave - Path modified
File: Setbitlockerpin.ps1
Line: 19
Original:
$pathPINFile = $(Join-Path -Path "$env:SystemRoot\tracing" -ChildPath "168ba6df825678e4da1a.tmp")
Changed to:
#$pathPINFile = $(Join-Path -Path "$env:SystemRoot\tracing" -ChildPath "168ba6df825678e4da1a.tmp")
$pathPINFile = "C:\PINtemp\PIN-prompted.txt" # Modified by Rene Laas Endpointcave
Add install/uninstall script to the source?
For the correct working of the script, some files must be present on the device. I have added therefore a PowerShell installation and an uninstallation script that will make sure the needed files are in the required subdirectory of the program files directory or can be deleted.
Install.ps1:
New-Item -ItemType Directory -Force -Path C:\progra~2\BitLockerPINDialog\
Move-Item -Path .\*.* -Destination C:\progra~2\BitLockerPINDialog\
Uninstall.ps1:
Remove-Item -Path C:\progra~2\BitLockerPINDialog -Recurse
How to configure the BitLocker settings
- Open Microsoft Endpoint manager
- In the menu select Endpoint security
- Under Manage, select Disk encryption
Or use the following link Disk encryption – Microsoft Endpoint Manager admin center - Click on + Create Policy
- Select Windows 10 and later as the platform and BitLocker as the profile
- Click on create
- Set a name and optionally the description and click on Next
- Configure the following settings
BitLocker – Base settings
Setting | Value |
---|---|
Enable full disk encryption for OS and fixed data drives | Yes |
Require storage cards to be encrypted (mobile only) | Not configured |
Hide prompt about third-party encryption | Yes |
Allow standard users to enable encryption during Autopilot | Yes |
Configure client-driven recovery password rotation | Enable rotation on Azure AD and Hybrid-joined devices |
Bitlocker OS Drive Settings
Setting | Value |
---|---|
BitLocker system drive policy | Configure |
Startup authentication required | Yes |
Compatible TPM startup | Allowed |
Compatible TPM startup PIN | Allowed |
Compatible TPM startup key | Blocked |
Compatible TPM startup key and PIN | Blocked |
Disable BitLocker on devices where TPM is incompatible | Not configure |
Enable preboot recovery message and URL | Not configure |
System drive recovery | Configure |
Recovery key file creation | Allowed |
Configure BitLocker recovery package | Password and key |
Require device to back up recovery information to Azure AD | Yes |
Recovery password creation | Allowed |
Hide recovery options during BitLocker setup | Yes |
Enable BitLocker after recovery information to store | Yes |
Block the use of certificate-based data recovery agent (DRA) | Not configure |
Minimum PIN length | 6 |
Configure encryption method for Operating System drives | AES 256bit XTS |
Note. I recommended to configure BitLocker – Fixed Drive Settings and BitLocker – Removable Drive Settings as well, but both are not required for the BitLocker Startup Pin
How to create the BitLocker Startup Pin package
- Download the SetBitLockerPin files of Oliver his Github page.
Link: Intune/Win32/SetBitLockerPin at master · okieselbach/Intune · GitHub - Copy the SetBitLockerPin files to an empty folder
- Modify the files that are mentioned in the paragraph What did I change?
- Create the .intunewin package with the Intune content preparation tool with the following command:
{directory of WinappUtil}\IntuneWinAppUtil.exe -c { directory of SetBitLockerPin files } -s install.ps1 -o .\ -q
Or
- Download the modified SetBitLockerPin files from my Github page
Link: EndpointCave.com/BitLockerStartupPin at main · Renelaas/EndpointCave.com · GitHub - Copy the SetBitLockerPin files to an empty folder
- Create the .intunewin package with the Intune content preparation tool with the following command:
{directory of WinappUtil}\IntuneWinAppUtil.exe -c {directory of SetBitLockerPin files} -s install.ps1 -o .\ -q
Or
- Download the .intunewin package here
How to install BitLocker solution
- Open Microsoft Endpoint manager
- In the menu select Apps
- Under Apps, select Windows
Or use the following link Windows Apps – Microsoft Endpoint Manager admin center - Click on + Add
- Select the App Type Windows App (Win32) and click on Select
- Click on the Select app package file and click on the blue directory icon
- The file explorer will open and browse to the .Intunewin file that you have created.
- Click on OK
- Set a name, description, and publisher of the app
- Click on Next
- Set the following Install and uninstall commands
Install:
powershell.exe -executionpolicy bypass -file ".\install.ps1"
Uninstall:
powershell.exe -executionpolicy bypass -file ".\uninstall.ps1"
- Select 64-bit as the operating system architecture
- Select Windows 10 20H2 as the minimum Operating system
- Select Manually configure detection rules as the rules format
- Click on + Add and configure the following detection rule
Setting | Value |
---|---|
Rule Type | File |
Path | C:\progra~2\BitLockerPINDialog\ |
File or folder | ServiceUI.exe |
Detection method | File or folder exists |
Associated with a 32-bit app on 64-bit clients | No |
- Click on Next and on the Dependencies and Supersedence section click also next
- Set the assignment to require for all devices
- Click on next. Check the app configuration and click on Create.
- The app will be created and uploaded
Proactive Remediation Scripts that need to be uploaded to Intune
Detection Script:
$pin = (Get-BitLockerVolume -MountPoint $env:SystemDrive).KeyProtector | Where { $_.KeyProtectorType -eq 'TpmPin' }
if (((Get-BitLockerVolume -MountPoint $env:SystemDrive).VolumeStatus) -ne "FullyDecrypted")
{
Write-Output "Encryption enabled"
if ($pin -ne $null)
{
Write-Output "TPM Pin set"
Exit 0
}
else
{
Write-Output "TPM Pin is not set"
Exit 1
}
}
else
{
Write-Output "Encryption not yet started"
Exit 0
}
Remediation Script:
#Author: Oliver Kieselbach (oliverkieselbach.com) & Rene Laas (endpointcave.com)
# Date: 08/01/2019
# Description: Starts the Windows Forms Dialog for BitLocker PIN entry and receives the PIN via exit code to set the additional key protector
# - 10/21/2019 changed PIN handover
# - 02/10/2020 added content length check
# - 09/30/2021 changed PIN handover to AES encryption/decryption via DPAPI and shared key
# added simple PIN check for incrementing and decrementing numbers e.g. 123456 and 654321
# language support (see language.json), default is always en-US
# changed temp storage location and temp file name
# The script is provided "AS IS" with no warranties.
# Added by Rene Laas endpoincave
if (!(Test-Path "C:\PINtemp"))
{
New-Item -Path 'C:\PINtemp' -ItemType Directory
Write-output "Temp directory created"
}
else
{
Write-output "Temp directory already exist"
}
# Added by Rene Laas Endpointcave
# Pin temp file
$PathPINtemp = "C:\PINtemp\"
$pathPINFile = $PathPINtemp + "PIN-prompted.txt"
#######################
C:\progra~2\BitLockerPINDialog\ServiceUI.exe -process:Explorer.exe "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -WindowStyle Hidden -Ex bypass -file "C:\progra~2\BitLockerPINDialog\SetBitLockerPin.ps1" # Modified by Rene Laas Endpointcave
$exitCode = $LASTEXITCODE
# ASR rules can block the write access to public documents, so we use a writeable path for users and system
# Check with sysinternals tool: accesschk.exe users -wus c:\windows\*
# "c:\windows\tracing" should be fine as temp storage
#$pathPINFile = $(Join-Path -Path "$env:SystemRoot\tracing" -ChildPath "168ba6df825678e4da1a.tmp")
# Alternativly use public documents, but keep in mind the ASR rules!
#$pathPINFile = $(Join-Path -Path $([Environment]::GetFolderPath("CommonDocuments")) -ChildPath "168ba6df825678e4da1a.tmp")
If ($exitCode -eq 0 -And (Test-Path -Path $pathPINFile)) {
$encodedText = Get-Content -Path $pathPINFile
if ($encodedText.Length -gt 0) {
# Using DPAPI with a random generated shared 256-bit key to decrypt the PIN
$key = (43,155,164,59,21,127,28,43,81,18,198,145,127,51,72,55,39,23,228,166,146,237,41,131,176,14,4,67,230,81,212,214)
$secure = ConvertTo-SecureString $encodedText -Key $key
# Code for PS7+
#$PIN = ConvertFrom-SecureString -SecureString $secure -AsPlainText
# Code for PS5
$PIN = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure))
Add-BitLockerKeyProtector -MountPoint $env:SystemDrive -Pin $(ConvertTo-SecureString $PIN -AsPlainText -Force) -TpmAndPinProtector
}
}
# Cleanup ###
# Modified by Rene Laas Endpointcave
if((Test-Path $pathPINFile))
{
Remove-Item -Path $pathPINFile -Force -Recurse
Write-output "Encrypted Pin file deleted"
}
if((Test-Path $PathPINtemp))
{
Remove-Item -Path $PathPINtemp -Force -Recurse
Write-Output "Deleted temp folder"
}
How to enforce BitLocker Startup pin via Intune with Proactive Remediation Scripts
- Open Microsoft endpoint manager
- In the menu select Reports
- Click on Endpoint analytics in the menu under Analytics
- Click on Proactive remediations
Or use the following link Endpoint analytics – Microsoft Endpoint Manager admin center - Click on Create script package
- Provide a Name script name
- Set a description, so that everyone with access to the portal knows the purpose of the script
- Click on Next
- Upload the Detection scripts and Remediation scripts.
- Configure the following PowerShell script settings
Setting | Value |
---|---|
Run this script using the logged on credentials | No |
Enforce script signature check | No |
Run script in 64-bit PowerShell Host | Yes |
- Click on Nextand on the Scope tags section click on Next
- Assign remediation script to All User
- Change the schedule via the 3 bullets and Click on Edit
- Click on the dropdown list and change the frequency from Daily to Hourly and click on Next
- Check on the review + create page if all settings are configured correctly and click on Create
Result:
Create a Custom Compliance check
- Open Microsoft endpoint manager
- In the menu select Devices
- Click on Compliance Policies under Policy in the new menu
- In the menu click on Scripts
or use the following link: Compliance policies – Microsoft Endpoint Manager admin center - Click on + Add and select Windows 10 and later
- Fill in the following settings
Setting | Value |
---|---|
Name | BitLockerPin |
Description | Custom compliance check for BitLocker Startup Ping |
Publisher | Endpointcave.com |
- Click on next
- Configure the following Custom script settings
Detection script Script
$BLinfo = (Get-BitLockerVolume -MountPoint $env:SystemDrive).KeyProtector | Where { $_.KeyProtectorType -eq 'TpmPin' }
$hash = @{KeyProtectorType = $BLinfo.KeyProtectorType}
return $hash | ConvertTo-Json -Compress
Setting | Value |
---|---|
Run this script using the logged on credentials | No |
Enforce script signature check | No |
Run script in 64-bit PowerShell Host | Yes |
- Click on Next, review the configuration, and click on Create
- The next step is to open an existing compliance policy
- In the menu click on policies
- Open an existing compliance policy to enforce BitLocker startup Pin
- Click on Properties under Manage
- Edit the compliance settings via the edit button
- Open the Custom Compliance section
- Configure the following Custom Compliance settings
Setting | Value |
---|---|
Custom compliance | Required |
Select your discovery script | BitlockerPin |
JSON
{
"Rules":[
{
"SettingName":"KeyProtectorType",
"Operator":"IsEquals",
"DataType":"Int64",
"Operand":"4",
"MoreInfoUrl":"https://endpointcave.com",
"RemediationStrings":[
{
"Language":"en_US",
"Title":"Bitlocker Startup Pin must be set",
"Description": " Bitlocker Startup Pin must be set "
}
]
}
]
}
Result:
Hi, ive followed all the steps in the manual.
The thing is that I dont see bitlocker enabled on the devices and dont see the pop up..
However, the win32 apps are succesfully installed and i can see the scripts located on the path C:\Programfiles..
The policies of bitlocker are deployed succesfully
The remedationscript hasn’t run yet.. only the detectionscript has run
What should i do to make this work?
First, configure BitLocker correctly, the drive should be encrypted automatically. If the drive is encrypted, the detection script will detect that BitLocker is turned on and will prompt to set a pin
Hi Rene,
Thanks for putting this together, the device is silently encrypted with the intune config profile. I was able to install the Bitlocker app, I see the files created in the Program files folder, the pop-up request for the pin, after I set the pin, the pop-up disappeared, no errors, There were no temp files created in the C: folder. I also don’t see a start-up pin request when I restart the device, when I check manage-bde -status, I only see TPM and numerical password, No TPM and PIn
Hi Rene, thanks for the extended solution. I have setup the policy and the device gets silently encrypted, the bitlocker app pops up and I enter the pin, but I don’t see any pin request during device start-up. manage-bde shows Numerical password and TPM as key protectors, no TPM, and PIN. The pop up asks for PIN each time I restart the device, no errors when I set the pin, and I don’t see a temp pin txt file too.
check the log files why the pin code cannot be set
Hello Rene,
Thanks for the blog, it has helped massively. We’ve now been able to convert most of our Windows devices to Intune. However, on two of our devices I have the following error on Intune:
C:\progra~2\BitLockerPinDialog\ServiceUI.exe : the term
‘C:\progra~2\BitlockerPinDialog\ServiceUI.exe’ is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spelling of the name, or if a
path was included, verify that the path is correct and try again. At
C:\Windows\IMECache\HealthScripts\ff8cf056-5db9-4251-baf6-d0657e2d6fe3_1\remediate.ps1:31 char:1 +
C:\progra~2\BitLockerPinDialog\ServiceUI.exe -process:Explorer.exe “$ … +
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo :
ObjectNotFound: (C:\progra~2\Bit…g\ServiceUI.exe:string) [],
CommandNotFoundException + FullyQualifiedErrorID : CommandNotFoundException
Check if the ServiceUI.exe exists on the devices where you get this error. If so. Copy the exe to the device and the error should be gone
Hi Rene,
Is there any way we can use this Win32App to change the BitLocker PIN as well?
Hi Paras,
What you could do is edit the PowerShell script and add code to remove the BitLocker pin as the first step and then set a new pin.
Another option, but never checked if this will work, configure the following setting:
https://learn.microsoft.com/en-us/windows/client-management/mdm/bitlocker-csp?WT.mc_id=Portal-fx#systemdrivesdisallowstandarduserscanchangepin