Enroll a BitLocker pre-boot startup PIN (TPM+PIN) on silently-encrypted devices — without ServiceUI.exe.
Silent BitLocker encryption and a startup PIN are mutually exclusive: silent encryption requires no user interaction, while a PIN requires the user to type it. On devices already encrypted TPM-only, Windows never spontaneously prompts for a PIN. A SYSTEM process must therefore show a UI in the user's session to collect the PIN, then add the protector.
Community solutions (Oliver Kieselbach's method, Blufix/bitlocker-pin-csg)
rely on ServiceUI.exe (an MDT binary) for that session projection.
This project removes that dependency using a native re-implementation of the
ServiceUI technique — WTSQueryUserToken + CreateProcessAsUser.
| 🧩 Area | 🕹️ Typical solution | 🚀 This project |
|---|---|---|
| 🖥️ UI projection | ServiceUI.exe (MDT binary) |
Native CreateProcessAsUser + WTSQueryUserToken |
| 🔑 PIN transfer key | Static key hard-coded in the script | Random 32-byte AES key per run, memory only |
| 🕵️ Clear-text PIN | Encrypted-at-rest, static key | SecureString rebuilt in SYSTEM; never in clear text |
| 🛡️ Boot enforcement | Adds TPM+PIN, leaves TPM-only protector | Removes the TPM-only protector → PIN truly enforced |
| 🎨 Branding | Hard-coded | -OrganizationName / config.json, configurable accent |
| 🌍 Localization | Limited | strings.json (🇮🇹 it-IT, 🇬🇧 en-US) |
install.cmd (SYSTEM)
└─ copies files to C:\ProgramData\BitLockerPIN
└─ Set-BitLockerPin.ps1 (SYSTEM)
├─ ⏭️ skip if TpmPin present / BitLocker off / no user session
├─ 🔑 generate random AES key + random temp path
├─ 🖥️ NativeUserLauncher.ps1 → launch the dialog in the USER session
│ └─ user types PIN → AES-encrypt with injected key → temp file
├─ 🔓 decrypt to SecureString → Add-BitLockerKeyProtector -TpmAndPinProtector
└─ 🛡️ remove the TPM-only protector (PIN now enforced at boot)
🔒 The AES key is injected into the child process via its environment block — never written to disk, never passed on a visible command line.
| 📄 File | 🧰 Role |
|---|---|
src/NativeUserLauncher.ps1 |
ServiceUI-free launcher (Invoke-InUserSession) |
src/BitLockerPinPrompt.ps1 |
🪟 WinForms PIN dialog (runs in the user session) |
src/Set-BitLockerPin.ps1 |
🧠 SYSTEM orchestrator (install / remediation) |
src/Detect-BitLockerPin.ps1 |
🔎 Detection (Win32 rule + Remediations) |
src/Uninstall-BitLockerPin.ps1 |
🧹 Removes the helper folder |
src/install.cmd / uninstall.cmd |
📦 Win32 app commands |
src/config.json |
🎛️ Org name, accent colour, min PIN, language |
src/strings.json |
🗣️ Localized UI strings |
- 🪟 Windows 10 / 11, TPM present, running as SYSTEM (Intune).
- 👤 An active interactive user session (console or RDP) when the dialog runs.
- 🧩 Intune BitLocker policy updated to allow the startup PIN (see docs/deployment.md).
- 🗝️ Recovery password protector present and escrowed to Microsoft Entra ID.
See 📘 docs/deployment.md for the full walkthrough (policy changes, Win32 packaging, Remediations, rollout guidance).
powershell -STA -ExecutionPolicy Bypass -File .\src\BitLockerPinPrompt.ps1 -Preview -OrganizationName "Contoso"Technique inspired by Oliver Kieselbach's "Pre-Boot BitLocker startup PIN with Intune" and the Blufix/bitlocker-pin-csg packaging — re-implemented natively to drop the ServiceUI dependency and harden PIN transfer and boot enforcement.
Provided "AS IS" with no warranty. Test on a pilot ring before broad deployment. Changing BitLocker key protectors can affect access to data — ensure recovery keys are escrowed first.
Released under the MIT License.