Overview
Managing BitLocker encryption across hundreds or thousands of endpoints manually is impractical. This guide walks you through building an enterprise-grade BitLocker automation script that handles TPM validation, encryption deployment, recovery key backup, and progress monitoring.
What You'll Build
- Automated TPM readiness validation (5-point check)
- Multi-mode execution (Check, Enable, Disable, Backup)
- Dual recovery key storage (Azure AD + RMM)
- Real-time encryption progress monitoring
- Graceful error handling for mixed environments
Why Automate BitLocker?
| Challenge | Solution |
|---|---|
| Manual deployment doesn't scale | Automated deployment via RMM/GPO |
| Recovery keys get lost | Automatic backup to Azure AD + RMM |
| TPM issues cause failures | Pre-flight validation before encryption |
| No visibility into status | Custom field reporting for inventory |
Architecture
The script operates in four distinct modes controlled by an environment variable:
┌───────────────────────────────────────────────────────────┐
│ BitLocker Automation │
├───────────────────────────────────────────────────────────┤
│ Mode: CheckBitlocker → Report TPM & encryption status │
│ Mode: EnableBitlocker → Full encryption workflow │
│ Mode: DisableBitlocker → Decryption with progress │
│ Mode: BackupBitlockerKeys → Recovery key backup only │
└───────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ Integration Points │
├───────────────────┬───────────────────┬───────────────────┤
│ NinjaRMM │ Azure AD │ On-Prem AD │
│ Custom Fields │ Key Backup API │ Key Escrow │
└───────────────────┴───────────────────┴───────────────────┘TPM Validation
Before any encryption operation, we perform a comprehensive TPM readiness check:
function Test-TpmReadiness {
$tpm = Get-Tpm
$checks = @{
Present = $tpm.TpmPresent
Ready = $tpm.TpmReady
Enabled = $tpm.TpmEnabled
Activated = $tpm.TpmActivated
Owned = $tpm.TpmOwned
}
Write-Host "TPM Status:"
Write-Host " Present: $($checks.Present)"
Write-Host " Ready: $($checks.Ready)"
Write-Host " Enabled: $($checks.Enabled)"
Write-Host " Activated: $($checks.Activated)"
Write-Host " Owned: $($checks.Owned)"
# All checks must pass
$allPassed = $checks.Values | Where-Object { $_ -eq $false } | Measure-Object
if ($allPassed.Count -gt 0) {
Write-Host "TPM validation FAILED" -ForegroundColor Red
return $false
}
Write-Host "TPM validation PASSED" -ForegroundColor Green
return $true
}TPM Requirements
| Check | Purpose |
|---|---|
| Present | TPM chip exists in hardware |
| Ready | TPM is initialized and ready |
| Enabled | TPM is enabled in BIOS |
| Activated | TPM is activated (not suspended) |
| Owned | TPM ownership has been taken |
Enable BitLocker Workflow
The encryption workflow follows this process:
function Enable-BitLockerEncryption {
param(
[string]$MountPoint = $env:SystemDrive
)
# Step 1: Validate TPM
if (-not (Test-TpmReadiness)) {
Set-BitlockerStatus "NO_ENCRYPTION_POSSIBLE"
return
}
# Step 2: Check current status
$volume = Get-BitLockerVolume -MountPoint $MountPoint
if ($volume.ProtectionStatus -eq "On") {
Write-Host "BitLocker already enabled"
Backup-BitlockerKeys -MountPoint $MountPoint
return
}
# Step 3: Add recovery password protector
Write-Host "Adding recovery password protector..."
Add-BitLockerKeyProtector -MountPoint $MountPoint -RecoveryPasswordProtector
# Step 4: Backup keys before encryption
Backup-BitlockerKeys -MountPoint $MountPoint
# Step 5: Enable encryption with AES-256
Write-Host "Starting encryption with AES-256..."
Enable-BitLocker `
-MountPoint $MountPoint `
-EncryptionMethod Aes256 `
-TpmProtector `
-SkipHardwareTest
# Step 6: Monitor progress
Watch-EncryptionProgress -MountPoint $MountPoint
}Encryption Methods
| Method | Key Size | Use Case |
|---|---|---|
| Aes128 | 128-bit | Legacy compatibility |
| Aes256 | 256-bit | Recommended for enterprise |
| XtsAes128 | 128-bit | Windows 10+ default |
| XtsAes256 | 256-bit | Maximum security |
Recovery Key Backup
Recovery keys are backed up to multiple locations for redundancy:
Azure AD Backup
function Backup-ToAzureAD {
param(
[string]$MountPoint = $env:SystemDrive
)
# Check Azure AD join status
$dsregStatus = dsregcmd /status
$isAzureADJoined = $dsregStatus -match "AzureAdJoined\s*:\s*YES"
if (-not $isAzureADJoined) {
Write-Host "Device not Azure AD joined - skipping AAD backup"
return $false
}
# Get recovery protector ID
$volume = Get-BitLockerVolume -MountPoint $MountPoint
$recoveryProtector = $volume.KeyProtector |
Where-Object { $_.KeyProtectorType -eq "RecoveryPassword" } |
Select-Object -First 1
if (-not $recoveryProtector) {
Write-Host "No recovery protector found"
return $false
}
try {
BackupToAAD-BitLockerKeyProtector `
-MountPoint $MountPoint `
-KeyProtectorId $recoveryProtector.KeyProtectorId
Write-Host "Recovery key backed up to Azure AD" -ForegroundColor Green
return $true
}
catch {
Write-Host "Azure AD backup failed: $_" -ForegroundColor Yellow
return $false
}
}NinjaRMM Custom Field
function Set-NinjaRecoveryKey {
param(
[string]$RecoveryKey
)
# Check if NinjaRMM CLI is available
$ninjaAvailable = Get-Command Ninja-Property-Set -ErrorAction SilentlyContinue
if (-not $ninjaAvailable) {
Write-Host "NinjaRMM CLI not available - skipping"
return
}
# Update custom field
Ninja-Property-Set BitlockerKey $RecoveryKey
Write-Host "Recovery key saved to NinjaRMM" -ForegroundColor Green
}Progress Monitoring
For large drives, encryption can take hours. Monitor progress in real-time:
function Watch-EncryptionProgress {
param(
[string]$MountPoint = $env:SystemDrive,
[int]$IntervalSeconds = 60
)
Write-Host "Monitoring encryption progress..."
do {
$volume = Get-BitLockerVolume -MountPoint $MountPoint
$percent = $volume.EncryptionPercentage
$status = $volume.VolumeStatus
$timestamp = Get-Date -Format "HH:mm:ss"
Write-Host "[$timestamp] Status: $status | Progress: $percent%"
if ($status -eq "FullyEncrypted") {
Write-Host "Encryption complete!" -ForegroundColor Green
break
}
if ($status -eq "EncryptionInProgress") {
Start-Sleep -Seconds $IntervalSeconds
}
else {
# Unexpected status
Write-Host "Unexpected status: $status" -ForegroundColor Yellow
break
}
} while ($true)
}Status Values
| Status | Meaning |
|---|---|
| FullyDecrypted | No encryption |
| EncryptionInProgress | Currently encrypting |
| FullyEncrypted | Encryption complete |
| DecryptionInProgress | Currently decrypting |
Complete Script
Here's the full enterprise-ready script:
#Requires -RunAsAdministrator
#Requires -Version 5.1
<#
.SYNOPSIS
Enterprise BitLocker automation with TPM validation and key backup.
.DESCRIPTION
Manages BitLocker encryption lifecycle with:
- TPM 5-point validation
- AES-256 encryption
- Dual key backup (Azure AD + NinjaRMM)
- Real-time progress monitoring
.PARAMETER Action
CheckBitlocker | EnableBitlocker | DisableBitlocker | BackupBitlockerKeys
#>
param(
[ValidateSet("CheckBitlocker", "EnableBitlocker", "DisableBitlocker", "BackupBitlockerKeys")]
[string]$Action = $env:action
)
# Main execution
switch ($Action) {
"CheckBitlocker" {
Test-TpmReadiness
Get-BitLockerVolume -MountPoint $env:SystemDrive | Format-List
}
"EnableBitlocker" {
Enable-BitLockerEncryption
}
"DisableBitlocker" {
Disable-BitLocker -MountPoint $env:SystemDrive
Watch-DecryptionProgress
}
"BackupBitlockerKeys" {
Backup-BitlockerKeys -MountPoint $env:SystemDrive
}
default {
Write-Host "No action specified. Use -Action parameter."
}
}Deployment Options
Via NinjaRMM
- Create a new script in NinjaRMM
- Set
actionas a script variable - Deploy to device groups via policy
Via Group Policy
- Save script to NETLOGON share
- Create GPO with scheduled task
- Set action via environment variable
Via Intune
- Package as Win32 app or PowerShell script
- Set detection rules based on BitLocker status
- Deploy to Azure AD groups
Troubleshooting
TPM Not Ready
# Clear TPM (requires physical presence)
Clear-Tpm
# Initialize TPM
Initialize-Tpm -AllowClearEncryption Stuck
# Check detailed status
manage-bde -status C:
# Resume if paused
Resume-BitLocker -MountPoint C:Recovery Key Not Found
# List all protectors
(Get-BitLockerVolume -MountPoint C:).KeyProtector
# Add new recovery password
Add-BitLockerKeyProtector -MountPoint C: -RecoveryPasswordProtectorBest Practices
- Always test in a lab first - Encryption issues can cause data loss
- Backup keys to multiple locations - Azure AD + RMM + on-prem AD
- Use AES-256 for compliance - Required for many regulations
- Monitor encryption progress - Large drives take hours
- Document recovery procedures - Know how to recover before you need to