Enable Bitlocker locally with no TPM

Over on the r/PowerShell subreddit, user u/3rddegreeofIT asked for assistance with using PowerShell to enable Bitlocker on systems with no TPM. They defined their criteria as:

  • Modifies registry to allow Advanced Startup
  • Modifies registry to allow BitLocker Drive Encryption without TPM
  • Set encryption to AES 256
  • Require a password on boot
  • Encrypt Used Space Only
  • Skip Hardware Test, No Reboot Required.

While I would prefer to manage via MEMCM (see my series on this) or GPO, I know this is not always possible. I considered including my PowerShell function to inject into LGPO, but that has a dependency on LGPO.exe and I assume u/3rddegreeofIT would like to keep this as simple as possible.

I made the assumption that the devices would be domain joined and based on that I create a recovery key and backup to AD. If not domain joined, I would highly recommend some other method to backup recovery keys.

As always, my code is written with an attempt at readability for those not as familiar with PowerShell. I am fully aware there are ways to write this all with less code at the cost of readability.

# Microsoft provides programming examples for illustration only, 
# without warranty either expressed or implied, including, but not 
# limited to, the implied warranties of merchantability and/or 
# fitness for a particular purpose. 
#
# This sample assumes that you are familiar with the programming 
# language being demonstrated and the tools used to create and debug 
# procedures. Microsoft support professionals can help explain the 
# functionality of a particular procedure, but they will not modify 
# these examples to provide added functionality or construct 
# procedures to meet your specific needs. If you have limited 
# programming experience, you may want to contact a Microsoft 
# Certified Partner or the Microsoft fee-based consulting line at 
# (800) 936-5200. 
#
# For more information about Microsoft Certified Partners, please 
# visit the following Microsoft Web site:
# https://partner.microsoft.com/global/30000104

Param(
  [parameter(Mandatory=$True,Position=0)][string]$password
)

#Verify FVE path exists and create if needed
if(!(test-path HKLM:\SOFTWARE\Policies\Microsoft\FVE)){
    New-Item -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE
}

#get value of UseAdvancedStartup (wrapped in try/catch to hide error if doesn't exist)
try{
    $UseAdvancedStartup = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name UseAdvancedStartup -ErrorAction SilentlyContinue
}
catch{}

#verify UseAdvancedStartup is set to 1 and create/change value if needed
if($UseAdvancedStartup -ne 1){
    New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name UseAdvancedStartup -Value 1 -Force | Out-Null
}

#get value of EnableBDEWithNoTPM (wrapped in try/catch to hide error if doesn't exist)
try{
    $EnableBDEWithNoTPM = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name EnableBDEWithNoTPM -ErrorAction SilentlyContinue
}
catch{}

#verify EnableBDEWithNoTPM is set to 1 and create/change value if needed
if($EnableBDEWithNoTPM -ne 1){
    New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name EnableBDEWithNoTPM -Value 1 | Out-Null
}

#Get OS Drive Bitlocker Info
$BLV = Get-BitLockerVolume -MountPoint $ENV:SystemDrive

#Verify OS drive is Fully Encrypted
If($BLV.VolumeStatus -ne "FullyEncrypted"){
    #create securestring from plaintext password (hardcoded example left in place, do not enable if using parameter)
    #$password = "C0mpl3x!"
    $pwss = ConvertTo-SecureString -String $password -AsPlainText -Force

    #enable BDE using xtsaes256, password protector, used space only, and skipping HW test
    Enable-BitLocker -MountPoint $ENV:SystemDrive -EncryptionMethod XtsAes256 -UsedSpaceOnly -PasswordProtector $pwss -SkipHardwareTest | out-null

    #update bitlocker info in variable
    $BLV = Get-BitLockerVolume -MountPoint $ENV:SystemDrive

    #get recovery password kepyprotector
    $RP = $BLV.KeyProtector | where-object{$_.KeyProtectorType -eq "RecoveryPassword"}

    #backup recovery key to Active Directory
    Backup-BitLockerKeyProtector -MountPoint $ENV:SystemDrive -KeyProtectorId $RP.KeyProtectorId
}

#testing support
#remove password keyprotector
#$PWKP = $BLV.KeyProtector | where-object{$_.KeyProtectorType -eq "Password"}
#Remove-BitLockerKeyProtector -MountPoint $ENV:SystemDrive -KeyProtectorId $PWKP.KeyProtectorId