Automating BitLocker Management with PowerShell

Automating BitLocker Management with PowerShell

As system administrators, security is a cornerstone of our responsibilities. One critical aspect of this is ensuring that sensitive data on workstations and servers is encrypted. Microsoft BitLocker provides robust encryption, but managing it at scale can be challenging. This script automates essential BitLocker-related tasks while ensuring the necessary prerequisites, such as a Trusted Platform Module (TPM) chip, are configured correctly. It also sets up a scheduled task to notify users of significant events without disrupting the user experience.

1. Internal/External Network Detection
The script starts by checking if the client is on an internal network using the Test-Path cmdlet

PowerShell
$LeaveMode = 'BREAK'
$Result = Test-Path 'SOME_INTERNAL_PATH'

if ($Result -eq $true) {
    Write-Host 'Client is internal!' -ForegroundColor Cyan
} else {
    Write-Host 'Client is not internal!' -ForegroundColor Magenta
    [System.Environment]::Exit(0x00041300)
    Invoke-Expression $LeaveMode
}

This ensures BitLocker operations are only performed on devices connected to a internal network. If the client is external, the script exits with a specific code (0x00041300) and executes the specified leave mode, such as BREAK – I use BREAK for Development and EXIT in Deployment.

2. Managing BitLocker Protection
The script evaluates BitLocker and TPM statuses using Get-BitLockerVolume and Get-Tpm

PowerShell
$Bitlocker = Get-BitLockerVolume -MountPoint $env:SystemDrive
$TPM = Get-Tpm

Case 1: BitLocker is already active

If BitLocker protection is On

PowerShell
if ($Bitlocker.ProtectionStatus -eq 'On') {
    Write-Host 'Bitlocker is activated! -> BREAK' -ForegroundColor Cyan
    Set-ItemProperty -Path 'HKLM:\HARDWARE' -Name 'Bitlocker' -Value 1
    Invoke-Expression $LeaveMode
}

This records the status in the registry and terminates further execution, confirming encryption is active.

The key HKLM:\HARDWARE ensures the script doesn’t re-run unnecessarily on systems where it has already been applied. When deploying the script via GPO, this prevents redundant execution, optimizes system performance, and allows the scheduled task to be removed via GPO after execution

Case 2: TPM is present, but BitLocker isn’t fully active
If encryption is incomplete or disabled, the script:

  1. Disables existing BitLocker encryption.
  2. Waits until decryption finishes.
  3. Adds a TPM-based protector.
  4. Re-enables encryption in “UsedSpaceOnly” mode for speed.
  5. Backs up the key to Azure Active Directory (AAD).

Adding the TPM Protector

PowerShell
Add-BitLockerKeyProtector -MountPoint $env:SystemDrive -RecoveryPasswordProtector | Out-Null

Enabling Bitlocker

PowerShell
Enable-BitLocker -MountPoint $env:SystemDrive -TpmProtector -UsedSpaceOnly -ErrorAction Stop

Backing Up the Key

PowerShell
BackupToAAD-BitLockerKeyProtector -MountPoint $env:SystemDrive -KeyProtectorId $ProtectorID | Out-Null

If the BitLocker configuration is corrupted, the script renames the ReAgent.xml file

PowerShell
Rename-Item -Path "C:\Windows\System32\Recovery\ReAgent.xml" -NewName "C:\Windows\System32\Recovery\ReAgent.old.xml"
Full Script
PowerShell
$LeaveMode = 'BREAK'

# Is Client internal?
$Result = Test-Path 'SOME_INTERNAL_PATH'

if ($Result -eq $true) {
    
    Write-Host 'Client is internal!' -ForegroundColor Cyan

}

else {

    Write-Host 'Client is not internal!' -ForegroundColor Magenta
    
    [System.Environment]::Exit(0x00041300)

    Invoke-Expression $LeaveMode

}


function Create-ScheduledTask{
    $currentUser = (Get-CimInstance -ClassName win32_ComputerSystem | select UserName).userName
    $currentDate = Get-Date
    $currentDate = $currentDate.AddSeconds(10)
    #$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-windowstyle hidden -executionpolicy bypass -noprofile -File *PATH_TO_SEND_TOAST_MESSAGE* -Restart"
    # use cmd variant to really supress the gui. otherwise the will be a short cli windows visible, since the script needs to run in user context
    $action = New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c start /min powershell.exe -windowstyle hidden -executionpolicy bypass -Command  *PATH_TO_SEND_TOAST_MESSAGE* -Restart"
    $trigger = New-ScheduledTaskTrigger -Once -At $currentDate
    $principal = New-ScheduledTaskPrincipal -UserId $currentUser

    $task = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger
    Register-ScheduledTask 'Send-ToastNotification' -InputObject $task | Out-Null

    # Edit Task and Set Auto Deletion 
    $createdTask = Get-ScheduledTask -TaskName 'Send-ToastNotification'
    $createdTask.Author = "DOMAIN\USERNAME"
    $createdTask.Triggers[0].StartBoundary = [DateTime]::Now.AddSeconds(15).ToString("yyyy-MM-dd'T'HH:mm:ss")
    $createdTask.Triggers[0].EndBoundary = [DateTime]::Now.AddSeconds(50).ToString("yyyy-MM-dd'T'HH:mm:ss")
    $createdTask.Settings.DeleteExpiredTaskAfter = "PT0S"

    $createdTask | Set-ScheduledTask 

}


$Bitlocker = Get-BitLockerVolume -MountPoint $env:SystemDrive
$TPM = Get-Tpm

# Bitlocker is working:
if ($Bitlocker.ProtectionStatus -eq 'On') {

    Write-Host 'Bitlocker is activated! -> BREAK' -ForegroundColor Cyan

    Set-ItemProperty -Path 'HKLM:\HARDWARE' -Name 'Bitlocker' -Value 1

    Invoke-Expression $LeaveMode

}

# TPM is present, Volume isnt FullyEncrypted:
elseif ($TPM.TpmPresent -eq $true -and $Bitlocker.ProtectionStatus -eq 'off' -and $Bitlocker.VolumeStatus -ne 'EncryptionInProgress') {

    # Disable present Bitlocker:
    Disable-BitLocker -MountPoint $env:SystemDrive

    do {


        $EncryptionStatus = (Get-BitLockerVolume -MountPoint $env:SystemDrive).EncryptionPercentage

        Write-Host 'EncryptionStatus:' $EncryptionStatus 'Percentage' -ForegroundColor Cyan

        sleep -Milliseconds 2000

    }

        until($EncryptionStatus -eq 0)


    # Add Bitlocker Protector with TPM
    Add-BitLockerKeyProtector -MountPoint $env:SystemDrive -RecoveryPasswordProtector | Out-Null 


    # Get Protector ID:
    $ProtectorID = Get-BitLockerVolume -MountPoint $env:SystemDrive | select -ExpandProperty KeyProtector
    $ProtectorID = $ProtectorID.KeyProtectorId

    try{
        # Enable Bitlocker:
        Enable-BitLocker -MountPoint $env:SystemDrive -TpmProtector -UsedSpaceOnly -ErrorAction Stop 

        # Backup ProtectorID in AD:
        BackupToAAD-BitLockerKeyProtector -MountPoint $env:SystemDrive -KeyProtectorId $ProtectorID | Out-Null
        
        Set-ItemProperty -Path 'HKLM:\HARDWARE' -Name 'Bitlocker' -Value 1        
        
        # CREATE SCHEDULED TASK TO SEND-TOASTMESSAGE
        Create-ScheduledTask

        Invoke-Expression $LeaveMode
    }catch{
        # Bitlocker Config corrupted
        Write-Output 'ReAgent File is corrupted - Rename File and run script at next logon again'
        Rename-Item -Path "C:\Windows\System32\Recovery\ReAgent.xml" -NewName "C:\Windows\System32\Recovery\ReAgent.old.xml"
        Invoke-Expression $LeaveMode
    }
    

}

else {

    Write-Host 'No TPM Chip was found or Encryption is in progress - BREAK!' -ForegroundColor Red

    Set-ItemProperty -Path 'HKLM:\HARDWARE' -Name 'Bitlocker' -Value 1

    Invoke-Expression $LeaveMode

}

Leave a Reply